From f647b7810a4e5495adf9192d6788a745a2b68608 Mon Sep 17 00:00:00 2001 From: sashimi36 Date: Sun, 10 Apr 2022 13:33:07 -0700 Subject: [PATCH 1/3] ChainClientRouter and its integration --- .../pages/dao/[contractAddress]/index.tsx | 4 +- .../multisig/[contractAddress]/index.tsx | 4 +- apps/dapp/selectors/cosm.ts | 20 +++--- apps/dapp/util/chainClientRouter.ts | 65 +++++++++++++++++++ 4 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 apps/dapp/util/chainClientRouter.ts diff --git a/apps/dapp/pages/dao/[contractAddress]/index.tsx b/apps/dapp/pages/dao/[contractAddress]/index.tsx index 88fac8b03..41d667f6a 100644 --- a/apps/dapp/pages/dao/[contractAddress]/index.tsx +++ b/apps/dapp/pages/dao/[contractAddress]/index.tsx @@ -5,7 +5,6 @@ import { useRouter } from 'next/router' import { useRecoilState, useRecoilValue } from 'recoil' -import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate' import { LibraryIcon, PlusSmIcon, UsersIcon } from '@heroicons/react/outline' import { useThemeContext } from 'ui' @@ -43,6 +42,7 @@ import { walletTokenBalanceLoading, } from 'selectors/treasury' import { addToken } from 'util/addToken' +import { cosmWasmClientRouter } from 'util/chainClientRouter' import { getFastAverageColor } from 'util/colors' import { convertMicroDenomToDenomWithDecimals } from 'util/conversion' @@ -324,7 +324,7 @@ export const getStaticProps: GetStaticProps = async ({ } try { - const client = await CosmWasmClient.connect(CHAIN_RPC_ENDPOINT) + const client = await cosmWasmClientRouter.connect(CHAIN_RPC_ENDPOINT) const daoInfo = await client.queryContractSmart(contractAddress, { get_config: {}, }) diff --git a/apps/dapp/pages/multisig/[contractAddress]/index.tsx b/apps/dapp/pages/multisig/[contractAddress]/index.tsx index ed15d3cae..cb696da1b 100644 --- a/apps/dapp/pages/multisig/[contractAddress]/index.tsx +++ b/apps/dapp/pages/multisig/[contractAddress]/index.tsx @@ -5,7 +5,6 @@ import { useRouter } from 'next/router' import { useRecoilState, useRecoilValue } from 'recoil' -import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate' import { Threshold } from '@dao-dao/types/contracts/cw3-multisig' import { ScaleIcon, @@ -35,6 +34,7 @@ import { sigSelector, totalWeight, } from 'selectors/multisigs' +import { cosmWasmClientRouter } from 'util/chainClientRouter' import { getFastAverageColor } from 'util/colors' const thresholdString = (t: Threshold) => { @@ -232,7 +232,7 @@ export const getStaticProps: GetStaticProps = async ({ } try { - const client = await CosmWasmClient.connect(CHAIN_RPC_ENDPOINT) + const client = await cosmWasmClientRouter.connect(CHAIN_RPC_ENDPOINT) const sigInfo = await client.queryContractSmart(contractAddress, { get_config: {}, }) diff --git a/apps/dapp/selectors/cosm.ts b/apps/dapp/selectors/cosm.ts index 43afd1661..cfe6f0720 100644 --- a/apps/dapp/selectors/cosm.ts +++ b/apps/dapp/selectors/cosm.ts @@ -1,13 +1,17 @@ import { selector, selectorFamily, atom } from 'recoil' -import { GasPrice, StargateClient } from '@cosmjs/stargate' + +import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate' +import { GasPrice } from '@cosmjs/stargate' + import { - CosmWasmClient, - SigningCosmWasmClient, -} from '@cosmjs/cosmwasm-stargate' + cosmWasmClientRouter, + stargateClientRouter, +} from 'util/chainClientRouter' +import { NATIVE_DENOM, GAS_PRICE } from 'util/constants' + +import { localStorageEffect } from '../atoms/localStorageEffect' import { connectKeplrWithoutAlerts } from '../services/keplr' import { walletTokenBalanceUpdateCountAtom } from './treasury' -import { localStorageEffect } from '../atoms/localStorageEffect' -import { NATIVE_DENOM, GAS_PRICE } from 'util/constants' export type WalletConnection = 'keplr' | '' @@ -18,14 +22,14 @@ const CHAIN_ID = process.env.NEXT_PUBLIC_CHAIN_ID export const stargateClient = selector({ key: 'stargateClient', get: () => { - return StargateClient.connect(CHAIN_RPC_ENDPOINT) + return stargateClientRouter.connect(CHAIN_RPC_ENDPOINT) }, }) export const cosmWasmClient = selector({ key: 'cosmWasmClient', get: () => { - return CosmWasmClient.connect(CHAIN_RPC_ENDPOINT) + return cosmWasmClientRouter.connect(CHAIN_RPC_ENDPOINT) }, }) diff --git a/apps/dapp/util/chainClientRouter.ts b/apps/dapp/util/chainClientRouter.ts new file mode 100644 index 000000000..b4b9a0255 --- /dev/null +++ b/apps/dapp/util/chainClientRouter.ts @@ -0,0 +1,65 @@ +import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate' +import { StargateClient } from '@cosmjs/stargate' + +type ChainClientRoutes = { + [rpcEndpoint: string]: T +} + +type HandleConnect = (rpcEndpoint: string) => Promise + +/* + * This is a workaround for `@cosmjs` clients to avoid re-connecting to the chain more than once. + * + * @example + * export const stargateClientRouter = new ChainClientRouter({ + * handleConnect: (rpcEndpoint: string) => StargateClient.connect(rpcEndpoint), + * }) + * + * const client = await stargateClientRouter.connect(RPC_ENDPOINT); + * + * const queryResponse = await client.queryContractSmart(...); + * */ +class ChainClientRouter { + private readonly handleConnect: HandleConnect + private instances: ChainClientRoutes = {} + + constructor({ handleConnect }: { handleConnect: HandleConnect }) { + this.handleConnect = handleConnect + } + + /* + * Connect to the chain and return the client + * or return an existing instance of the client. + * */ + async connect(rpcEndpoint: string) { + if (this.getClientInstance(rpcEndpoint)) { + return this.getClientInstance(rpcEndpoint) + } + + this.setClientInstance(rpcEndpoint, await this.handleConnect(rpcEndpoint)) + + return this.getClientInstance(rpcEndpoint) + } + + private getClientInstance(rpcEndpoint: string) { + return this.instances[rpcEndpoint] + } + + private setClientInstance(rpcEndpoint: string, client: T) { + this.instances[rpcEndpoint] = client + } +} + +/* + * Router for connecting to `CosmWasmClient`. + * */ +export const cosmWasmClientRouter = new ChainClientRouter({ + handleConnect: (rpcEndpoint: string) => CosmWasmClient.connect(rpcEndpoint), +}) + +/* + * Router for connecting to `StargateClient`. + * */ +export const stargateClientRouter = new ChainClientRouter({ + handleConnect: (rpcEndpoint: string) => StargateClient.connect(rpcEndpoint), +}) From 8f7fcb19cb780a4e8ec6bb30c02c32e43a53e98e Mon Sep 17 00:00:00 2001 From: sashimi36 Date: Sun, 10 Apr 2022 13:40:14 -0700 Subject: [PATCH 2/3] upd comment --- apps/dapp/util/chainClientRouter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dapp/util/chainClientRouter.ts b/apps/dapp/util/chainClientRouter.ts index b4b9a0255..57b87bd97 100644 --- a/apps/dapp/util/chainClientRouter.ts +++ b/apps/dapp/util/chainClientRouter.ts @@ -8,7 +8,7 @@ type ChainClientRoutes = { type HandleConnect = (rpcEndpoint: string) => Promise /* - * This is a workaround for `@cosmjs` clients to avoid re-connecting to the chain more than once. + * This is a workaround for `@cosmjs` clients to avoid connecting to the chain more than once. * * @example * export const stargateClientRouter = new ChainClientRouter({ From 3d6345a9916329dd93839a9260df28620f01d3af Mon Sep 17 00:00:00 2001 From: sashimi36 Date: Sun, 10 Apr 2022 15:49:52 -0700 Subject: [PATCH 3/3] pr feedback --- apps/dapp/util/chainClientRouter.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/dapp/util/chainClientRouter.ts b/apps/dapp/util/chainClientRouter.ts index 57b87bd97..8bb286bc7 100644 --- a/apps/dapp/util/chainClientRouter.ts +++ b/apps/dapp/util/chainClientRouter.ts @@ -32,12 +32,11 @@ class ChainClientRouter { * or return an existing instance of the client. * */ async connect(rpcEndpoint: string) { - if (this.getClientInstance(rpcEndpoint)) { - return this.getClientInstance(rpcEndpoint) + if (!this.getClientInstance(rpcEndpoint)) { + const instance = await this.handleConnect(rpcEndpoint) + this.setClientInstance(rpcEndpoint, instance) } - this.setClientInstance(rpcEndpoint, await this.handleConnect(rpcEndpoint)) - return this.getClientInstance(rpcEndpoint) }