From a0c0fd77f45de7d2098f213dfd3a222695dddc7f Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Fri, 21 Jul 2023 19:53:04 +0200 Subject: [PATCH] Refactor Chains service to hooks --- context/ChainsContext/service.tsx | 166 ++++++++++++++++++------------ 1 file changed, 100 insertions(+), 66 deletions(-) diff --git a/context/ChainsContext/service.tsx b/context/ChainsContext/service.tsx index 828a37f..18ef538 100644 --- a/context/ChainsContext/service.tsx +++ b/context/ChainsContext/service.tsx @@ -1,5 +1,6 @@ import { StargateClient } from "@cosmjs/stargate"; -import { assert } from "@cosmjs/utils"; +import { useEffect, useState } from "react"; +import { requestJson } from "../../lib/request"; import { GithubChainRegistryItem, RegistryAsset, @@ -19,40 +20,45 @@ import { ChainInfo, ChainItems } from "./types"; const chainsUrl = "https://api.github.com/repos/cosmos/chain-registry/contents"; const testnetsUrl = "https://api.github.com/repos/cosmos/chain-registry/contents/testnets"; -const registryGhUrl = "https://cdn.jsdelivr.net/gh/cosmos/chain-registry@master/"; +const registryGhUrl = "https://cdn.jsdelivr.net/gh/cosmos/chain-registry/"; -const getChains = async (chainUrl: string) => { - const response = await fetch(chainUrl); - if (!response.ok) { - throw new Error("Failed to get chains from registry"); - } +const nonChainsFilter = (item: GithubChainRegistryItem) => + item.type === "dir" && !item.name.startsWith(".") && !item.name.startsWith("_"); - const chainItems: readonly GithubChainRegistryItem[] = await response.json(); - return chainItems; -}; +export const useChainsFromRegistry = () => { + const [chainItems, setChainItems] = useState({ mainnets: [], testnets: [] }); + const [chainItemsError, setChainItemsError] = useState(null); -export const getChainItemsFromRegistry: () => Promise = async () => { - const [mainnets, testnets] = await Promise.all([getChains(chainsUrl), getChains(testnetsUrl)]); + useEffect(() => { + (async function () { + try { + const [mainnets, testnets] = await Promise.all([ + requestJson(chainsUrl), + requestJson(testnetsUrl), + ]); + setChainItems({ + mainnets: mainnets.filter(nonChainsFilter), + testnets: testnets.filter(nonChainsFilter), + }); + } catch (e) { + if (e instanceof Error) { + setChainItemsError(e.message); + } else { + setChainItemsError("Failed to get chains from registry"); + } + } + })(); + }, []); - const nonChainsFilter = (item: GithubChainRegistryItem) => - item.type === "dir" && !item.name.startsWith(".") && !item.name.startsWith("_"); - - return { - mainnets: mainnets.filter(nonChainsFilter), - testnets: testnets.filter(nonChainsFilter), - }; + return { chainItems, chainItemsError }; }; export const getChainItemFromRegistry = async (chainName: string, isTestnet?: boolean) => { const chainGhPath = isTestnet ? "testnets/" + chainName : chainName; const chainGhUrl = registryGhUrl + chainGhPath + "/chain.json"; - const response = await fetch(chainGhUrl); - if (!response.ok) { - throw new Error(`Failed to get ${chainName} chain from registry`); - } - - const chain: RegistryChain = await response.json(); + const chain: RegistryChain = await requestJson(chainGhUrl); + // const chain: RegistryChain = await (await fetch(chainGhUrl)).json(); return chain; }; @@ -60,12 +66,8 @@ export const getAssetItemsFromRegistry = async (chainName: string, isTestnet?: b const assetsGhPath = isTestnet ? "testnets/" + chainName : chainName; const assetsGhUrl = registryGhUrl + assetsGhPath + "/assetlist.json"; - const response = await fetch(assetsGhUrl); - if (!response.ok) { - throw new Error(`Failed to get assets for ${chainName} chain from registry`); - } - - const assets: readonly RegistryAsset[] = (await response.json()).assets; + const assets: readonly RegistryAsset[] = (await requestJson(assetsGhUrl)).assets; + // const assets: readonly RegistryAsset[] = (await (await fetch(assetsGhUrl)).json()).assets; return assets; }; @@ -95,46 +97,78 @@ const getExplorerFromArray = (explorers: readonly RegistryChainExplorer[]) => { return explorers[0]?.tx_page ?? ""; }; -export const getChainFromRegistry = async (chainName: string, isTestnet?: boolean) => { - const chainItem = await getChainItemFromRegistry(chainName, isTestnet); - const registryAssets = await getAssetItemsFromRegistry(chainName, isTestnet); - const firstAsset = registryAssets[0]; +export const useChainFromRegistry = (chain: ChainInfo, chains: ChainItems) => { + const [chainFromRegistry, setChainFromRegistry] = useState(chain); + const [chainFromRegistryError, setChainFromRegistryError] = useState(null); - const nodeAddress = await getNodeFromArray(chainItem.apis.rpc); - const explorerLink = getExplorerFromArray(chainItem.explorers); - const firstAssetDenom = firstAsset.base; - const displayDenom = firstAsset.symbol; - const displayUnit = firstAsset.denom_units.find((u) => u.denom == firstAsset.display); - assert(displayUnit, `Unit not found for ${firstAsset.display}`); + useEffect(() => { + (async function () { + try { + if (isChainInfoFilled(chain) || !chains.mainnets.length || !chains.testnets.length) { + return; + } - const feeToken = chainItem.fees.fee_tokens.find((token) => token.denom == firstAssetDenom) ?? { - denom: firstAssetDenom, - }; - const gasPrice = - feeToken.average_gas_price ?? - feeToken.low_gas_price ?? - feeToken.high_gas_price ?? - feeToken.fixed_min_gas_price ?? - 0.03; - const formattedGasPrice = firstAsset ? `${gasPrice}${firstAssetDenom}` : ""; + const isTestnet = !!chains.testnets.find(({ name }) => name === chain.registryName); + const chainItem = await getChainItemFromRegistry(chain.registryName, isTestnet); + const registryAssets = await getAssetItemsFromRegistry(chain.registryName, isTestnet); + const firstAsset = registryAssets[0]; - const chain: ChainInfo = { - registryName: chainName, - addressPrefix: chainItem.bech32_prefix, - chainId: chainItem.chain_id, - chainDisplayName: chainItem.pretty_name, - nodeAddress, - explorerLink, - denom: firstAssetDenom, - displayDenom, - displayDenomExponent: displayUnit.exponent, - gasPrice: formattedGasPrice, - assets: registryAssets, - }; + const nodeAddress = await getNodeFromArray(chainItem.apis.rpc); + const explorerLink = getExplorerFromArray(chainItem.explorers); + const firstAssetDenom = firstAsset.base; + const displayDenom = firstAsset.symbol; + const displayUnit = firstAsset.denom_units.find((u) => u.denom == firstAsset.display); - assert(isChainInfoFilled(chain), `Chain ${chainName} loaded from the registry with missing data`); + if (!displayUnit) { + return setChainFromRegistryError(`Unit not found for ${firstAsset.display}`); + } - return chain; + const feeToken = chainItem.fees.fee_tokens.find( + (token) => token.denom == firstAssetDenom, + ) ?? { + denom: firstAssetDenom, + }; + const gasPrice = + feeToken.average_gas_price ?? + feeToken.low_gas_price ?? + feeToken.high_gas_price ?? + feeToken.fixed_min_gas_price ?? + 0.03; + const formattedGasPrice = firstAsset ? `${gasPrice}${firstAssetDenom}` : ""; + + const newChain: ChainInfo = { + registryName: chain.registryName, + addressPrefix: chainItem.bech32_prefix, + chainId: chainItem.chain_id, + chainDisplayName: chainItem.pretty_name, + nodeAddress, + explorerLink, + denom: firstAssetDenom, + displayDenom, + displayDenomExponent: displayUnit.exponent, + gasPrice: formattedGasPrice, + assets: registryAssets, + }; + + if (!isChainInfoFilled(newChain)) { + setChainFromRegistryError( + `Chain ${newChain.registryName} loaded from the registry with missing data`, + ); + return; + } + + setChainFromRegistry(newChain); + } catch (e) { + if (e instanceof Error) { + setChainFromRegistryError(e.message); + } else { + setChainFromRegistryError(`Failed to get chain ${chain.registryName} from registry`); + } + } + })(); + }, [chain, chains.mainnets.length, chains.testnets]); + + return { chainFromRegistry, chainFromRegistryError }; }; export const getChain = () => {