From cee28f139dc30fadee33cd49eae100c663c46678 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Thu, 21 Sep 2023 11:54:56 -0700 Subject: [PATCH] Fixed chains potentially not having native tokens. --- packages/i18n/locales/en/translation.json | 1 + .../core/treasury/CommunityPoolDeposit/index.tsx | 2 +- .../core/treasury/ManageStaking/Component.tsx | 4 ++++ .../actions/core/treasury/ManageStaking/index.tsx | 6 ++++++ .../stateful/actions/core/treasury/Spend/index.tsx | 4 ++-- packages/stateful/components/WalletStakingModal.tsx | 4 ++++ .../stateful/components/dao/DaoTreasuryHistory.tsx | 12 ++++++------ .../ProposalDepositVotingConfigItem.tsx | 4 ++-- .../Renderer/components/stateless/NewAttribute.tsx | 2 +- packages/stateless/components/ChainProvider.tsx | 4 ++-- packages/stateless/components/inputs/TokenInput.tsx | 10 ++++------ packages/types/chain.ts | 8 ++++++-- packages/utils/chain.ts | 12 +++++++++++- 13 files changed, 50 insertions(+), 23 deletions(-) diff --git a/packages/i18n/locales/en/translation.json b/packages/i18n/locales/en/translation.json index 4cbfa1d7c..386cd2bb2 100644 --- a/packages/i18n/locales/en/translation.json +++ b/packages/i18n/locales/en/translation.json @@ -330,6 +330,7 @@ "metadataUpload": "Failed to upload metadata.", "missingGovernanceTokenAddress": "Missing governance token address.", "missingGovernanceTokenDenom": "Missing governance token denom.", + "missingNativeToken": "Missing native token.", "mustBeMemberToCreateCompensationCycle": "You must be a member of the DAO to create a compensation cycle.", "mustBeMemberToCreatePost": "You must be a member of the DAO to create a post.", "mustBeMemberToCreateProposal": "You must be a member of the DAO to create a proposal.", diff --git a/packages/stateful/actions/core/treasury/CommunityPoolDeposit/index.tsx b/packages/stateful/actions/core/treasury/CommunityPoolDeposit/index.tsx index 6198a12f8..bf1540352 100644 --- a/packages/stateful/actions/core/treasury/CommunityPoolDeposit/index.tsx +++ b/packages/stateful/actions/core/treasury/CommunityPoolDeposit/index.tsx @@ -80,7 +80,7 @@ export const makeCommunityPoolDepositAction: ActionMaker< const useDefaults: UseDefaults = () => ({ chainId: currentChainId, amount: 100, - denom: nativeToken.denomOrAddress, + denom: nativeToken?.denomOrAddress || '', }) const useTransformToCosmos: UseTransformToCosmos< diff --git a/packages/stateful/actions/core/treasury/ManageStaking/Component.tsx b/packages/stateful/actions/core/treasury/ManageStaking/Component.tsx index c1a284e5b..b0c0da751 100644 --- a/packages/stateful/actions/core/treasury/ManageStaking/Component.tsx +++ b/packages/stateful/actions/core/treasury/ManageStaking/Component.tsx @@ -116,6 +116,10 @@ export const ManageStakingComponent: ActionComponent< nativeToken, } = useChainContext() + if (!nativeToken) { + throw new Error(t('error.missingNativeToken')) + } + // Metadata for the given denom. const minAmount = convertMicroDenomToDenomWithDecimals( 1, diff --git a/packages/stateful/actions/core/treasury/ManageStaking/index.tsx b/packages/stateful/actions/core/treasury/ManageStaking/index.tsx index 77f63718b..793dd7056 100644 --- a/packages/stateful/actions/core/treasury/ManageStaking/index.tsx +++ b/packages/stateful/actions/core/treasury/ManageStaking/index.tsx @@ -1,6 +1,7 @@ import { coin, parseCoins } from '@cosmjs/amino' import { useCallback } from 'react' import { useFormContext } from 'react-hook-form' +import { useTranslation } from 'react-i18next' import { nativeDelegationInfoSelector, @@ -175,6 +176,7 @@ const useDecodedCosmosMsg: UseDecodedCosmosMsg = ( } const InnerComponent: ActionComponent = (props) => { + const { t } = useTranslation() const { address: _address, context, chain } = useActionOptions() const { watch } = useFormContext() @@ -183,6 +185,10 @@ const InnerComponent: ActionComponent = (props) => { nativeToken, } = useChainContext() + if (!nativeToken) { + throw new Error(t('error.missingNativeToken')) + } + const address = context.type === ActionContextType.Dao && currentChainId !== chain.chain_id ? context.info.polytoneProxies[currentChainId] || '' diff --git a/packages/stateful/actions/core/treasury/Spend/index.tsx b/packages/stateful/actions/core/treasury/Spend/index.tsx index 6a0656eff..f23fa1ff7 100644 --- a/packages/stateful/actions/core/treasury/Spend/index.tsx +++ b/packages/stateful/actions/core/treasury/Spend/index.tsx @@ -28,7 +28,6 @@ import { getChainForChainName, getIbcTransferInfoBetweenChains, getIbcTransferInfoFromChainSource, - getNativeTokenForChainId, isDecodedStargateMsg, isValidBech32Address, isValidContractAddress, @@ -36,6 +35,7 @@ import { makePolytoneExecuteMessage, makeStargateMessage, makeWasmMessage, + maybeGetNativeTokenForChainId, objectMatchesStructure, transformBech32Address, } from '@dao-dao/utils' @@ -61,7 +61,7 @@ const useDefaults: UseDefaults = () => { toChainId: chainId, to: walletAddress, amount: 1, - denom: getNativeTokenForChainId(chainId).denomOrAddress, + denom: maybeGetNativeTokenForChainId(chainId)?.denomOrAddress || '', } } diff --git a/packages/stateful/components/WalletStakingModal.tsx b/packages/stateful/components/WalletStakingModal.tsx index 086f0307f..b0fc8b9b9 100644 --- a/packages/stateful/components/WalletStakingModal.tsx +++ b/packages/stateful/components/WalletStakingModal.tsx @@ -41,6 +41,10 @@ export const WalletStakingModal = (props: WalletStakingModalProps) => { } = useChainContext() const { address: walletAddress = '', getSigningCosmWasmClient } = useWallet() + if (!nativeToken) { + throw new Error(t('error.missingNativeToken')) + } + const { walletBalance, refreshBalances } = useWalletInfo() // Refreshes validator balances. const setRefreshValidatorBalances = useSetRecoilState( diff --git a/packages/stateful/components/dao/DaoTreasuryHistory.tsx b/packages/stateful/components/dao/DaoTreasuryHistory.tsx index 28d2ae55a..dd4e427c7 100644 --- a/packages/stateful/components/dao/DaoTreasuryHistory.tsx +++ b/packages/stateful/components/dao/DaoTreasuryHistory.tsx @@ -166,12 +166,12 @@ export const InnerDaoTreasuryHistory = ({ const lineGraphValues = useMemo(() => { let runningTotal = convertMicroDenomToDenomWithDecimals( nativeBalance.amount, - nativeToken.decimals + nativeToken?.decimals ?? 0 ) return ( transactions - .filter(({ denomLabel }) => denomLabel === nativeToken.symbol) + .filter(({ denomLabel }) => denomLabel === nativeToken?.symbol) .map(({ amount, outgoing }) => { let currentTotal = runningTotal runningTotal -= (outgoing ? -1 : 1) * amount @@ -183,8 +183,8 @@ export const InnerDaoTreasuryHistory = ({ ) }, [ nativeBalance.amount, - nativeToken.decimals, - nativeToken.symbol, + nativeToken?.decimals, + nativeToken?.symbol, transactions, ]) @@ -199,9 +199,9 @@ export const InnerDaoTreasuryHistory = ({
diff --git a/packages/stateful/components/dao/commonVotingConfig/ProposalDepositVotingConfigItem.tsx b/packages/stateful/components/dao/commonVotingConfig/ProposalDepositVotingConfigItem.tsx index d409151d7..2a272a7d2 100644 --- a/packages/stateful/components/dao/commonVotingConfig/ProposalDepositVotingConfigItem.tsx +++ b/packages/stateful/components/dao/commonVotingConfig/ProposalDepositVotingConfigItem.tsx @@ -159,7 +159,7 @@ const ProposalDepositInput = ({ ] : []), // Then native. - nativeToken, + ...(nativeToken ? [nativeToken] : []), // Then other CW20. { chainId, @@ -171,7 +171,7 @@ const ProposalDepositInput = ({ }, // Then the chain assets. ...getChainAssets(chainId).filter( - ({ denomOrAddress }) => denomOrAddress !== nativeToken.denomOrAddress + ({ denomOrAddress }) => denomOrAddress !== nativeToken?.denomOrAddress ), ] const selectedToken = availableTokens.find( diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/NewAttribute.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/NewAttribute.tsx index e57c876eb..1601e3c92 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/NewAttribute.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/NewAttribute.tsx @@ -168,7 +168,7 @@ export const NewAttribute = ({ className="self-start" onClick={() => appendToken({ - denomOrAddress: nativeToken.denomOrAddress, + denomOrAddress: nativeToken?.denomOrAddress || '', }) } variant="ghost" diff --git a/packages/stateless/components/ChainProvider.tsx b/packages/stateless/components/ChainProvider.tsx index 1cfec8cfd..92d34e52b 100644 --- a/packages/stateless/components/ChainProvider.tsx +++ b/packages/stateless/components/ChainProvider.tsx @@ -2,8 +2,8 @@ import { ReactNode } from 'react' import { getChainForChainId, - getNativeTokenForChainId, getSupportedChainConfig, + maybeGetNativeTokenForChainId, } from '@dao-dao/utils' import { ChainContext } from '../hooks/useChainContext' @@ -18,7 +18,7 @@ export const ChainProvider = ({ chainId, children }: ChainProviderProps) => ( value={{ chainId, chain: getChainForChainId(chainId), - nativeToken: getNativeTokenForChainId(chainId), + nativeToken: maybeGetNativeTokenForChainId(chainId), config: getSupportedChainConfig(chainId), }} > diff --git a/packages/stateless/components/inputs/TokenInput.tsx b/packages/stateless/components/inputs/TokenInput.tsx index d8bf1896f..7fa312bee 100644 --- a/packages/stateless/components/inputs/TokenInput.tsx +++ b/packages/stateless/components/inputs/TokenInput.tsx @@ -132,7 +132,7 @@ export const TokenInput = < const selectedTokenDisplay = useMemo( () => selectedToken ? ( -
+
-

