import React, { useEffect, useState } from "react"; import axios from "axios"; import { StargateClient } from "@cosmjs/stargate"; import GearIcon from "../icons/Gear"; import Button from "../inputs/Button"; import Input from "../../components/inputs/Input"; import { useAppContext } from "../../context/AppContext"; import Select from "../inputs/Select"; import StackableContainer from "../layout/StackableContainer"; const ChainSelect = () => { const { state, dispatch } = useAppContext(); // UI State const [chainArray, setChainArray] = useState(); const [chainOptions, setChainOptions] = useState(); const [chainError, setChainError] = useState(); const [showSettings, setShowSettings] = useState(false); const [denomOptions, setDenomOptions] = useState([]); const [selectValue, setSelectValue] = useState({ label: "Loading...", value: -1 }); // Chain State const [tempChainId, setChainId] = useState(state.chain.chainId); const [tempNodeAddress, setNodeAddress] = useState(state.chain.nodeAddress); const [tempAddressPrefix, setAddressPrefix] = useState(state.chain.addressPrefix); const [tempDenom, setDenom] = useState(state.chain.denom); const [tempDisplayDenom, setDisplayDenom] = useState(state.chain.displayDenom); const [tempDisplayDenomExponent, setDisplayDenomExponent] = useState( state.chain.displayDenomExponent, ); const [tempGasPrice, setGasPrice] = useState(state.chain.gasPrice); const [tempChainName, setChainName] = useState(state.chain.chainDisplayName); const [tempExplorerLink, setExplorerLink] = useState(state.chain.explorerLink); let url = "https://api.github.com/repos/cosmos/chain-registry/contents"; useEffect(() => { getGhJson(); }, []); useEffect(() => { // set settings form fields to new values setChainId(state.chain.chainId); setNodeAddress(state.chain.nodeAddress); setAddressPrefix(state.chain.addressPrefix); setDenom(state.chain.denom); setDisplayDenom(state.chain.displayDenom); setDisplayDenomExponent(state.chain.displayDenomExponent); setGasPrice(state.chain.gasPrice); setChainName(state.chain.chainDisplayName); setExplorerLink(state.chain.explorerLink); }, [state]); const getGhJson = async () => { // getting chain info from this repo: https://github.com/cosmos/chain-registry try { const res = await axios.get(url); const chains = res.data.filter((item) => { return item.type == "dir" && item.name != ".github"; }); setChainArray(chains); const options = chains.map(({ name }, index) => { return { label: name, value: index }; }); setChainOptions(options); setSelectValue(findExistingOption(options, state.chain.chainDisplayName)); } catch (error) { console.log(error); setShowSettings(true); setChainError(error.message); } }; const findExistingOption = (options, chainName) => { const index = options.findIndex((option) => option.label === chainName); if (index >= 0) { return options[index]; } return { label: "unkown chain", value: -1 }; }; const getChainInfo = async (chainOption) => { setChainError(null); try { const chainInfoUrl = "https://cdn.jsdelivr.net/gh/cosmos/chain-registry@master/" + chainOption.path + "/chain.json"; const chainAssetUrl = "https://cdn.jsdelivr.net/gh/cosmos/chain-registry@master/" + chainOption.path + "/assetlist.json"; const { data: chainData } = await axios.get(chainInfoUrl); const { data: assetData } = await axios.get(chainAssetUrl); const nodeAddress = getNodeFromArray(chainData.apis.rpc); const addressPrefix = chainData["bech32_prefix"]; const chainId = chainData["chain_id"]; const chainDisplayName = chainData["chain_name"]; const explorerLink = getExplorerFromArray(chainData.explorers); let asset = ""; let denom = ""; let displayDenom = ""; const displayDenomExponent = 6; let gasPrice = ""; if (assetData.assets.length > 1) { setDenomOptions(assetData.assets); asset = ""; denom = ""; displayDenom = ""; gasPrice = ""; setChainError("Multiple token denoms available, enter manually"); setShowSettings(true); } else { asset = assetData.assets[0]; denom = asset.base; displayDenom = asset.symbol; gasPrice = `0.03${asset.base}`; } // test client connection const client = await StargateClient.connect(nodeAddress); await client.getHeight(); // change app state dispatch({ type: "changeChain", value: { nodeAddress, denom, displayDenom, displayDenomExponent, gasPrice, chainId, chainDisplayName, addressPrefix, explorerLink, }, }); setShowSettings(false); } catch (error) { console.log(error); setShowSettings(true); setChainError(error.message); } }; const getExplorerFromArray = (array) => { if (array && array.length > 0) { return array[0]["tx_page"]; } return ""; }; const getNodeFromArray = (nodeArray) => { // only return https connections const secureNodes = nodeArray.filter((node) => node.address.includes("https://")); if (secureNodes.length === 0) { throw new Error("No SSL enabled RPC nodes available for this chain"); } return secureNodes[0].address; }; const onChainSelect = (option) => { const index = chainOptions.findIndex((opt) => opt.label === option.label); setSelectValue(chainOptions[index]); getChainInfo(chainArray[option.value]); }; const setChainFromForm = async () => { setChainError(null); try { // test client connection const client = await StargateClient.connect(tempNodeAddress); await client.getHeight(); // change app state dispatch({ type: "changeChain", value: { nodeAddress: tempNodeAddress, denom: tempDenom, displayDenom: tempDisplayDenom, displayDenomExponent: tempDisplayDenomExponent, gasPrice: tempGasPrice, chainId: tempChainId, chainDisplayName: tempChainName, addressPrefix: tempAddressPrefix, explorerLink: tempExplorerLink, }, }); const selectedOption = findExistingOption(chainOptions, tempChainName); setSelectValue(selectedOption); setShowSettings(false); } catch (error) { console.log(error); setShowSettings(true); setChainError(error.message); } }; return (

Chain select

setChainName(e.target.value)} label="Chain Name" /> setChainId(e.target.value)} label="Chain ID" />
setAddressPrefix(e.target.value)} label="Bech32 Prefix (address prefix)" /> setNodeAddress(e.target.value)} label="RPC Node URL (must be https)" />
setDisplayDenom(e.target.value)} label="Display Denom" /> setDenom(e.target.value)} label="Base Denom" />
setDisplayDenomExponent(e.target.value)} label="Denom Exponent" /> setGasPrice(e.target.value)} label="Gas Price" />
setExplorerLink(e.target.value)} label="Explorer Link (with '${txHash}' included)" />
); }; export default ChainSelect;