From e1af358d57c0d3221fd055b3f718c72c61af4e82 Mon Sep 17 00:00:00 2001 From: luca Date: Mon, 26 Aug 2024 13:32:17 +0800 Subject: [PATCH] feat: make spawn compatible with staking, gov and asset-list examples --- .../common/Header/ChainDropdown.tsx | 3 +- .../components/staking/AllValidatorsList.tsx | 13 +- .../components/staking/DelegateModal.tsx | 17 ++- .../components/staking/MyValidatorsList.tsx | 6 +- .../components/staking/Overview.tsx | 6 +- .../components/staking/RedelegateModal.tsx | 9 +- .../staking/SelectValidatorModal.tsx | 6 +- .../components/staking/UndelegateModal.tsx | 10 +- .../components/staking/ValidatorInfoModal.tsx | 6 +- .../components/voting/Proposal.tsx | 23 +-- .../components/voting/Voting.tsx | 143 ++++++++++-------- examples/chain-template-spawn/config/spawn.ts | 2 + .../hooks/asset-list/useChainAssetsPrices.ts | 22 ++- .../hooks/asset-list/useChainUtils.ts | 16 +- .../hooks/staking/useAssetsPrices.ts | 23 +++ .../hooks/staking/useStakingData.ts | 10 +- .../hooks/voting/useVoting.ts | 6 +- examples/chain-template-spawn/pages/_app.tsx | 31 +--- examples/chain-template-spawn/utils/common.ts | 42 +++-- 19 files changed, 238 insertions(+), 156 deletions(-) diff --git a/examples/chain-template-spawn/components/common/Header/ChainDropdown.tsx b/examples/chain-template-spawn/components/common/Header/ChainDropdown.tsx index 1a03aaaba..3f9a18a8f 100644 --- a/examples/chain-template-spawn/components/common/Header/ChainDropdown.tsx +++ b/examples/chain-template-spawn/components/common/Header/ChainDropdown.tsx @@ -6,6 +6,7 @@ import { Box, Combobox, Skeleton, Stack, Text } from '@interchain-ui/react'; import { useDetectBreakpoints, useSpawnChains } from '@/hooks'; import { chainStore, useChainStore } from '@/contexts'; import { chainOptions } from '@/config'; +import { getSignerOptions } from '@/utils'; export const ChainDropdown = () => { const { selectedChain } = useChainStore(); @@ -23,7 +24,7 @@ export const ChainDropdown = () => { spawnChains?.assets?.length && !isChainsAdded ) { - addChains(spawnChains.chains, spawnChains.assets); + addChains(spawnChains.chains, spawnChains.assets, getSignerOptions()); setIsChainsAdded(true); } }, [spawnChains, isChainsAdded]); diff --git a/examples/chain-template-spawn/components/staking/AllValidatorsList.tsx b/examples/chain-template-spawn/components/staking/AllValidatorsList.tsx index 0d8ce73b2..efd6a8a9e 100644 --- a/examples/chain-template-spawn/components/staking/AllValidatorsList.tsx +++ b/examples/chain-template-spawn/components/staking/AllValidatorsList.tsx @@ -1,8 +1,5 @@ import React, { Dispatch, SetStateAction, useMemo } from 'react'; import { ChainName } from 'cosmos-kit'; - -import { getCoin } from '@/utils'; -import { shiftDigits, type ExtendedValidator as Validator } from '@/utils'; import { Text, Button, @@ -11,6 +8,13 @@ import { ValidatorTokenAmountCell, GridColumn, } from '@interchain-ui/react'; +import { useChain } from '@cosmos-kit/react'; + +import { + shiftDigits, + getNativeAsset, + type ExtendedValidator as Validator, +} from '@/utils'; const AllValidatorsList = ({ validators, @@ -27,7 +31,8 @@ const AllValidatorsList = ({ [key: string]: string; }; }) => { - const coin = getCoin(chainName); + const { assets } = useChain(chainName); + const coin = getNativeAsset(assets!); const columns = useMemo(() => { const _columns: GridColumn[] = [ diff --git a/examples/chain-template-spawn/components/staking/DelegateModal.tsx b/examples/chain-template-spawn/components/staking/DelegateModal.tsx index 2cf84d1e6..fc5c9679c 100644 --- a/examples/chain-template-spawn/components/staking/DelegateModal.tsx +++ b/examples/chain-template-spawn/components/staking/DelegateModal.tsx @@ -20,8 +20,8 @@ import { isGreaterThanZero, shiftDigits, calcDollarValue, - getCoin, - getExponent, + getNativeAsset, + getExponentFromAsset, toBaseAmount, } from '@/utils'; import { Prices, UseDisclosureReturn, useTx } from '@/hooks'; @@ -59,15 +59,15 @@ export const DelegateModal = ({ showDescription?: boolean; }) => { const { isOpen, onClose } = modalControl; - const { address, estimateFee } = useChain(chainName); + const { address, estimateFee, assets } = useChain(chainName); const [amount, setAmount] = useState(0); const [isDelegating, setIsDelegating] = useState(false); const [isSimulating, setIsSimulating] = useState(false); const [maxAmountAndFee, setMaxAmountAndFee] = useState(); - const coin = getCoin(chainName); - const exp = getExponent(chainName); + const coin = getNativeAsset(assets!); + const exp = getExponentFromAsset(coin); const { tx } = useTx(chainName); const onModalClose = () => { @@ -197,8 +197,8 @@ export const DelegateModal = ({ minValue: 0, maxValue: maxAmountAndFee?.maxAmount ?? Number(balance), value: amount, - onValueChange: (val) => { - setAmount(val); + onValueInput: (val) => { + setAmount(Number(val)); }, partials: [ { @@ -222,7 +222,8 @@ export const DelegateModal = ({ }, { label: 'Max', - onClick: () => setAmount(Number(balance)), + onClick: handleMaxClick, + isLoading: isSimulating, }, ], }} diff --git a/examples/chain-template-spawn/components/staking/MyValidatorsList.tsx b/examples/chain-template-spawn/components/staking/MyValidatorsList.tsx index 1ac5d73cd..7b2f4fe72 100644 --- a/examples/chain-template-spawn/components/staking/MyValidatorsList.tsx +++ b/examples/chain-template-spawn/components/staking/MyValidatorsList.tsx @@ -7,8 +7,9 @@ import { ValidatorTokenAmountCell, } from '@interchain-ui/react'; import { ChainName } from 'cosmos-kit'; -import { getCoin } from '@/utils'; +import { getNativeAsset } from '@/utils'; import { type ExtendedValidator as Validator } from '@/utils'; +import { useChain } from '@cosmos-kit/react'; const MyValidatorsList = ({ myValidators, @@ -25,7 +26,8 @@ const MyValidatorsList = ({ [key: string]: string; }; }) => { - const coin = getCoin(chainName); + const { assets } = useChain(chainName); + const coin = getNativeAsset(assets!); return ( { const [isClaiming, setIsClaiming] = useState(false); - const { address } = useChain(chainName); + const { address, assets } = useChain(chainName); const { tx } = useTx(chainName); const totalAmount = sum(balance, staked, rewards?.total ?? 0); - const coin = getCoin(chainName); + const coin = getNativeAsset(assets!); const onClaimRewardClick = async () => { setIsClaiming(true); diff --git a/examples/chain-template-spawn/components/staking/RedelegateModal.tsx b/examples/chain-template-spawn/components/staking/RedelegateModal.tsx index bc29a103b..2b765bb04 100644 --- a/examples/chain-template-spawn/components/staking/RedelegateModal.tsx +++ b/examples/chain-template-spawn/components/staking/RedelegateModal.tsx @@ -20,7 +20,7 @@ import { toBaseAmount, type ExtendedValidator as Validator, } from '@/utils'; -import { getCoin, getExponent } from '@/utils'; +import { getNativeAsset, getExponentFromAsset } from '@/utils'; import { Prices, UseDisclosureReturn, useTx } from '@/hooks'; const { beginRedelegate } = cosmos.staking.v1beta1.MessageComposer.fromPartial; @@ -40,14 +40,13 @@ export const RedelegateModal = ({ modalControl: UseDisclosureReturn; prices: Prices; }) => { - const { address } = useChain(chainName); + const { address, assets } = useChain(chainName); const [amount, setAmount] = useState(0); const [isRedelegating, setIsRedelegating] = useState(false); - const [, forceUpdate] = useState(0); - const coin = getCoin(chainName); - const exp = getExponent(chainName); + const coin = getNativeAsset(assets!); + const exp = getExponentFromAsset(coin); const { tx } = useTx(chainName); diff --git a/examples/chain-template-spawn/components/staking/SelectValidatorModal.tsx b/examples/chain-template-spawn/components/staking/SelectValidatorModal.tsx index 76bcafc89..620d95df2 100644 --- a/examples/chain-template-spawn/components/staking/SelectValidatorModal.tsx +++ b/examples/chain-template-spawn/components/staking/SelectValidatorModal.tsx @@ -11,9 +11,10 @@ import { Box, } from '@interchain-ui/react'; -import { getCoin } from '@/utils'; +import { getNativeAsset } from '@/utils'; import { UseDisclosureReturn } from '@/hooks'; import { shiftDigits, type ExtendedValidator as Validator } from '@/utils'; +import { useChain } from '@cosmos-kit/react'; export const SelectValidatorModal = ({ allValidators, @@ -30,7 +31,8 @@ export const SelectValidatorModal = ({ [key: string]: string; }; }) => { - const coin = getCoin(chainName); + const { assets } = useChain(chainName); + const coin = getNativeAsset(assets!); const columns = useMemo(() => { const hasApr = !!allValidators[0]?.apr; diff --git a/examples/chain-template-spawn/components/staking/UndelegateModal.tsx b/examples/chain-template-spawn/components/staking/UndelegateModal.tsx index 907a89a2b..03e858eba 100644 --- a/examples/chain-template-spawn/components/staking/UndelegateModal.tsx +++ b/examples/chain-template-spawn/components/staking/UndelegateModal.tsx @@ -11,7 +11,7 @@ import { Button, } from '@interchain-ui/react'; -import { getCoin, getExponent } from '@/utils'; +import { getNativeAsset, getExponentFromAsset } from '@/utils'; import { Prices, UseDisclosureReturn, useTx } from '@/hooks'; import { calcDollarValue, @@ -46,13 +46,11 @@ export const UndelegateModal = ({ }) => { const [amount, setAmount] = useState(0); const [isUndelegating, setIsUndelegating] = useState(false); - const [, forceUpdate] = useState(0); - - const { address } = useChain(chainName); + const { address, assets } = useChain(chainName); const { tx } = useTx(chainName); - const coin = getCoin(chainName); - const exp = getExponent(chainName); + const coin = getNativeAsset(assets!); + const exp = getExponentFromAsset(coin); const closeUndelegateModal = () => { setAmount(0); diff --git a/examples/chain-template-spawn/components/staking/ValidatorInfoModal.tsx b/examples/chain-template-spawn/components/staking/ValidatorInfoModal.tsx index 7d60feabf..298855075 100644 --- a/examples/chain-template-spawn/components/staking/ValidatorInfoModal.tsx +++ b/examples/chain-template-spawn/components/staking/ValidatorInfoModal.tsx @@ -1,4 +1,4 @@ -import { getCoin } from '@/utils'; +import { getNativeAsset } from '@/utils'; import { ChainName } from 'cosmos-kit'; import { formatValidatorMetaInfo, @@ -12,6 +12,7 @@ import { Text, } from '@interchain-ui/react'; import { UseDisclosureReturn } from '@/hooks'; +import { useChain } from '@cosmos-kit/react'; export const ValidatorInfoModal = ({ chainName, @@ -30,7 +31,8 @@ export const ValidatorInfoModal = ({ }; logoUrl: string; }) => { - const coin = getCoin(chainName); + const { assets } = useChain(chainName); + const coin = getNativeAsset(assets!); const { isOpen, onClose } = modalControl; const { openDelegateModal, openSelectValidatorModal, openUndelegateModal } = diff --git a/examples/chain-template-spawn/components/voting/Proposal.tsx b/examples/chain-template-spawn/components/voting/Proposal.tsx index 220539fcf..e4b01e1e4 100644 --- a/examples/chain-template-spawn/components/voting/Proposal.tsx +++ b/examples/chain-template-spawn/components/voting/Proposal.tsx @@ -17,13 +17,14 @@ import { import { exponentiate, formatDate, - getCoin, - getExponent, + getNativeAsset, + getExponentFromAsset, percent, } from '@/utils'; import Markdown from 'react-markdown'; import { useEffect, useState } from 'react'; import { useVoting, Votes } from '@/hooks'; +import { useChain } from '@cosmos-kit/react'; // export declare enum VoteOption { // /** VOTE_OPTION_UNSPECIFIED - VOTE_OPTION_UNSPECIFIED defines a no-op vote option. */ @@ -56,15 +57,16 @@ export function Proposal({ proposal, chainName, bondedTokens, - onVoteSuccess = () => { }, + onVoteSuccess = () => {}, }: ProposalProps) { const vote = votes?.[proposal.id.toString()]; const [showMore, setShowMore] = useState(false); const [voteType, setVoteType] = useState(); - const coin = getCoin(chainName); - const exponent = getExponent(chainName); + const { assets } = useChain(chainName); + const coin = getNativeAsset(assets!); + const exponent = getExponentFromAsset(coin); const { isVoting, onVote } = useVoting({ chainName, proposal }); const toggleShowMore = () => setShowMore((v) => !v); @@ -92,9 +94,9 @@ export function Proposal({ const total = proposal.finalTallyResult ? Object.values(proposal.finalTallyResult).reduce( - (sum, val) => sum + Number(val), - 0 - ) + (sum, val) => sum + Number(val), + 0 + ) : 0; const turnout = total / Number(bondedTokens); @@ -248,8 +250,9 @@ export function Proposal({ px: '$2', }} > - {`Minimum of staked ${minStakedTokens} ${coin.symbol}(${quorum * 100 - }%) need to vote + {`Minimum of staked ${minStakedTokens} ${coin.symbol}(${ + quorum * 100 + }%) need to vote for this proposal to pass.`} diff --git a/examples/chain-template-spawn/components/voting/Voting.tsx b/examples/chain-template-spawn/components/voting/Voting.tsx index e5bbee40c..2ece541ed 100644 --- a/examples/chain-template-spawn/components/voting/Voting.tsx +++ b/examples/chain-template-spawn/components/voting/Voting.tsx @@ -1,5 +1,5 @@ -import { useEffect, useState } from "react"; -import { useChain } from "@cosmos-kit/react"; +import { useEffect, useState } from 'react'; +import { useChain } from '@cosmos-kit/react'; import { Proposal as IProposal, ProposalStatus, @@ -12,11 +12,11 @@ import { Spinner, Text, useColorModeValue, -} from "@interchain-ui/react"; -import { useModal, useVotingData } from "@/hooks"; -import { Proposal } from "@/components"; -import { formatDate } from "@/utils"; -import { chains } from 'chain-registry' +} from '@interchain-ui/react'; +import { useModal, useVotingData } from '@/hooks'; +import { Proposal } from '@/components'; +import { formatDate } from '@/utils'; +import { chains } from 'chain-registry'; function status(s: ProposalStatus) { switch (s) { @@ -54,40 +54,43 @@ export function Voting({ chainName }: VotingProps) { const { address } = useChain(chainName); const [proposal, setProposal] = useState(); const { data, isLoading, refetch } = useVotingData(chainName); - const { modal, open: openModal, close: closeModal, setTitle } = useModal(""); + const { modal, open: openModal, close: closeModal, setTitle } = useModal(''); const [tallies, setTallies] = useState<{ [key: string]: TallyResult }>({}); const chain = chains.find((c) => c.chain_name === chainName); useEffect(() => { - if (!data.proposals || data.proposals.length === 0) return + if (!data.proposals || data.proposals.length === 0) return; data.proposals.forEach((proposal) => { if (proposal.status === ProposalStatus.PROPOSAL_STATUS_VOTING_PERIOD) { (async () => { for (const { address } of chain?.apis?.rest || []) { - const api = `${address}/cosmos/gov/v1/proposals/${Number(proposal.id)}/tally` + const api = `${address}/cosmos/gov/v1/proposals/${Number( + proposal.id + )}/tally`; try { - const tally = (await (await fetch(api)).json()).tally + const tally = (await (await fetch(api)).json()).tally; if (!tally) { - continue + continue; } - setTallies(prev => { + setTallies((prev) => { return { - ...prev, [proposal.id.toString()]: { + ...prev, + [proposal.id.toString()]: { yesCount: tally.yes_count, noCount: tally.no_count, abstainCount: tally.abstain_count, noWithVetoCount: tally.no_with_veto_count, - } - } - }) - break - } catch (e) { } + }, + }; + }); + break; + } catch (e) {} } - })() + })(); } }); - }, [data.proposals?.length, chainName]) + }, [data.proposals?.length, chainName]); function onClickProposal(index: number) { const proposal = data.proposals![index]; @@ -97,50 +100,72 @@ export function Voting({ chainName }: VotingProps) { setTitle(`#${proposal.id?.toString()} ${proposal?.title}`); } + const empty = ( + + No proposals found + + ); + const content = ( - {data.proposals?.map((proposal, index) => { - let tally = proposal.finalTallyResult - if (proposal.status === ProposalStatus.PROPOSAL_STATUS_VOTING_PERIOD) { - tally = tallies[proposal.id.toString()] - } - return ( onClickProposal(index) }} - > - {data.votes[proposal.id.toString()] ? ( - - - Voted - - - ) : null} - - ) - })} + {data.proposals?.length === 0 + ? empty + : data.proposals?.map((proposal, index) => { + let tally = proposal.finalTallyResult; + if ( + proposal.status === ProposalStatus.PROPOSAL_STATUS_VOTING_PERIOD + ) { + tally = tallies[proposal.id.toString()]; + } + return ( + onClickProposal(index) }} + > + {data.votes[proposal.id.toString()] ? ( + + + Voted + + + ) : null} + + + ); + })} ); const connect = ( - + Please connect to your wallet to see the proposals. diff --git a/examples/chain-template-spawn/config/spawn.ts b/examples/chain-template-spawn/config/spawn.ts index d34af4023..95cdf60d2 100644 --- a/examples/chain-template-spawn/config/spawn.ts +++ b/examples/chain-template-spawn/config/spawn.ts @@ -9,3 +9,5 @@ const api = { export const SPAWN_API_BASE_URL = api.baseUrl; export const SPAWN_CHAIN_URL = `${api.baseUrl}${api.endpoints.chain}`; export const SPAWN_ASSETS_URL = `${api.baseUrl}${api.endpoints.assets}`; + +export const DEFAULT_SPAWN_TOKEN_PRICE = 1; diff --git a/examples/chain-template-spawn/hooks/asset-list/useChainAssetsPrices.ts b/examples/chain-template-spawn/hooks/asset-list/useChainAssetsPrices.ts index 72b63edfb..ffe72616f 100644 --- a/examples/chain-template-spawn/hooks/asset-list/useChainAssetsPrices.ts +++ b/examples/chain-template-spawn/hooks/asset-list/useChainAssetsPrices.ts @@ -2,6 +2,8 @@ import { Asset } from '@chain-registry/types'; import { useQuery } from '@tanstack/react-query'; import { useChainUtils } from './useChainUtils'; import { handleError } from './useTopTokens'; +import { useSpawnChains } from '../common'; +import { DEFAULT_SPAWN_TOKEN_PRICE } from '@/config'; type CoinGeckoId = string; type CoinGeckoUSD = { usd: number }; @@ -36,12 +38,28 @@ const fetchPrices = async ( }; export const useChainAssetsPrices = (chainName: string) => { - const { allAssets } = useChainUtils(chainName); + const { allAssets, isSpawnChain } = useChainUtils(chainName); + const { data: spawnData } = useSpawnChains(); + const { assets: spawnAssets } = spawnData ?? {}; + + const queryKey = ['useChainAssetsPrices', chainName]; + + if (isSpawnChain) { + return useQuery({ + queryKey, + queryFn: () => { + const nativeAsset = spawnAssets?.[0].assets[0]!; + return { [nativeAsset.base]: DEFAULT_SPAWN_TOKEN_PRICE }; + }, + staleTime: Infinity, + }); + } + const assetsWithGeckoIds = getAssetsWithGeckoIds(allAssets); const geckoIds = getGeckoIds(assetsWithGeckoIds); return useQuery({ - queryKey: ['useChainAssetsPrices', chainName], + queryKey, queryFn: () => fetchPrices(geckoIds), select: (data) => formatPrices(data, assetsWithGeckoIds), staleTime: Infinity, diff --git a/examples/chain-template-spawn/hooks/asset-list/useChainUtils.ts b/examples/chain-template-spawn/hooks/asset-list/useChainUtils.ts index 496239e64..75df8ade9 100644 --- a/examples/chain-template-spawn/hooks/asset-list/useChainUtils.ts +++ b/examples/chain-template-spawn/hooks/asset-list/useChainUtils.ts @@ -8,9 +8,17 @@ import BigNumber from 'bignumber.js'; import { Coin } from '@cosmjs/amino'; import { PrettyAsset } from '@/components'; import { ChainName } from 'cosmos-kit'; +import { useSpawnChains } from '../common'; export const useChainUtils = (chainName: string) => { const { getChainRecord } = useManager(); + const { data: spawnData } = useSpawnChains(); + const { chains: spawnChains = [], assets: spawnAssets = [] } = + spawnData ?? {}; + + const isSpawnChain = spawnChains.some( + (chain) => chain.chain_name === chainName + ); const filterAssets = (assetList: AssetList[]): Asset[] => { return ( @@ -22,7 +30,10 @@ export const useChainUtils = (chainName: string) => { const { nativeAssets, ibcAssets } = useMemo(() => { // @ts-ignore - const nativeAssets = filterAssets(chainAssets); + const nativeAssets = filterAssets([ + ...chainAssets, + ...(isSpawnChain ? spawnAssets : []), + ]); // @ts-ignore const ibcAssets = filterAssets(ibcAssetLists); @@ -67,7 +78,7 @@ export const useChainUtils = (chainName: string) => { const getExponentByDenom = (denom: CoinDenom): Exponent => { const asset = getAssetByDenom(denom); const unit = asset.denom_units.find(({ denom }) => denom === asset.display); - return unit?.exponent || 0; + return unit?.exponent || 6; }; const convRawToDispAmount = (symbol: string, amount: string | number) => { @@ -157,6 +168,7 @@ export const useChainUtils = (chainName: string) => { }; return { + isSpawnChain, allAssets, nativeAssets, ibcAssets, diff --git a/examples/chain-template-spawn/hooks/staking/useAssetsPrices.ts b/examples/chain-template-spawn/hooks/staking/useAssetsPrices.ts index 76af1d357..4fc630398 100644 --- a/examples/chain-template-spawn/hooks/staking/useAssetsPrices.ts +++ b/examples/chain-template-spawn/hooks/staking/useAssetsPrices.ts @@ -1,6 +1,9 @@ import { assets } from 'chain-registry'; import { useQuery } from '@tanstack/react-query'; import { AssetList } from '@chain-registry/types'; +import { useSpawnChains } from '../common'; +import { useChainStore } from '@/contexts'; +import { DEFAULT_SPAWN_TOKEN_PRICE } from '@/config'; type CoinGeckoId = string; type CoinGeckoUSD = { usd: number }; @@ -42,6 +45,26 @@ const fetchPrices = async ( }; export const useAssetsPrices = () => { + const { selectedChain } = useChainStore(); + const { data: spawnData } = useSpawnChains(); + const { chains: spawnChains = [], assets: spawnAssets = [] } = + spawnData ?? {}; + + const isSpawnChain = spawnChains.some( + (chain) => chain.chain_name === selectedChain + ); + + if (isSpawnChain) { + return useQuery({ + queryKey: ['useAssetsPrices', selectedChain], + queryFn: () => { + const nativeAsset = spawnAssets?.[0].assets[0]!; + return { [nativeAsset.base]: DEFAULT_SPAWN_TOKEN_PRICE }; + }, + staleTime: Infinity, + }); + } + const geckoIds = getGeckoIdsFromAssets(assets); return useQuery({ diff --git a/examples/chain-template-spawn/hooks/staking/useStakingData.ts b/examples/chain-template-spawn/hooks/staking/useStakingData.ts index f23a70808..0787b6cd4 100644 --- a/examples/chain-template-spawn/hooks/staking/useStakingData.ts +++ b/examples/chain-template-spawn/hooks/staking/useStakingData.ts @@ -18,8 +18,8 @@ import { parseRewards, parseUnbondingDays, parseValidators, - getCoin, - getExponent, + getNativeAsset, + getExponentFromAsset, } from '@/utils'; (BigInt.prototype as any).toJSON = function () { @@ -27,10 +27,10 @@ import { }; export const useStakingData = (chainName: string) => { - const { address, getRpcEndpoint } = useChain(chainName); + const { address, getRpcEndpoint, assets } = useChain(chainName); - const coin = getCoin(chainName); - const exp = getExponent(chainName); + const coin = getNativeAsset(assets!); + const exp = getExponentFromAsset(coin); const rpcEndpointQuery = useRpcEndpoint({ getter: getRpcEndpoint, diff --git a/examples/chain-template-spawn/hooks/voting/useVoting.ts b/examples/chain-template-spawn/hooks/voting/useVoting.ts index 32e8a8978..f6988dc0f 100644 --- a/examples/chain-template-spawn/hooks/voting/useVoting.ts +++ b/examples/chain-template-spawn/hooks/voting/useVoting.ts @@ -4,7 +4,7 @@ import { toast } from '@interchain-ui/react'; import { useChain } from '@cosmos-kit/react'; import { coins, StdFee } from '@cosmjs/stargate'; import { Proposal } from 'interchain-query/cosmos/gov/v1/gov'; -import { getCoin } from '@/utils'; +import { getNativeAsset } from '@/utils'; import { useVotingTx } from './useVotingTx'; const MessageComposer = cosmos.gov.v1beta1.MessageComposer; @@ -22,10 +22,10 @@ export type onVoteOptions = { export function useVoting({ chainName, proposal }: useVotingOptions) { const { tx } = useVotingTx(chainName); - const { address } = useChain(chainName); + const { address, assets } = useChain(chainName); const [isVoting, setIsVoting] = useState(false); - const coin = getCoin(chainName); + const coin = getNativeAsset(assets!); async function onVote({ option, diff --git a/examples/chain-template-spawn/pages/_app.tsx b/examples/chain-template-spawn/pages/_app.tsx index 55bbbc262..2bb1827e6 100644 --- a/examples/chain-template-spawn/pages/_app.tsx +++ b/examples/chain-template-spawn/pages/_app.tsx @@ -2,16 +2,15 @@ import '../styles/globals.css'; import '@interchain-ui/react/styles'; import type { AppProps } from 'next/app'; -import { SignerOptions } from 'cosmos-kit'; import { ChainProvider } from '@cosmos-kit/react'; import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; // import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { Box, Toaster, useTheme } from '@interchain-ui/react'; import { chains, assets } from 'chain-registry'; -import { GasPrice } from '@cosmjs/stargate'; import { CustomThemeProvider, Layout } from '@/components'; import { wallets } from '@/config'; +import { getSignerOptions } from '@/utils'; const queryClient = new QueryClient({ defaultOptions: { @@ -26,24 +25,6 @@ const queryClient = new QueryClient({ function CreateCosmosApp({ Component, pageProps }: AppProps) { const { themeClass } = useTheme(); - const signerOptions: SignerOptions = { - // TODO fix type error - // @ts-ignore - signingStargate: (chain) => { - let gasPrice; - try { - // TODO fix type error - // @ts-ignore - const feeToken = chain.fees?.fee_tokens[0]; - const fee = `${feeToken?.average_gas_price || 0.025}${feeToken?.denom}`; - gasPrice = GasPrice.fromString(fee); - } catch (error) { - gasPrice = GasPrice.fromString('0.025uosmo'); - } - return { gasPrice }; - }, - }; - return ( diff --git a/examples/chain-template-spawn/utils/common.ts b/examples/chain-template-spawn/utils/common.ts index 33f0f4d1e..a55413329 100644 --- a/examples/chain-template-spawn/utils/common.ts +++ b/examples/chain-template-spawn/utils/common.ts @@ -1,20 +1,14 @@ -import { assets } from 'chain-registry'; import { Asset, AssetList } from '@chain-registry/types'; -import { Wallet } from '@cosmos-kit/core'; +import { GasPrice } from '@cosmjs/stargate'; +import { SignerOptions, Wallet } from '@cosmos-kit/core'; -export const getChainAssets = (chainName: string) => { - return assets.find((chain) => chain.chain_name === chainName) as AssetList; +export const getNativeAsset = (assets: AssetList) => { + return assets.assets[0] as Asset; }; -export const getCoin = (chainName: string) => { - const chainAssets = getChainAssets(chainName); - return chainAssets.assets[0] as Asset; -}; - -export const getExponent = (chainName: string) => { - return getCoin(chainName).denom_units.find( - (unit) => unit.denom === getCoin(chainName).display - )?.exponent as number; +export const getExponentFromAsset = (asset: Asset) => { + const unit = asset.denom_units.find((unit) => unit.denom === asset.display); + return unit?.exponent ?? 6; }; export const shortenAddress = (address: string, partLength = 6) => { @@ -28,3 +22,25 @@ export const getWalletLogo = (wallet: Wallet) => { ? wallet.logo : wallet.logo.major || wallet.logo.minor; }; + +export const getSignerOptions = (): SignerOptions => { + const defaultGasPrice = GasPrice.fromString('0.025uosmo'); + + return { + // @ts-ignore + signingStargate: (chain) => { + if (typeof chain === 'string') { + return { gasPrice: defaultGasPrice }; + } + let gasPrice; + try { + const feeToken = chain.fees?.fee_tokens[0]; + const fee = `${feeToken?.average_gas_price || 0.025}${feeToken?.denom}`; + gasPrice = GasPrice.fromString(fee); + } catch (error) { + gasPrice = defaultGasPrice; + } + return { gasPrice }; + }, + }; +};