+

{readOnly && amount.toLocaleString(undefined, { // Show as many decimals as possible (max is 20). @@ -232,11 +232,9 @@ export const TokenInput = < token.imageUrl || getFallbackImage(token.denomOrAddress), ...token, rightNode: ( -

+

{allTokensOnSameChain - ? token.denomOrAddress.startsWith('ibc/') - ? token.denomOrAddress.slice(0, 9) + '...' - : token.denomOrAddress + ? token.denomOrAddress : getDisplayNameForChainId(token.chainId)}

), diff --git a/packages/types/chain.ts b/packages/types/chain.ts index 9c92887c9..268654408 100644 --- a/packages/types/chain.ts +++ b/packages/types/chain.ts @@ -7,12 +7,16 @@ import { CodeIdConfig, PolytoneConfig } from './utils' export type IChainContext = { chainId: string chain: Chain - nativeToken: GenericToken + // Chain may not have a native token. + nativeToken?: GenericToken // If defined, this is a supported chain. config?: SupportedChainConfig } -export type SupportedChainContext = Required +// Require supported chain config. +export type SupportedChainContext = Omit & { + config: SupportedChainConfig +} export interface Validator { address: string diff --git a/packages/utils/chain.ts b/packages/utils/chain.ts index 484a2c363..79510f4d2 100644 --- a/packages/utils/chain.ts +++ b/packages/utils/chain.ts @@ -74,7 +74,7 @@ export const cosmosValidatorToValidator = ({ export const getImageUrlForChainId = (chainId: string): string => { // Use native token image if available. - const { imageUrl } = getNativeTokenForChainId(chainId) + const { imageUrl } = maybeGetNativeTokenForChainId(chainId) || {} if (imageUrl) { return imageUrl } @@ -184,6 +184,16 @@ export const getNativeTokenForChainId = (chainId: string): GenericToken => { return cachedNativeTokens[chainId]! } +export const maybeGetNativeTokenForChainId = ( + chainId: string +): GenericToken | undefined => { + try { + return getNativeTokenForChainId(chainId) + } catch { + return undefined + } +} + const cachedTokens: Record = {} export const getTokenForChainIdAndDenom = ( chainId: string,