From fce77f4e5d26e5b0756c525b9be0d13ec42ffcc4 Mon Sep 17 00:00:00 2001 From: Pedro Rezende Date: Tue, 14 Jan 2025 20:02:06 -0300 Subject: [PATCH] feat: fetching known ibc channels from github chain-registry repository --- apps/namadillo/public/config.toml | 3 + apps/namadillo/src/App/Ibc/IbcTransfer.tsx | 20 +++-- apps/namadillo/src/App/Ibc/IbcWithdraw.tsx | 13 +-- .../namadillo/src/atoms/integrations/atoms.ts | 41 ++++++--- .../src/atoms/integrations/functions.ts | 85 ++++++++----------- .../src/atoms/integrations/services.ts | 33 ++++++- apps/namadillo/src/lib/chain.ts | 8 ++ apps/namadillo/src/types.ts | 2 + 8 files changed, 124 insertions(+), 81 deletions(-) create mode 100644 apps/namadillo/src/lib/chain.ts diff --git a/apps/namadillo/public/config.toml b/apps/namadillo/public/config.toml index 29d5b71031..99681ff101 100644 --- a/apps/namadillo/public/config.toml +++ b/apps/namadillo/public/config.toml @@ -3,3 +3,6 @@ #rpc_url = "" #masp_indexer_url = "" #localnet_enabled = false + +github_chain_registry_base_url = "https://raw.githubusercontent.com/anoma/namada-chain-registry/refs/heads/main" +github_namada_interface_url = "https://raw.githubusercontent.com/anoma/namada-interface/refs/heads/main" diff --git a/apps/namadillo/src/App/Ibc/IbcTransfer.tsx b/apps/namadillo/src/App/Ibc/IbcTransfer.tsx index 6675825788..8e2443b3f5 100644 --- a/apps/namadillo/src/App/Ibc/IbcTransfer.tsx +++ b/apps/namadillo/src/App/Ibc/IbcTransfer.tsx @@ -44,9 +44,11 @@ export const IbcTransfer = (): JSX.Element => { } = useWalletManager(keplr); // IBC Channels & Balances - const { data: ibcChannels } = useAtomValue( - ibcChannelsFamily(registry?.chain.chain_name) - ); + const { + data: ibcChannels, + isError: unknownIbcChannels, + isLoading: isLoadingIbcChannels, + } = useAtomValue(ibcChannelsFamily(registry?.chain.chain_name)); const { data: userAssets, isLoading: isLoadingBalances } = useAtomValue( assetBalanceAtomFamily({ @@ -101,16 +103,18 @@ export const IbcTransfer = (): JSX.Element => { ); }, [defaultAccounts, shielded]); - const requiresIbcChannels = - !ibcChannels?.cosmosChannelId || - (shielded && !ibcChannels?.namadaChannelId); + const requiresIbcChannels = Boolean( + !isLoadingIbcChannels && + (unknownIbcChannels || + (shielded && ibcChannels && !ibcChannels?.namadaChannel)) + ); useEffect(() => setSelectedAssetAddress(undefined), [registry]); // Set source and destination channels based on IBC channels data useEffect(() => { - setSourceChannel(ibcChannels?.cosmosChannelId || ""); - setDestinationChannel(ibcChannels?.namadaChannelId || ""); + setSourceChannel(ibcChannels?.ibcChannel || ""); + setDestinationChannel(ibcChannels?.namadaChannel || ""); }, [ibcChannels]); const onSubmitTransfer = async ({ diff --git a/apps/namadillo/src/App/Ibc/IbcWithdraw.tsx b/apps/namadillo/src/App/Ibc/IbcWithdraw.tsx index abb19f68d5..7880468030 100644 --- a/apps/namadillo/src/App/Ibc/IbcWithdraw.tsx +++ b/apps/namadillo/src/App/Ibc/IbcWithdraw.tsx @@ -80,12 +80,14 @@ export const IbcWithdraw: React.FC = () => { connectToChainId(chain.chain_id); }; - const { data: ibcChannels } = useAtomValue( - ibcChannelsFamily(registry?.chain.chain_name) - ); + const { + data: ibcChannels, + isError: unknownIbcChannels, + isLoading: isLoadingIbcChannels, + } = useAtomValue(ibcChannelsFamily(registry?.chain.chain_name)); useEffect(() => { - setSourceChannel(ibcChannels?.namadaChannelId || ""); + setSourceChannel(ibcChannels?.namadaChannel || ""); }, [ibcChannels]); const { execute: performWithdraw, isPending } = useTransaction({ @@ -190,7 +192,8 @@ export const IbcWithdraw: React.FC = () => { } }; - const requiresIbcChannels = !ibcChannels?.cosmosChannelId; + const requiresIbcChannels = !isLoadingIbcChannels && unknownIbcChannels; + return (
diff --git a/apps/namadillo/src/atoms/integrations/atoms.ts b/apps/namadillo/src/atoms/integrations/atoms.ts index 46dbbff5fa..88006b1cc3 100644 --- a/apps/namadillo/src/atoms/integrations/atoms.ts +++ b/apps/namadillo/src/atoms/integrations/atoms.ts @@ -6,10 +6,11 @@ import { IbcTransferProps, } from "@namada/types"; import { defaultAccountAtom } from "atoms/accounts"; -import { chainAtom, chainParametersAtom } from "atoms/chain"; +import { chainAtom } from "atoms/chain"; import { defaultServerConfigAtom, settingsAtom } from "atoms/settings"; import { queryDependentFn } from "atoms/utils"; import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx"; +import invariant from "invariant"; import { atom } from "jotai"; import { atomWithMutation, atomWithQuery } from "jotai-tanstack-query"; import { atomFamily, atomWithStorage } from "jotai/utils"; @@ -24,7 +25,6 @@ import { import { addLocalnetToRegistry, createIbcTx, - getIbcChannels, getKnownChains, ibcAddressToDenomTrace, IbcChannels, @@ -32,6 +32,7 @@ import { } from "./functions"; import { broadcastIbcTransaction, + fetchIbcChannelFromRegistry, fetchLocalnetTomlConfig, queryAndStoreRpc, queryAssetBalances, @@ -130,19 +131,31 @@ export const availableAssetsAtom = atom((get) => { return getKnownChains(settings.enableTestnets).map(({ assets }) => assets); }); -export const ibcChannelsFamily = atomFamily((cosmosChainName?: string) => - atomWithQuery((get) => { - const chainParameters = get(chainParametersAtom); - +export const ibcChannelsFamily = atomFamily((ibcChainName?: string) => + atomWithQuery((get) => { + const chainSettings = get(chainAtom); + const config = get(defaultServerConfigAtom); return { - queryKey: ["ibc-channel", cosmosChainName, chainParameters.data!.chainId], - ...queryDependentFn( - () => - Promise.resolve( - getIbcChannels(chainParameters.data!.chainId, cosmosChainName!) - ), - [typeof cosmosChainName !== "undefined", chainParameters] - ), + queryKey: ["known-channels", ibcChainName, chainSettings.data?.chainId], + retry: false, + ...queryDependentFn(async () => { + invariant(chainSettings.data, "No chain settings"); + invariant(ibcChainName, "No IBC chain name"); + invariant( + config.data?.github_chain_registry_base_url, + "No github_chain_registry_base_url was provided on config.toml" + ); + return fetchIbcChannelFromRegistry( + chainSettings.data.chainId, + ibcChainName, + config.data?.github_chain_registry_base_url + ); + }, [ + chainSettings, + config, + !!ibcChainName, + !!config.data?.github_chain_registry_base_url, + ]), }; }) ); diff --git a/apps/namadillo/src/atoms/integrations/functions.ts b/apps/namadillo/src/atoms/integrations/functions.ts index b0ac3c87d4..492819ee3b 100644 --- a/apps/namadillo/src/atoms/integrations/functions.ts +++ b/apps/namadillo/src/atoms/integrations/functions.ts @@ -17,6 +17,7 @@ import * as osmosisTestnet from "chain-registry/testnet/osmosistestnet"; import * as stargazeTestnet from "chain-registry/testnet/stargazetestnet"; import { DenomTrace } from "cosmjs-types/ibc/applications/transfer/v1/transfer"; import { TransactionPair, buildTxPair } from "lib/query"; +import namadaMainnetChain from "namada-chain-registry/namada/chain.json"; import { Address, AddressWithAssetAndAmount, @@ -43,21 +44,20 @@ import internalDevnetChain from "namada-chain-registry/_testnets/namadainternald import namadaAssets from "namada-chain-registry/namada/assetlist.json"; import namadaChain from "namada-chain-registry/namada/chain.json"; -import campfireOsmosisTestnetIbc from "namada-chain-registry/_testnets/_IBC/namadacampfire-osmosistestnet.json"; -import housefireOldCosmosTestnetIbc from "namada-chain-registry/_testnets/_IBC/namadahousefireold-cosmoshubtestnet.json"; -import housefireOldOsmosisTestnetIbc from "namada-chain-registry/_testnets/_IBC/namadahousefireold-osmosistestnet.json"; import internalDevnetCosmosTestnetIbc from "namada-chain-registry/_testnets/_IBC/namadainternaldevnet-cosmoshubtestnet.json"; // TODO: this causes a big increase on bundle size. See #1224. import cosmosRegistry from "chain-registry"; +import { searchNamadaTestnetByChainId } from "lib/chain"; -cosmosRegistry.chains.push( +export const namadaTestnetChainList = [ internalDevnetChain, campfireChain, housefireChain, housefireOldChain, - namadaChain -); +] as Chain[]; + +cosmosRegistry.chains.push(...namadaTestnetChainList, namadaChain); cosmosRegistry.assets.push( internalDevnetAssets, @@ -67,13 +67,6 @@ cosmosRegistry.assets.push( namadaAssets ); -cosmosRegistry.ibc.push( - campfireOsmosisTestnetIbc, - internalDevnetCosmosTestnetIbc, - housefireOldCosmosTestnetIbc, - housefireOldOsmosisTestnetIbc -); - const mainnetChains: ChainRegistryEntry[] = [ celestia, cosmos, @@ -323,51 +316,43 @@ export const getRestApiAddressByIndex = (chain: Chain, index = 0): string => { return randomRestApi.address; }; +export const getChainRegistryIbcFilePath = ( + currentNamadaChainId: string, + ibcChainName: string +): string => { + const chain = + searchNamadaTestnetByChainId(currentNamadaChainId) || namadaMainnetChain; + const searchFilename = `${chain.chain_name}-${ibcChainName}.json`; + const isMainnet = currentNamadaChainId === namadaMainnetChain.chain_id; + const ibcFolder = isMainnet ? "_IBC" : "_testnets/_IBC"; + return `${ibcFolder}/${searchFilename}`; +}; + export type IbcChannels = { - namadaChannelId: string; - cosmosChannelId: string; + namadaChannel: string; + ibcChannel: string; }; -export const getIbcChannels = ( - namadaChainId: string, - cosmosChainName: string +export const getChannelFromIbcInfo = ( + ibcChainName: string, + ibcInfo: IBCInfo ): IbcChannels | undefined => { - const namadaChainName = cosmosRegistry.chains.find( - (chain) => chain.chain_id === namadaChainId - )?.chain_name; + const { chain_2, channels } = ibcInfo; + const channelEntry = channels[0]; - if (typeof namadaChainName === "undefined") { - return undefined; + if (!channelEntry) { + console.warn("No channel entry found in IBC info"); + return; } - for (const ibcEntry of cosmosRegistry.ibc) { - const { chain_1, chain_2, channels } = ibcEntry; - const channelEntry = channels[0]; + const namadaOnChannel1 = chain_2.chain_name === ibcChainName; + const namadaChannelId = namadaOnChannel1 ? "chain_1" : "chain_2"; + const ibcChannelId = namadaOnChannel1 ? "chain_2" : "chain_1"; - if (typeof channelEntry === "undefined") { - continue; - } - - if ( - chain_1.chain_name === namadaChainName && - chain_2.chain_name === cosmosChainName - ) { - return { - namadaChannelId: channelEntry.chain_1.channel_id, - cosmosChannelId: channelEntry.chain_2.channel_id, - }; - } - - if ( - chain_1.chain_name === cosmosChainName && - chain_2.chain_name === namadaChainName - ) { - return { - cosmosChannelId: channelEntry.chain_1.channel_id, - namadaChannelId: channelEntry.chain_2.channel_id, - }; - } - } + return { + namadaChannel: channelEntry[namadaChannelId].channel_id, + ibcChannel: channelEntry[ibcChannelId].channel_id, + }; }; export const createIbcTx = async ( diff --git a/apps/namadillo/src/atoms/integrations/services.ts b/apps/namadillo/src/atoms/integrations/services.ts index ac11a042a4..8a2cf9cf9a 100644 --- a/apps/namadillo/src/atoms/integrations/services.ts +++ b/apps/namadillo/src/atoms/integrations/services.ts @@ -1,15 +1,16 @@ -import { Chain } from "@chain-registry/types"; +import { Chain, IBCInfo } from "@chain-registry/types"; import { OfflineSigner } from "@cosmjs/launchpad"; import { + assertIsDeliverTxSuccess, + calculateFee, DeliverTxResponse, SigningStargateClient, StargateClient, StdFee, - assertIsDeliverTxSuccess, - calculateFee, } from "@cosmjs/stargate"; import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx"; +import { sanitizeUrl } from "@namada/utils"; import { getIndexerApi } from "atoms/api"; import { queryForAck, queryForIbcTimeout } from "atoms/transactions"; import BigNumber from "bignumber.js"; @@ -28,7 +29,12 @@ import { toBaseAmount } from "utils"; import { getKeplrWallet } from "utils/ibc"; import { getSdkInstance } from "utils/sdk"; import { rpcByChainAtom } from "./atoms"; -import { getRpcByIndex } from "./functions"; +import { + getChainRegistryIbcFilePath, + getChannelFromIbcInfo, + getRpcByIndex, + IbcChannels, +} from "./functions"; type CommonParams = { signer: OfflineSigner; @@ -256,3 +262,22 @@ export const fetchLocalnetTomlConfig = async (): Promise => { const response = await fetch("/localnet-config.toml"); return toml.parse(await response.text()) as LocalnetToml; }; + +export const fetchIbcChannelFromRegistry = async ( + currentNamadaChainId: string, + ibcChainName: string, + namadaChainRegistryUrl: string +): Promise => { + const ibcFilePath = getChainRegistryIbcFilePath( + currentNamadaChainId, + ibcChainName + ); + + const queryUrl = new URL( + ibcFilePath, + sanitizeUrl(namadaChainRegistryUrl) + "/" + ); + + const channelInfo: IBCInfo = await (await fetch(queryUrl.toString())).json(); + return getChannelFromIbcInfo(ibcChainName, channelInfo) || null; +}; diff --git a/apps/namadillo/src/lib/chain.ts b/apps/namadillo/src/lib/chain.ts new file mode 100644 index 0000000000..1f61fe1d66 --- /dev/null +++ b/apps/namadillo/src/lib/chain.ts @@ -0,0 +1,8 @@ +import { Chain } from "@chain-registry/types"; +import { namadaTestnetChainList } from "atoms/integrations"; + +export const searchNamadaTestnetByChainId = ( + chainId: string +): Chain | undefined => { + return namadaTestnetChainList.find((chain) => chain.chain_id === chainId); +}; diff --git a/apps/namadillo/src/types.ts b/apps/namadillo/src/types.ts index 49deac9efb..f69995b504 100644 --- a/apps/namadillo/src/types.ts +++ b/apps/namadillo/src/types.ts @@ -62,6 +62,8 @@ export type SettingsTomlOptions = { masp_indexer_url?: string; rpc_url?: string; localnet_enabled?: boolean; + github_chain_registry_base_url?: string; + github_namada_interface_url?: string; }; export type ChainParameters = {