diff --git a/packages/vue/package.json b/packages/vue/package.json new file mode 100644 index 000000000..372f950fe --- /dev/null +++ b/packages/vue/package.json @@ -0,0 +1,48 @@ +{ + "name": "@interchain-kit/vue", + "version": "0.0.1-beta.1", + "author": "cosmology-tech ", + "description": "interchain-kit wallet connector vue package", + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "homepage": "https://github.com/interchain-kit/vue", + "license": "SEE LICENSE IN LICENSE", + "publishConfig": { + "access": "public", + "directory": "dist" + }, + "repository": { + "type": "git", + "url": "https://github.com/interchain-kit/vue" + }, + "bugs": { + "url": "https://github.com/interchain-kit/vue/issues" + }, + "scripts": { + "copy": "copyfiles -f ../../LICENSE README.md package.json dist", + "clean": "rimraf dist/**", + "build": "vite build && vue-tsc --emitDeclarationOnly", + "lint": "eslint . --fix", + "test": "vitest", + "test:watch": "vitest --watch", + "dev": "vite" + }, + "keywords": [], + "dependencies": { + "@chain-registry/v2-types": "^0.49.6", + "@interchain-kit/core": "0.0.1-beta.26", + "@interchain-ui/vue": "1.24.0", + "@interchainjs/cosmos-types": "0.0.1-beta.9", + "@interchainjs/injective": "0.0.1-beta.13", + "interchainjs": "0.0.1-beta.14", + "vue": "^3.3.4" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^4.2.3", + "typescript": "^5.0.2", + "vite": "^4.4.5", + "vitest": "^0.34.1", + "vue-tsc": "^1.8.5" + } +} \ No newline at end of file diff --git a/packages/vue/src/composables/useAccount.ts b/packages/vue/src/composables/useAccount.ts new file mode 100644 index 000000000..35453890e --- /dev/null +++ b/packages/vue/src/composables/useAccount.ts @@ -0,0 +1,42 @@ +import { ref, watch, onMounted, onUnmounted, Ref } from 'vue' +import { WalletAccount, WalletState } from "@interchain-kit/core" +import { useWalletManager } from './useWalletManager' + +export function useAccount(chainName: Ref, walletName: Ref): Ref { + const walletManager = useWalletManager() + const account = ref(null) + + const getAccount = async () => { + const wallet = walletManager.wallets.find(w => w.option.name === walletName.value) + const chain = walletManager.chains.find(c => c.chainName === chainName.value) + + if (wallet && chain) { + if (wallet.walletState === WalletState.Connected) { + const newAccount = await wallet.getAccount(chain.chainId) + account.value = newAccount + } + if (wallet.walletState === WalletState.Disconnected) { + account.value = null + } + } + } + + watch([chainName, walletName], getAccount) + + onMounted(() => { + const wallet = walletManager.wallets.find(w => w.option.name === walletName.value) + if (wallet) { + wallet.events.on('keystoreChange', getAccount) + } + getAccount() + }) + + onUnmounted(() => { + const wallet = walletManager.wallets.find(w => w.option.name === walletName.value) + if (wallet) { + wallet.events.off('keystoreChange', getAccount) + } + }) + + return account +} diff --git a/packages/vue/src/composables/useChain.ts b/packages/vue/src/composables/useChain.ts new file mode 100644 index 000000000..327d2d00e --- /dev/null +++ b/packages/vue/src/composables/useChain.ts @@ -0,0 +1,46 @@ +import { computed } from 'vue' +import { useWalletManager } from './useWalletManager' +import { useAccount } from './useAccount' +import { useCurrentWallet } from './useCurrentWallet' +import { useInterchainClient } from './useInterchainClient' +import { useWalletModal } from '../modal' +import { ChainNameNotExist } from '@interchain-kit/core' +import { getChainLogoUrl } from '../utils' + +export function useChain(chainName: string) { + const walletManager = useWalletManager() + const currentWallet = useCurrentWallet() + const account = useAccount(chainName, computed(() => currentWallet.value?.option?.name)) + const interchainClient = useInterchainClient(chainName, computed(() => currentWallet.value?.option?.name)) + const { open, close } = useWalletModal() + + const chainToShow = computed(() => walletManager.chains.find(c => c.chainName === chainName)) + const assetList = computed(() => walletManager.assetLists.find(a => a.chainName === chainName)) + + if (!chainToShow.value) { + throw new ChainNameNotExist(chainName) + } + + return { + logoUrl: computed(() => getChainLogoUrl(assetList.value)), + chain: chainToShow, + assetList, + address: computed(() => account.value?.address), + wallet: currentWallet, + connect: () => { + if (currentWallet.value) { + return + } + open() + }, + openView: open, + closeView: close, + getRpcEndpoint: () => walletManager.getRpcEndpoint(currentWallet.value, chainName), + status: computed(() => currentWallet.value?.walletState), + username: computed(() => account.value?.username), + message: computed(() => currentWallet.value?.errorMessage), + getSigningCosmWasmClient: () => walletManager.getSigningCosmwasmClient(currentWallet.value?.option?.name || '', chainName), + getSigningCosmosClient: () => walletManager.getSigningCosmosClient(currentWallet.value?.option?.name || '', chainName), + ...interchainClient + } +} \ No newline at end of file diff --git a/packages/vue/src/composables/useChainWallet.ts b/packages/vue/src/composables/useChainWallet.ts new file mode 100644 index 000000000..5cf966afe --- /dev/null +++ b/packages/vue/src/composables/useChainWallet.ts @@ -0,0 +1,28 @@ +import { computed, Ref } from 'vue' +import { AssetList, Chain } from "@chain-registry/v2-types" +import { useWalletManager } from "./useWalletManager" +import { useAccount } from "./useAccount" +import { BaseWallet } from "@interchain-kit/core" +import { UseChainReturnType } from "../types/chain" +import { useInterchainClient } from "./useInterchainClient" +import { getChainLogoUrl } from "../utils" + +export function useChainWallet(chainName: Ref, walletName: Ref): UseChainReturnType { + const walletManager = useWalletManager() + + const chainToShow = computed(() => walletManager.chains.find((c: Chain) => c.chainName === chainName.value)) + const assetList = computed(() => walletManager.assetLists.find((a: AssetList) => a.chainName === chainName.value)) + const wallet = computed(() => walletManager.wallets.find((w: BaseWallet) => w.option.name === walletName.value)) + + const account = useAccount(chainName, walletName) + const interchainClient = useInterchainClient(chainName, walletName) + + return { + logoUrl: computed(() => getChainLogoUrl(assetList.value)), + chain: chainToShow, + assetList, + address: computed(() => account.value?.address), + wallet, + ...interchainClient + } +} diff --git a/packages/vue/src/composables/useConfig.ts b/packages/vue/src/composables/useConfig.ts new file mode 100644 index 000000000..7009fd3bb --- /dev/null +++ b/packages/vue/src/composables/useConfig.ts @@ -0,0 +1,14 @@ +import { AssetList, Chain } from "@chain-registry/v2-types" +import { useWalletManager } from "./useWalletManager" +import { EndpointOptions, SignerOptions } from "@interchain-kit/core" + +export function useConfig() { + const walletManager = useWalletManager() + + return { + updateChains: (chains: Chain[]) => walletManager.chains = chains, + updateAssetLists: (assetLists: AssetList[]) => walletManager.assetLists = assetLists, + updateSignerOptions: (signerOptions: SignerOptions) => walletManager.signerOptions = signerOptions, + updateEndpoints: (endpointOptions: EndpointOptions) => walletManager.endpointOptions = endpointOptions, + } +} diff --git a/packages/vue/src/composables/useInterchainClient.ts b/packages/vue/src/composables/useInterchainClient.ts new file mode 100644 index 000000000..ab02fcdf8 --- /dev/null +++ b/packages/vue/src/composables/useInterchainClient.ts @@ -0,0 +1,62 @@ +import { HttpEndpoint } from '@interchainjs/types' +import { CosmWasmSigningClient } from 'interchainjs/cosmwasm' +import { CosmosSigningClient } from 'interchainjs/cosmos' +import { SigningClient } from 'interchainjs/signing-client' +import { RpcQuery } from 'interchainjs/query/rpc' +import { ref, watch, Ref } from 'vue' +import { useWalletManager } from './useWalletManager' +import { Chain } from '@chain-registry/v2-types' +import { useAccount } from './useAccount' +import { WalletState } from '@interchain-kit/core' +import { InjSigningClient } from '@interchainjs/injective/signing-client' + +export function useInterchainClient(chainName: Ref, walletName: Ref) { + const rpcEndpoint = ref() + const queryClient = ref(null) + const signingClient = ref(null) + const signingCosmosClient = ref(null) + const signingCosmWasmClient = ref(null) + const signingInjectiveClient = ref(null) + const error = ref(null) + const isLoading = ref(false) + + const walletManager = useWalletManager() + const account = useAccount(chainName, walletName) + + const initialize = async () => { + const wallet = walletManager.wallets.find((w) => w.option.name === walletName.value) + const chainToShow = walletManager.chains.find((c: Chain) => c.chainName === chainName.value) + + if (wallet && chainToShow && wallet?.walletState === WalletState.Connected) { + try { + isLoading.value = true + + rpcEndpoint.value = await walletManager.getRpcEndpoint(wallet, chainName.value) + queryClient.value = await walletManager.getQueryClient(walletName.value, chainName.value) + signingClient.value = await walletManager.getSigningClient(walletName.value, chainName.value) + signingCosmosClient.value = await walletManager.getSigningCosmosClient(walletName.value, chainName.value) + signingCosmWasmClient.value = await walletManager.getSigningCosmwasmClient(walletName.value, chainName.value) + signingInjectiveClient.value = await walletManager.getSigningInjectiveClient(walletName.value, chainName.value) + + } catch (err) { + error.value = err + console.log("create client error", err) + } finally { + isLoading.value = false + } + } + } + + watch([chainName, walletName, account], initialize) + + return { + rpcEndpoint, + signingClient: computed(() => chainName.value === 'injective' ? signingInjectiveClient.value : signingClient.value), + queryClient, + signingCosmosClient, + signingCosmWasmClient, + signingInjectiveClient, + error, + isLoading + } +} diff --git a/packages/vue/src/composables/useOfflineSigner.ts b/packages/vue/src/composables/useOfflineSigner.ts new file mode 100644 index 000000000..7c4b3b08d --- /dev/null +++ b/packages/vue/src/composables/useOfflineSigner.ts @@ -0,0 +1,19 @@ +import { OfflineSigner } from '@interchainjs/cosmos/types/wallet' +import { ref, watch, Ref } from 'vue' +import { useWalletManager } from './useWalletManager' + +export function useOfflineSigner(chainName: Ref, walletName: Ref) { + const walletManager = useWalletManager() + const offlineSigner = ref(null) + + watch([chainName, walletName], () => { + const wallet = walletManager.wallets.find((w) => w.option.name === walletName.value) + if (wallet && chainName.value) { + offlineSigner.value = walletManager.getOfflineSigner(wallet, chainName.value) + } + }, { immediate: true }) + + return { + offlineSigner + } +} diff --git a/packages/vue/src/composables/useWalletManager.ts b/packages/vue/src/composables/useWalletManager.ts new file mode 100644 index 000000000..f8b6cdf18 --- /dev/null +++ b/packages/vue/src/composables/useWalletManager.ts @@ -0,0 +1,11 @@ +import { inject } from 'vue' +import { WalletManager } from '@interchain-kit/core' +import { WALLET_MANAGER_KEY } from '../provider' + +export function useWalletManager(): WalletManager { + const walletManager = inject(WALLET_MANAGER_KEY) + if (!walletManager) { + throw new Error('useWalletManager must be used within a ChainProvider') + } + return walletManager +} \ No newline at end of file diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts new file mode 100644 index 000000000..70a445594 --- /dev/null +++ b/packages/vue/src/index.ts @@ -0,0 +1,5 @@ +export * from './provider' +export * from './composables' +export * from './types' +export * from './enum' +export * from './modal' \ No newline at end of file