Refactor Chains service to hooks

This commit is contained in:
abefernan 2023-07-21 19:53:04 +02:00
parent 3d13a34664
commit a0c0fd77f4

View File

@ -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<ChainItems>({ mainnets: [], testnets: [] });
const [chainItemsError, setChainItemsError] = useState<string | null>(null);
export const getChainItemsFromRegistry: () => Promise<ChainItems> = 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<ChainInfo>(chain);
const [chainFromRegistryError, setChainFromRegistryError] = useState<string | null>(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 = () => {