From 1add0ff3c035156820e436b33d85d378341d208a Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Thu, 26 Sep 2024 16:52:45 -0400 Subject: [PATCH 01/26] started refactoring to use bignumber.js --- packages/state/package.json | 1 + packages/state/query/queries/chain.ts | 5 +- .../queries/contracts/CwVesting.extra.ts | 69 ++++++----- packages/state/query/queries/neutron.ts | 3 +- packages/state/recoil/selectors/skip.ts | 3 +- packages/state/recoil/selectors/token.ts | 114 ++++++++---------- packages/state/recoil/selectors/treasury.ts | 12 +- packages/state/recoil/selectors/wallet.ts | 20 +-- .../ManageStaking/Component.stories.tsx | 13 +- .../core/actions/ManageStaking/Component.tsx | 40 +++--- .../core/actions/ManageStaking/index.tsx | 22 ++-- .../actions/ManageVesting/BeginVesting.tsx | 5 +- .../components/SelfRelayExecuteModal.tsx | 74 ++++++------ .../stateful/components/dao/DaoTokenCard.tsx | 47 ++++---- .../stateful/components/dao/DaoTokenLine.tsx | 2 +- .../stateful/components/dao/tabs/HomeTab.tsx | 32 ++--- .../components/gov/GovCommunityPoolTab.tsx | 24 ++-- .../components/gov/GovProposalVotes.tsx | 3 +- .../stateful/components/gov/GovTokenLine.tsx | 2 +- .../profile/ProfileProposalCard.tsx | 32 ++--- .../components/vesting/VestingPaymentCard.tsx | 5 +- .../vesting/VestingStakingModal.tsx | 7 +- .../components/wallet/WalletStakingModal.tsx | 17 +-- .../components/wallet/WalletTokenCard.tsx | 2 +- .../wallet/WalletTokenCardReadonly.tsx | 2 +- .../components/wallet/WalletTokenLine.tsx | 2 +- .../wallet/WalletTokenLineReadonly.tsx | 2 +- .../GovernanceConfigurationInput.tsx | 3 +- packages/stateful/package.json | 1 + .../common/hooks/makeUsePublishProposal.ts | 34 +++--- .../common/hooks/makeUsePublishProposal.ts | 34 +++--- .../components/ProfileCardMemberInfo.tsx | 11 +- .../components/StakingModal.tsx | 11 +- .../components/ProfileCardMemberInfo.tsx | 5 +- .../components/ProfileCardMemberInfo.tsx | 11 +- .../components/StakingModal.tsx | 11 +- .../components/ProfileCardMemberInfo.tsx | 5 +- .../components/ProfileCardMemberInfo.tsx | 11 +- .../components/StakingModal.tsx | 11 +- .../components/StakingModal.tsx | 12 +- .../ProfileCardMemberInfoTokens.tsx | 64 ++++++---- .../RetroactiveCompensation/Renderer/utils.ts | 16 ++- .../stateless/components/ValidatorPicker.tsx | 15 ++- .../components/token/StakingModal.stories.tsx | 9 +- .../components/token/StakingModal.tsx | 71 ++++++++--- .../components/token/TokenCard.stories.tsx | 48 +++++--- .../stateless/components/token/TokenCard.tsx | 64 +++++++--- .../stateless/components/token/TokenLine.tsx | 14 ++- .../token/UnstakingLine.stories.tsx | 3 +- .../components/token/UnstakingLine.tsx | 13 +- .../components/token/UnstakingModal.tsx | 2 +- .../vesting/VestingPaymentCard.stories.tsx | 5 +- .../components/vesting/VestingPaymentCard.tsx | 59 ++++++--- .../vesting/VestingStepsLineGraph.tsx | 2 +- .../stateless/hooks/useTokenSortOptions.ts | 17 ++- packages/stateless/package.json | 1 + packages/types/components/StakingModal.ts | 6 +- packages/types/package.json | 1 + packages/types/token.ts | 27 +++-- packages/types/vesting.ts | 4 +- packages/utils/conversion.ts | 40 +++--- packages/utils/package.json | 1 + packages/utils/token.ts | 11 +- 63 files changed, 676 insertions(+), 537 deletions(-) diff --git a/packages/state/package.json b/packages/state/package.json index 55f24c659..e6b79b064 100644 --- a/packages/state/package.json +++ b/packages/state/package.json @@ -19,6 +19,7 @@ "@cosmjs/tendermint-rpc": "^0.32.3", "@dao-dao/utils": "2.5.0-rc.3", "@tanstack/react-query": "^5.40.0", + "bignumber.js": "^9.1.2", "graphql": "^16.8.1", "json5": "^2.2.0", "lodash.uniq": "^4.5.0", diff --git a/packages/state/query/queries/chain.ts b/packages/state/query/queries/chain.ts index 9d8241d10..3e48aff7f 100644 --- a/packages/state/query/queries/chain.ts +++ b/packages/state/query/queries/chain.ts @@ -1,6 +1,7 @@ import { fromBase64 } from '@cosmjs/encoding' import { Coin } from '@cosmjs/stargate' import { QueryClient, queryOptions, skipToken } from '@tanstack/react-query' +import { BigNumber } from 'bignumber.js' import uniq from 'lodash.uniq' import { @@ -1219,7 +1220,7 @@ export const fetchGovProposalVotes = async ( /** * Paginated votes with staked amounts. */ - votes: (Vote & { staked: bigint })[] + votes: (Vote & { staked: BigNumber })[] /** * Total votes cast. */ @@ -1252,7 +1253,7 @@ export const fetchGovProposalVotes = async ( return { votes: votes.map((vote, index) => ({ ...vote, - staked: BigInt(stakes[index].amount), + staked: BigNumber(stakes[index].amount), })), total: Number(pagination?.total ?? 0), } diff --git a/packages/state/query/queries/contracts/CwVesting.extra.ts b/packages/state/query/queries/contracts/CwVesting.extra.ts index 5c5bc6c0d..2a6beac1e 100644 --- a/packages/state/query/queries/contracts/CwVesting.extra.ts +++ b/packages/state/query/queries/contracts/CwVesting.extra.ts @@ -1,4 +1,5 @@ import { QueryClient, queryOptions } from '@tanstack/react-query' +import { BigNumber } from 'bignumber.js' import { TokenType, @@ -115,7 +116,7 @@ export const fetchVestingPaymentInfo = async ( { slashes: [] as VestingValidatorWithSlashes[], hasUnregisteredSlashes: false, - actualSlashed: 0n, + actualSlashed: BigNumber(0), }, // Promise.all([ // queryClient.fetchQuery( @@ -137,7 +138,7 @@ export const fetchVestingPaymentInfo = async ( // async ([stakeHistory, unbondingDurationSeconds]): Promise<{ // slashes: VestingValidatorWithSlashes[] // hasUnregisteredSlashes: boolean - // actualSlashed: bigint + // actualSlashed: BigNumber // }> => { // const uniqueValidators = uniq( // stakeHistory?.stakeEvents.flatMap((event) => @@ -181,10 +182,10 @@ export const fetchVestingPaymentInfo = async ( // (slashed, { slashes }) => // slashed + // slashes.reduce( - // (acc, { amount }) => acc + BigInt(amount), - // BigInt(0) + // (acc, { amount }) => acc.plus(amount), + // BigNumber(0) // ), - // BigInt(0) + // BigNumber(0) // ) // return { @@ -203,12 +204,12 @@ export const fetchVestingPaymentInfo = async ( ]) const actualStaked = delegationInfo.delegations.reduce( - (acc, { delegated }) => acc + BigInt(delegated.amount), - 0n + (acc, { delegated }) => acc.plus(BigNumber(delegated.amount)), + BigNumber(0) ) const actualUnstaking = delegationInfo.unbondingDelegations.reduce( - (acc, { balance }) => acc + BigInt(balance.amount), - 0n + (acc, { balance }) => acc.plus(BigNumber(balance.amount)), + BigNumber(0) ) // If cannot compute the actual slashed amount, then we cannot compute the @@ -216,13 +217,12 @@ export const fetchVestingPaymentInfo = async ( const stakable = actualSlashed === undefined ? '0' - : ( - BigInt(total) - - BigInt(vest.claimed) - - BigInt(actualStaked) - - BigInt(actualUnstaking) - - actualSlashed - ).toString() + : BigNumber(total) + .minus(BigNumber(vest.claimed)) + .minus(BigNumber(actualStaked)) + .minus(BigNumber(actualUnstaking)) + .minus(actualSlashed) + .toString() const completed = (vest.status === 'funded' || @@ -238,16 +238,20 @@ export const fetchVestingPaymentInfo = async ( ? [ { timestamp: startTimeMs, - amount: convertMicroDenomToDenomWithDecimals( - vest.vested.constant.y, - token.decimals + amount: BigNumber( + convertMicroDenomToDenomWithDecimals( + vest.vested.constant.y, + token.decimals + ) ), }, { timestamp: startTimeMs, - amount: convertMicroDenomToDenomWithDecimals( - vest.vested.constant.y, - token.decimals + amount: BigNumber( + convertMicroDenomToDenomWithDecimals( + vest.vested.constant.y, + token.decimals + ) ), }, ] @@ -255,16 +259,20 @@ export const fetchVestingPaymentInfo = async ( ? [ { timestamp: startTimeMs + vest.vested.saturating_linear.min_x * 1000, - amount: convertMicroDenomToDenomWithDecimals( - vest.vested.saturating_linear.min_y, - token.decimals + amount: BigNumber( + convertMicroDenomToDenomWithDecimals( + vest.vested.saturating_linear.min_y, + token.decimals + ) ), }, { timestamp: startTimeMs + vest.vested.saturating_linear.max_x * 1000, - amount: convertMicroDenomToDenomWithDecimals( - vest.vested.saturating_linear.max_y, - token.decimals + amount: BigNumber( + convertMicroDenomToDenomWithDecimals( + vest.vested.saturating_linear.max_y, + token.decimals + ) ), }, ] @@ -279,9 +287,8 @@ export const fetchVestingPaymentInfo = async ( ...acc, { timestamp: startTimeMs + seconds * 1000, - amount: convertMicroDenomToDenomWithDecimals( - amount, - token.decimals + amount: BigNumber( + convertMicroDenomToDenomWithDecimals(amount, token.decimals) ), }, ] diff --git a/packages/state/query/queries/neutron.ts b/packages/state/query/queries/neutron.ts index 9a48944c5..f58abd13e 100644 --- a/packages/state/query/queries/neutron.ts +++ b/packages/state/query/queries/neutron.ts @@ -1,4 +1,5 @@ import { QueryClient, queryOptions } from '@tanstack/react-query' +import { BigNumber } from 'bignumber.js' import uniq from 'lodash.uniq' import { ChainId, GenericTokenBalance, TokenType } from '@dao-dao/types' @@ -43,7 +44,7 @@ export const fetchNeutronIbcTransferFee = async ( token: tokens.find((token) => token.denomOrAddress === denom)!, balance: fees .filter(({ denom: feeDenom }) => feeDenom === denom) - .reduce((acc, { amount }) => acc + BigInt(amount), 0n) + .reduce((acc, { amount }) => acc.plus(amount), BigNumber(0)) .toString(), })), } diff --git a/packages/state/recoil/selectors/skip.ts b/packages/state/recoil/selectors/skip.ts index 519691041..9e430febd 100644 --- a/packages/state/recoil/selectors/skip.ts +++ b/packages/state/recoil/selectors/skip.ts @@ -1,3 +1,4 @@ +import { BigNumber } from 'bignumber.js' import { selectorFamily } from 'recoil' import { @@ -150,7 +151,7 @@ export const skipRouteSelector = selectorFamily< source_asset_denom: sourceDenom, dest_asset_chain_id: toChainId, dest_asset_denom: asset.denom, - amount_in: BigInt(amountIn).toString(), + amount_in: BigNumber(amountIn).toString(), }), }) ).json() diff --git a/packages/state/recoil/selectors/token.ts b/packages/state/recoil/selectors/token.ts index bbeaf2ed8..842be1688 100644 --- a/packages/state/recoil/selectors/token.ts +++ b/packages/state/recoil/selectors/token.ts @@ -1,3 +1,5 @@ +import { BigNumber } from 'bignumber.js' +import { uniqBy } from 'lodash' import { selectorFamily, waitForAll, @@ -15,12 +17,12 @@ import { TokenCardLazyInfo, TokenPriceHistoryRange, TokenType, + UnstakingTask, UnstakingTaskStatus, WithChainId, } from '@dao-dao/types' import { MAINNET, - convertMicroDenomToDenomWithDecimals, getChainForChainId, getChainForChainName, getIbcTransferInfoFromChannel, @@ -390,9 +392,7 @@ export const genericTokenUndelegatingBalancesSelector = selectorFamily< } acc.push(existing) } - existing.balance = ( - BigInt(existing.balance) + BigInt(balance) - ).toString() + existing.balance = BigNumber(existing.balance).plus(balance).toString() return acc }, [] as GenericTokenBalance[]) @@ -592,7 +592,7 @@ export const tokenCardLazyInfoSelector = selectorFamily< owner: string token: GenericToken // For calculating totalBalance. - unstakedBalance: number + unstakedBalance: string } >({ key: 'tokenCardLazyInfo', @@ -640,13 +640,10 @@ export const tokenCardLazyInfoSelector = selectorFamily< ) const unstakingTasks = unbondingDelegations.map( - ({ balance, finishesAt }) => ({ + ({ balance, finishesAt }): UnstakingTask => ({ token, status: UnstakingTaskStatus.Unstaking, - amount: convertMicroDenomToDenomWithDecimals( - balance.amount, - token.decimals - ), + amount: BigNumber(balance.amount), date: finishesAt, }) ) @@ -655,29 +652,29 @@ export const tokenCardLazyInfoSelector = selectorFamily< ({ validator, delegated, pendingReward }) => ({ token, validator, - amount: convertMicroDenomToDenomWithDecimals( - delegated.amount, - token.decimals - ), - rewards: convertMicroDenomToDenomWithDecimals( - pendingReward.amount, - token.decimals - ), + amount: BigNumber(delegated.amount), + rewards: BigNumber(pendingReward.amount), }) ) - const totalStaked = - stakes.reduce((acc, stake) => acc + stake.amount, 0) ?? 0 - const totalPendingRewards = - stakes?.reduce((acc, stake) => acc + stake.rewards, 0) ?? 0 - const totalUnstaking = - unstakingTasks.reduce( - (acc, task) => - acc + + const totalStaked = stakes.reduce( + (acc, stake) => acc.plus(stake.amount), + BigNumber(0) + ) + const totalPendingRewards = stakes.reduce( + (acc, stake) => acc.plus(stake.rewards), + BigNumber(0) + ) + const totalUnstaking = unstakingTasks.reduce( + (acc, task) => + acc.plus( // Only include balance of unstaking tasks. - (task.status === UnstakingTaskStatus.Unstaking ? task.amount : 0), - 0 - ) ?? 0 + task.status === UnstakingTaskStatus.Unstaking + ? task.amount + : BigNumber(0) + ), + BigNumber(0) + ) stakingInfo = { unstakingTasks, @@ -699,40 +696,29 @@ export const tokenCardLazyInfoSelector = selectorFamily< }) ) // Only include DAOs this owner has staked with. - .filter(({ stakedBalance }) => stakedBalance > 0) - .map(({ stakedBalance, ...rest }) => ({ - ...rest, - // Convert to expected denom. - stakedBalance: convertMicroDenomToDenomWithDecimals( - stakedBalance, - token.decimals - ), - })) + .filter(({ stakedBalance }) => stakedBalance.isPositive()) } - const totalBalance = - unstakedBalance + - // Add staked and unstaking balances. - (stakingInfo - ? stakingInfo.totalStaked + stakingInfo.totalUnstaking - : 0) + - // Add balances staked in DAOs, grouped by their - // `stakingContractAddress` so we don't double-count tokens staked with - // the same staking contract if that staking contract is used in - // different DAOs in the list. - Object.values( - daosGoverned?.reduce( - (acc, { stakingContractAddress, stakedBalance = 0 }) => ({ - ...acc, - // If the staking contract address is already in the accumulator, - // overwrite so we don't double-count. All staked balances for the - // same staking contract should be the same, so overwriting should - // do nothing. - [stakingContractAddress]: stakedBalance, - }), - {} as Record - ) || {} - ).reduce((acc, stakedBalance) => acc + stakedBalance, 0) + const totalBalance = BigNumber(unstakedBalance) + .plus( + // Add staked and unstaking balances. + stakingInfo + ? stakingInfo.totalStaked.plus(stakingInfo.totalUnstaking) + : 0 + ) + .plus( + // Add balances staked in DAOs, unique by their + // `stakingContractAddress` so we don't double-count tokens staked + // with the same staking contract if that staking contract is used in + // different DAOs in the list. + uniqBy( + daosGoverned || [], + ({ stakingContractAddress }) => stakingContractAddress + ).reduce( + (acc, { stakedBalance }) => acc.plus(stakedBalance || BigNumber(0)), + BigNumber(0) + ) + ) return { usdUnitPrice, @@ -794,7 +780,7 @@ export const tokenDaosWithStakedBalanceSelector = selectorFamily< { coreAddress: string stakingContractAddress: string - stakedBalance: number + stakedBalance: BigNumber }[], WithChainId<{ type: TokenType @@ -854,10 +840,10 @@ export const tokenDaosWithStakedBalanceSelector = selectorFamily< .map(({ coreAddress, stakingContractAddress }, index) => ({ coreAddress, stakingContractAddress, - stakedBalance: Number(daosWalletStakedTokens[index]), + stakedBalance: BigNumber(daosWalletStakedTokens[index]), })) // Sort descending by staked tokens. - .sort((a, b) => b.stakedBalance - a.stakedBalance) + .sort((a, b) => b.stakedBalance.minus(a.stakedBalance).toNumber()) return daosWithBalances }, diff --git a/packages/state/recoil/selectors/treasury.ts b/packages/state/recoil/selectors/treasury.ts index 4c24afce5..539a268ab 100644 --- a/packages/state/recoil/selectors/treasury.ts +++ b/packages/state/recoil/selectors/treasury.ts @@ -1,5 +1,6 @@ import { parseCoins } from '@cosmjs/proto-signing' import { Event, IndexedTx } from '@cosmjs/stargate' +import { BigNumber } from 'bignumber.js' import uniq from 'lodash.uniq' import { noWait, selectorFamily, waitForAll, waitForNone } from 'recoil' @@ -248,11 +249,6 @@ export const treasuryTokenCardInfosForDaoSelector = selectorFamily< balance, isGovernanceToken = false, }): TokenCardInfo | [] => { - const unstakedBalance = convertMicroDenomToDenomWithDecimals( - balance, - token.decimals - ) - let hasStakingInfo = false // Staking info only exists for native token. if ( @@ -288,7 +284,7 @@ export const treasuryTokenCardInfosForDaoSelector = selectorFamily< tokenCardLazyInfoSelector({ owner: account.address, token, - unstakedBalance, + unstakedBalance: balance, }) ) ) @@ -297,12 +293,12 @@ export const treasuryTokenCardInfosForDaoSelector = selectorFamily< owner: account, token, isGovernanceToken, - unstakedBalance, + unstakedBalance: BigNumber(balance), hasStakingInfo, lazyInfo: loadableToLoadingData(lazyInfo, { usdUnitPrice: undefined, stakingInfo: undefined, - totalBalance: unstakedBalance, + totalBalance: BigNumber(balance), }), } } diff --git a/packages/state/recoil/selectors/wallet.ts b/packages/state/recoil/selectors/wallet.ts index 462f0d874..22891cc11 100644 --- a/packages/state/recoil/selectors/wallet.ts +++ b/packages/state/recoil/selectors/wallet.ts @@ -1,3 +1,4 @@ +import { BigNumber } from 'bignumber.js' import { Loadable, atomFamily, @@ -25,7 +26,6 @@ import { INACTIVE_DAO_NAMES, KVPK_API_BASE, ME_SAVED_TX_PREFIX, - convertMicroDenomToDenomWithDecimals, getFallbackImage, getNativeTokenForChainId, loadableToLoadingData, @@ -357,11 +357,6 @@ export const walletTokenCardInfosSelector = selectorFamily< const infos: TokenCardInfo[] = [ ...nativeBalances.flatMap((accountBalances, accountIndex) => accountBalances.map(({ token, balance }) => { - const unstakedBalance = convertMicroDenomToDenomWithDecimals( - balance, - token.decimals - ) - // Staking info only exists for native token. const hasStakingInfo = token.denomOrAddress === @@ -383,7 +378,7 @@ export const walletTokenCardInfosSelector = selectorFamily< tokenCardLazyInfoSelector({ owner: owner.address, token, - unstakedBalance, + unstakedBalance: balance, }) ) ) @@ -392,12 +387,12 @@ export const walletTokenCardInfosSelector = selectorFamily< owner, token, isGovernanceToken: false, - unstakedBalance, + unstakedBalance: BigNumber(balance), hasStakingInfo, lazyInfo: loadableToLoadingData(lazyInfo, { usdUnitPrice: undefined, stakingInfo: undefined, - totalBalance: unstakedBalance, + totalBalance: BigNumber(balance), }), } @@ -410,17 +405,14 @@ export const walletTokenCardInfosSelector = selectorFamily< return [] } - const unstakedBalance = convertMicroDenomToDenomWithDecimals( - cw20Contracts[index].balance || '0', - token.decimals - ) + const unstakedBalance = BigNumber(cw20Contracts[index].balance || '0') const lazyInfo = get( noWait( tokenCardLazyInfoSelector({ owner: walletAddress, token, - unstakedBalance, + unstakedBalance: unstakedBalance.toString(), }) ) ) diff --git a/packages/stateful/actions/core/actions/ManageStaking/Component.stories.tsx b/packages/stateful/actions/core/actions/ManageStaking/Component.stories.tsx index 8541d9b23..085b82edd 100644 --- a/packages/stateful/actions/core/actions/ManageStaking/Component.stories.tsx +++ b/packages/stateful/actions/core/actions/ManageStaking/Component.stories.tsx @@ -1,4 +1,5 @@ import { ComponentMeta, ComponentStory } from '@storybook/react' +import { BigNumber } from 'bignumber.js' import { token } from '@dao-dao/stateless/components/token/TokenCard.stories' import { CHAIN_ID } from '@dao-dao/storybook' @@ -33,7 +34,7 @@ const stakes: TokenStake[] = [ { token, // Random price between 0 and 10000 with up to 6 decimals. - amount: Math.floor(Math.random() * (10000 * 1e6) + 1e6) / 1e6, + amount: BigNumber(Math.floor(Math.random() * (10000 * 1e6) + 1e6) / 1e6), validator: { address: 'sparkIBC', moniker: 'Spark IBC', @@ -43,12 +44,12 @@ const stakes: TokenStake[] = [ status: 'BOND_STATUS_BONDED', tokens: 5, }, - rewards: 1.23, + rewards: BigNumber(1.23), }, { token, // Random price between 0 and 10000 with up to 6 decimals. - amount: Math.floor(Math.random() * (10000 * 1e6) + 1e6) / 1e6, + amount: BigNumber(Math.floor(Math.random() * (10000 * 1e6) + 1e6) / 1e6), validator: { address: 'elsehow', moniker: 'elsehow', @@ -58,12 +59,12 @@ const stakes: TokenStake[] = [ status: 'BOND_STATUS_BONDED', tokens: 6.2, }, - rewards: 4.56, + rewards: BigNumber(4.56), }, { token, // Random price between 0 and 10000 with up to 6 decimals. - amount: Math.floor(Math.random() * (10000 * 1e6) + 1e6) / 1e6, + amount: BigNumber(Math.floor(Math.random() * (10000 * 1e6) + 1e6) / 1e6), validator: { address: 'cosmostation', moniker: 'Cosmostation', @@ -73,7 +74,7 @@ const stakes: TokenStake[] = [ status: 'BOND_STATUS_BONDED', tokens: 7, }, - rewards: 7.89, + rewards: BigNumber(7.89), }, ] diff --git a/packages/stateful/actions/core/actions/ManageStaking/Component.tsx b/packages/stateful/actions/core/actions/ManageStaking/Component.tsx index 8ecba5944..3dbe3bd62 100644 --- a/packages/stateful/actions/core/actions/ManageStaking/Component.tsx +++ b/packages/stateful/actions/core/actions/ManageStaking/Component.tsx @@ -1,3 +1,4 @@ +import { BigNumber } from 'bignumber.js' import clsx from 'clsx' import { TFunction } from 'next-i18next' import { ComponentType, useCallback, useEffect } from 'react' @@ -132,22 +133,19 @@ export const ManageStakingComponent: ActionComponent< (isValidValidatorAddress(validator, bech32Prefix) && stakes.find(({ validator: { address } }) => address === validator) ?.amount) || - 0 + BigNumber(0) const sourceValidatorPendingRewards = (isValidValidatorAddress(validator, bech32Prefix) && stakes.find(({ validator: { address } }) => address === validator) ?.rewards) || - 0 + BigNumber(0) // If staking, maxAmount is denom treasury balance. Otherwise (for // undelegating and redelegating), maxAmount is the staked amount for the // source validator. const maxAmount = type === StakingActionType.Delegate - ? convertMicroDenomToDenomWithDecimals( - nativeBalance, - nativeToken.decimals - ) + ? BigNumber(nativeBalance) : sourceValidatorStaked // Manually validate based on context. @@ -179,15 +177,13 @@ export const ManageStakingComponent: ActionComponent< return true } - const humanReadableAmount = maxAmount.toLocaleString(undefined, { - maximumFractionDigits: 6, - }) + const humanReadableAmount = maxAmount.toFormat(6) // Logic for undelegating. if (type === StakingActionType.Undelegate) { return ( - Number(amount) <= sourceValidatorStaked || - (sourceValidatorStaked === 0 + sourceValidatorStaked.gte(amount) || + (sourceValidatorStaked.isZero() ? t('error.nothingStaked') : t('error.stakeInsufficient', { amount: humanReadableAmount, @@ -208,8 +204,8 @@ export const ManageStakingComponent: ActionComponent< } return ( - Number(amount) <= sourceValidatorStaked || - (sourceValidatorStaked === 0 + sourceValidatorStaked.gte(amount) || + (sourceValidatorStaked.isZero() ? t('error.nothingStaked') : t('error.stakeInsufficient', { amount: humanReadableAmount, @@ -253,9 +249,12 @@ export const ManageStakingComponent: ActionComponent< // high. We don't want to make this an error because often people want to // spend funds that a previous action makes available, so just show a warning. const delegateWarning = - isCreating && amount > maxAmount && type === StakingActionType.Delegate + isCreating && maxAmount.lt(amount) && type === StakingActionType.Delegate ? t('error.insufficientFundsWarning', { - amount: maxAmount.toLocaleString(undefined, { + amount: convertMicroDenomToDenomWithDecimals( + maxAmount, + nativeToken.decimals + ).toLocaleString(undefined, { maximumFractionDigits: nativeToken.decimals, }), tokenSymbol: nativeToken.symbol, @@ -376,7 +375,7 @@ export const ManageStakingComponent: ActionComponent< // If claiming rewards, show pending rewards if not executed, and // claimed rewards if executed. (executed && !!claimedRewards) || - (!executed && sourceValidatorPendingRewards > 0)) && + (!executed && sourceValidatorPendingRewards.isPositive())) && // Only show balance if creating. isCreating && (
@@ -393,13 +392,14 @@ export const ManageStakingComponent: ActionComponent<

{ ({ validator, delegated, pendingReward }) => ({ token: nativeToken, validator, - amount: convertMicroDenomToDenomWithDecimals( - delegated.amount, - nativeToken.decimals - ), - rewards: convertMicroDenomToDenomWithDecimals( - pendingReward.amount, - nativeToken.decimals - ), + amount: BigNumber(delegated.amount), + rewards: BigNumber(pendingReward.amount), }) ), validators: loadingValidators.loading ? [] : loadingValidators.data, @@ -344,12 +339,11 @@ export class ManageStakingAction extends ActionBase { chainId ) const nativeToken = getNativeTokenForChainId(chainId) - const microAmount = convertDenomToMicroDenomWithDecimals( - macroAmount, - nativeToken.decimals - ) const amount = coin( - BigInt(microAmount).toString(), + convertDenomToMicroDenomStringWithDecimals( + macroAmount, + nativeToken.decimals + ), nativeToken.denomOrAddress ) diff --git a/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx b/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx index 4f98191ac..1eff8bf4b 100644 --- a/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx +++ b/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx @@ -5,6 +5,7 @@ import { SubdirectoryArrowRightRounded, WarningRounded, } from '@mui/icons-material' +import { BigNumber } from 'bignumber.js' import clsx from 'clsx' import { ComponentType, useCallback, useEffect } from 'react' import { useFieldArray, useFormContext } from 'react-hook-form' @@ -201,13 +202,13 @@ export const BeginVesting: ActionComponent = ({ const lastMs = index === 0 ? startDate.getTime() : acc[acc.length - 1].timestamp - const lastAmount = index === 0 ? 0 : acc[acc.length - 1].amount + const lastAmount = index === 0 ? BigNumber(0) : acc[acc.length - 1].amount return [ ...acc, { timestamp: lastMs + delayMs, - amount: lastAmount + (percent / 100) * watchAmount, + amount: lastAmount.plus((percent / 100) * watchAmount), }, ] }, [] as VestingStep[]) diff --git a/packages/stateful/components/SelfRelayExecuteModal.tsx b/packages/stateful/components/SelfRelayExecuteModal.tsx index 5770b0030..0cd21af39 100644 --- a/packages/stateful/components/SelfRelayExecuteModal.tsx +++ b/packages/stateful/components/SelfRelayExecuteModal.tsx @@ -19,6 +19,7 @@ import { import { ChainWalletBase } from '@cosmos-kit/core' import { Check, Close, Send, Verified } from '@mui/icons-material' import { useQueryClient } from '@tanstack/react-query' +import { BigNumber } from 'bignumber.js' import { MsgGrant as MsgGrantEncoder } from 'cosmjs-types/cosmos/authz/v1beta1/tx' import uniq from 'lodash.uniq' import { Fragment, useEffect, useState } from 'react' @@ -156,7 +157,7 @@ export const SelfRelayExecuteModal = ({ ) // Amount funded once funding is complete. const [fundedAmount, setFundedAmount] = useState< - Record + Record >({}) const [executeTx, setExecuteTx] = useState>() @@ -166,7 +167,7 @@ export const SelfRelayExecuteModal = ({ }>() // Amount refunded once refunding is complete. const [refundedAmount, setRefundedAmount] = useState< - Record + Record >({}) // If relay fails and user decides to refund and cancel, this will be set to @@ -457,30 +458,30 @@ export const SelfRelayExecuteModal = ({ const fundsNeeded = // Give a little extra to cover the authz tx fee. - getRelayerFundsRef.current(chainId) * 1.2 - - Number(currentBalance.amount) - - let msgs: EncodeObject[] = - fundsNeeded > 0 - ? // Send tokens to relayer wallet if needed. - [ - cwMsgToEncodeObject( - chainId, - { - bank: { - send: { - amount: coins( - BigInt(fundsNeeded).toString(), - relayer.feeToken.denomOrAddress - ), - to_address: relayer.relayerAddress, - }, + BigNumber(getRelayerFundsRef.current(chainId) * 1.2).minus( + currentBalance.amount + ) + + let msgs: EncodeObject[] = fundsNeeded.isPositive() + ? // Send tokens to relayer wallet if needed. + [ + cwMsgToEncodeObject( + chainId, + { + bank: { + send: { + amount: coins( + fundsNeeded.toString(), + relayer.feeToken.denomOrAddress + ), + to_address: relayer.relayerAddress, }, }, - relayer.wallet.address - ), - ] - : [] + }, + relayer.wallet.address + ), + ] + : [] // Add execute message if executing and has not already executed. if (withExecuteRelay && !executeTx && transaction.type === 'execute') { @@ -506,7 +507,7 @@ export const SelfRelayExecuteModal = ({ } // Get new balance of relayer wallet. - const newBalance = Number( + const newBalance = BigNumber( ( await relayer.client.query.bank.balance( relayer.relayerAddress, @@ -541,7 +542,7 @@ export const SelfRelayExecuteModal = ({ authorization: SendAuthorization.toProtoMsg( SendAuthorization.fromPartial({ spendLimit: coins( - BigInt(newBalance).toString(), + newBalance.toString(), relayer.feeToken.denomOrAddress ), }) @@ -1043,15 +1044,16 @@ export const SelfRelayExecuteModal = ({ // @ts-ignore client.gasPrice ) - const remainingTokensAfterFee = - Number(remainingTokens.amount) - Number(fee.amount[0].amount) + const remainingTokensAfterFee = BigNumber(remainingTokens.amount).minus( + fee.amount[0].amount + ) // Send remaining tokens if there are more than enough to pay the fee. - if (remainingTokensAfterFee > 0) { + if (remainingTokensAfterFee.isPositive()) { await client.sign.sendTokens( relayerAddress, wallet.address, - coins(BigInt(remainingTokensAfterFee).toString(), feeDenom), + coins(remainingTokensAfterFee.toString(), feeDenom), fee ) @@ -1171,12 +1173,14 @@ export const SelfRelayExecuteModal = ({ const funds = !relayerFunds.loading && !relayerFunds.errored - ? Number(relayerFunds.data[index].amount) + ? BigNumber(relayerFunds.data[index].amount) : // Use the previously funded amount if the step is past. - fundedAmount[chain_id] ?? 0 - const empty = funds === 0 + fundedAmount[chain_id] ?? BigNumber(0) + const empty = funds.isZero() - const funded = funds >= getRelayerFundsRef.current(chain_id) + const funded = funds.gte( + getRelayerFundsRef.current(chain_id) + ) const isExecute = index === 0 // If this is the execute, we need to make sure all @@ -1390,7 +1394,7 @@ export const SelfRelayExecuteModal = ({ : 0 const empty = funds === 0 - const refunded = refundedAmount[chain_id] ?? 0 + const refunded = refundedAmount[chain_id] ?? BigNumber(0) return ( diff --git a/packages/stateful/components/dao/DaoTokenCard.tsx b/packages/stateful/components/dao/DaoTokenCard.tsx index da02886e3..cb106cc2a 100644 --- a/packages/stateful/components/dao/DaoTokenCard.tsx +++ b/packages/stateful/components/dao/DaoTokenCard.tsx @@ -56,7 +56,7 @@ export const DaoTokenCard = ({ tokenCardLazyInfoSelector({ owner: owner.address, token, - unstakedBalance, + unstakedBalance: unstakedBalance.toString(), }), { usdUnitPrice: undefined, @@ -101,7 +101,9 @@ export const DaoTokenCard = ({ const lazyStakes = lazyInfo.loading ? [] : lazyInfo.data.stakingInfo?.stakes ?? [] - const stakesWithRewards = lazyStakes.filter(({ rewards }) => rewards > 0) + const stakesWithRewards = lazyStakes.filter(({ rewards }) => + rewards.isPositive() + ) const nativeToken = getNativeTokenForChainId(token.chainId) @@ -131,37 +133,36 @@ export const DaoTokenCard = ({ // Prefill URL is valid if... const proposeStakeUnstakeHref = // ...there is something to stake or unstake - (unstakedBalance > 0 || lazyStakes.length > 0) && + (unstakedBalance.isPositive() || lazyStakes.length > 0) && // ...and this is the native token token.denomOrAddress === nativeToken.denomOrAddress ? getDaoProposalPath(coreAddress, 'create', { prefill: getDaoProposalSinglePrefill({ // If has unstaked, show stake action by default. - actions: - unstakedBalance > 0 - ? [ - { - actionKey: ActionKey.ManageStaking, - data: { - chainId: token.chainId, - stakeType: StakingActionType.Delegate, - validator: '', - amount: unstakedBalance, - denom: token.denomOrAddress, - }, - }, - ] - : // If has only staked, show unstake actions by default. - lazyStakes.map(({ validator, amount }) => ({ + actions: unstakedBalance.isPositive() + ? [ + { actionKey: ActionKey.ManageStaking, data: { chainId: token.chainId, - stakeType: StakingActionType.Undelegate, - validator, - amount, + stakeType: StakingActionType.Delegate, + validator: '', + amount: unstakedBalance, denom: token.denomOrAddress, }, - })), + }, + ] + : // If has only staked, show unstake actions by default. + lazyStakes.map(({ validator, amount }) => ({ + actionKey: ActionKey.ManageStaking, + data: { + chainId: token.chainId, + stakeType: StakingActionType.Undelegate, + validator, + amount, + denom: token.denomOrAddress, + }, + })), }), }) : undefined diff --git a/packages/stateful/components/dao/DaoTokenLine.tsx b/packages/stateful/components/dao/DaoTokenLine.tsx index 8fab1ae43..f6bd66df7 100644 --- a/packages/stateful/components/dao/DaoTokenLine.tsx +++ b/packages/stateful/components/dao/DaoTokenLine.tsx @@ -15,7 +15,7 @@ export const DaoTokenLine = ( tokenCardLazyInfoSelector({ owner: props.owner.address, token: props.token, - unstakedBalance: props.unstakedBalance, + unstakedBalance: props.unstakedBalance.toString(), }), { usdUnitPrice: undefined, diff --git a/packages/stateful/components/dao/tabs/HomeTab.tsx b/packages/stateful/components/dao/tabs/HomeTab.tsx index 9488535ac..2ca03023b 100644 --- a/packages/stateful/components/dao/tabs/HomeTab.tsx +++ b/packages/stateful/components/dao/tabs/HomeTab.tsx @@ -1,3 +1,4 @@ +import { BigNumber } from 'bignumber.js' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { waitForAll } from 'recoil' @@ -54,19 +55,20 @@ export const HomeTab = () => { // Get max deposit of governance token across all proposal modules. const maxGovernanceTokenProposalModuleDeposit = proposalModuleDepositInfosLoadable.state !== 'hasValue' - ? 0 - : Math.max( - ...proposalModuleDepositInfosLoadable.contents - .filter( - (depositInfo): depositInfo is CheckedDepositInfo => - !!depositInfo && - ('cw20' in depositInfo.denom - ? depositInfo.denom.cw20 - : depositInfo.denom.native) === governanceDenomOrAddress - ) - .map(({ amount }) => Number(amount)), - 0 - ) + ? BigNumber(0) + : proposalModuleDepositInfosLoadable.contents + .filter( + (depositInfo): depositInfo is CheckedDepositInfo => + !!depositInfo && + ('cw20' in depositInfo.denom + ? depositInfo.denom.cw20 + : depositInfo.denom.native) === governanceDenomOrAddress + ) + // Get max. + .reduce( + (acc, { amount }) => (acc.gt(amount) ? acc : BigNumber(amount)), + BigNumber(0) + ) const hasRewardDistributors = getDaoRewardDistributors(dao.info.items).length > 0 @@ -89,8 +91,8 @@ export const HomeTab = () => { {isWalletConnected && !isSecretNetworkPermitNeeded ? ( 0 - ? BigInt(maxGovernanceTokenProposalModuleDeposit).toString() + maxGovernanceTokenProposalModuleDeposit.isPositive() + ? maxGovernanceTokenProposalModuleDeposit.toString() : undefined } /> diff --git a/packages/stateful/components/gov/GovCommunityPoolTab.tsx b/packages/stateful/components/gov/GovCommunityPoolTab.tsx index d1084ed9b..05c5ece85 100644 --- a/packages/stateful/components/gov/GovCommunityPoolTab.tsx +++ b/packages/stateful/components/gov/GovCommunityPoolTab.tsx @@ -1,3 +1,4 @@ +import { BigNumber } from 'bignumber.js' import { waitForAny } from 'recoil' import { @@ -15,11 +16,7 @@ import { useTokenSortOptions, } from '@dao-dao/stateless' import { LoadingDataWithError, TokenCardInfo } from '@dao-dao/types' -import { - convertMicroDenomToDenomWithDecimals, - getNativeTokenForChainId, - loadableToLoadingData, -} from '@dao-dao/utils' +import { getNativeTokenForChainId, loadableToLoadingData } from '@dao-dao/utils' import { GovActionsProvider } from '../../actions' import { GovTokenLine } from './GovTokenLine' @@ -33,23 +30,18 @@ export const GovCommunityPoolTab = () => { }), (data) => data - .map(({ owner, token, balance }): TokenCardInfo => { - const unstakedBalance = convertMicroDenomToDenomWithDecimals( - balance, - token.decimals - ) - - return { + .map( + ({ owner, token, balance }): TokenCardInfo => ({ owner, token, isGovernanceToken: getNativeTokenForChainId(token.chainId).denomOrAddress === token.denomOrAddress, - unstakedBalance, + unstakedBalance: BigNumber(balance), hasStakingInfo: false, lazyInfo: { loading: true }, - } - }) + }) + ) // Sort governance token first and factory tokens last. .sort((a, b) => { if (a.isGovernanceToken) { @@ -83,7 +75,7 @@ export const GovCommunityPoolTab = () => { tokenCardLazyInfoSelector({ owner: owner.address, token, - unstakedBalance, + unstakedBalance: unstakedBalance.toString(), }) ) ) diff --git a/packages/stateful/components/gov/GovProposalVotes.tsx b/packages/stateful/components/gov/GovProposalVotes.tsx index 479c67ae4..deb203a5c 100644 --- a/packages/stateful/components/gov/GovProposalVotes.tsx +++ b/packages/stateful/components/gov/GovProposalVotes.tsx @@ -68,8 +68,7 @@ const InnerGovProposalVotes = ({ voterAddress: voter, vote: options.sort((a, b) => Number(b.weight) - Number(a.weight))[0] .option, - votingPowerPercent: - Number(staked) / Number(BigInt(bondedTokens) / 100n), + votingPowerPercent: staked.div(bondedTokens).div(100).toNumber(), }) ) diff --git a/packages/stateful/components/gov/GovTokenLine.tsx b/packages/stateful/components/gov/GovTokenLine.tsx index a02ce2e25..b499b7644 100644 --- a/packages/stateful/components/gov/GovTokenLine.tsx +++ b/packages/stateful/components/gov/GovTokenLine.tsx @@ -15,7 +15,7 @@ export const GovTokenLine = ( tokenCardLazyInfoSelector({ owner: props.owner.address, token: props.token, - unstakedBalance: props.unstakedBalance, + unstakedBalance: props.unstakedBalance.toString(), }), { usdUnitPrice: undefined, diff --git a/packages/stateful/components/profile/ProfileProposalCard.tsx b/packages/stateful/components/profile/ProfileProposalCard.tsx index f3735a777..67cbffe96 100644 --- a/packages/stateful/components/profile/ProfileProposalCard.tsx +++ b/packages/stateful/components/profile/ProfileProposalCard.tsx @@ -1,3 +1,4 @@ +import { BigNumber } from 'bignumber.js' import { useMemo } from 'react' import { useSetRecoilState, waitForAll } from 'recoil' @@ -63,19 +64,20 @@ export const ProfileProposalCard = () => { // Get max deposit of governance token across all proposal modules. const maxGovernanceTokenProposalModuleDeposit = proposalModuleDepositInfosLoadable.state !== 'hasValue' - ? 0 - : Math.max( - ...proposalModuleDepositInfosLoadable.contents - .filter( - (depositInfo): depositInfo is CheckedDepositInfo => - !!depositInfo && - ('cw20' in depositInfo.denom - ? depositInfo.denom.cw20 - : depositInfo.denom.native) === governanceDenomOrAddress - ) - .map(({ amount }) => Number(amount)), - 0 - ) + ? BigNumber(0) + : proposalModuleDepositInfosLoadable.contents + .filter( + (depositInfo): depositInfo is CheckedDepositInfo => + !!depositInfo && + ('cw20' in depositInfo.denom + ? depositInfo.denom.cw20 + : depositInfo.denom.native) === governanceDenomOrAddress + ) + // Get max. + .reduce( + (acc, { amount }) => (acc.gt(amount) ? acc : BigNumber(amount)), + BigNumber(0) + ) // If wallet is a member right now as opposed to when the proposal was open. // Relevant for showing them membership join info or not. @@ -134,8 +136,8 @@ export const ProfileProposalCard = () => { 0 - ? BigInt(maxGovernanceTokenProposalModuleDeposit).toString() + maxGovernanceTokenProposalModuleDeposit.isPositive() + ? maxGovernanceTokenProposalModuleDeposit.toString() : undefined } /> diff --git a/packages/stateful/components/vesting/VestingPaymentCard.tsx b/packages/stateful/components/vesting/VestingPaymentCard.tsx index e50810ee3..0ce0b1f55 100644 --- a/packages/stateful/components/vesting/VestingPaymentCard.tsx +++ b/packages/stateful/components/vesting/VestingPaymentCard.tsx @@ -1,4 +1,5 @@ import { useQueryClient } from '@tanstack/react-query' +import { BigNumber } from 'bignumber.js' import { useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -94,14 +95,14 @@ export const VestingPaymentCard = ({ owner: vestingContractAddress, token, // Unused. We just want the USD price and staking info. - unstakedBalance: 0, + unstakedBalance: '0', }) ), { usdUnitPrice: undefined, stakingInfo: undefined, // Unused. We just want the USD price and staking info. - totalBalance: 0, + totalBalance: BigNumber(0), } ) diff --git a/packages/stateful/components/vesting/VestingStakingModal.tsx b/packages/stateful/components/vesting/VestingStakingModal.tsx index eaf9cadc0..28ad6be22 100644 --- a/packages/stateful/components/vesting/VestingStakingModal.tsx +++ b/packages/stateful/components/vesting/VestingStakingModal.tsx @@ -1,4 +1,5 @@ import { useQueryClient } from '@tanstack/react-query' +import { BigNumber } from 'bignumber.js' import { useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -22,7 +23,6 @@ import { import { ActionKey, TokenStake, VestingInfo } from '@dao-dao/types' import { convertDenomToMicroDenomStringWithDecimals, - convertMicroDenomToDenomWithDecimals, getDaoProposalSinglePrefill, getNativeTokenForChainId, processError, @@ -300,10 +300,7 @@ export const VestingStakingModal = ({ loading={loading} loadingStakableTokens={{ loading: false, - data: convertMicroDenomToDenomWithDecimals( - stakable, - nativeToken.decimals - ), + data: BigNumber(stakable), }} onAction={onAction} setAmount={setAmount} diff --git a/packages/stateful/components/wallet/WalletStakingModal.tsx b/packages/stateful/components/wallet/WalletStakingModal.tsx index 006a06fd3..3b032564d 100644 --- a/packages/stateful/components/wallet/WalletStakingModal.tsx +++ b/packages/stateful/components/wallet/WalletStakingModal.tsx @@ -1,5 +1,6 @@ import { coin } from '@cosmjs/stargate' import { useQueryClient } from '@tanstack/react-query' +import BigNumber from 'bignumber.js' import { useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -20,7 +21,6 @@ import { cwMsgToEncodeObject } from '@dao-dao/types' import { CHAIN_GAS_MULTIPLIER, convertDenomToMicroDenomStringWithDecimals, - convertMicroDenomToDenomWithDecimals, processError, } from '@dao-dao/utils' @@ -61,10 +61,9 @@ export const WalletStakingModal = (props: WalletStakingModalProps) => { address: walletAddress, }) : undefined, - 0, + BigNumber(0), { - transform: ({ amount }) => - convertMicroDenomToDenomWithDecimals(amount, nativeToken.decimals), + transform: ({ amount }) => BigNumber(amount), } ) @@ -94,14 +93,8 @@ export const WalletStakingModal = (props: WalletStakingModalProps) => { delegations.map(({ validator, delegated, pendingReward }) => ({ token: nativeToken, validator, - amount: convertMicroDenomToDenomWithDecimals( - delegated.amount, - nativeToken.decimals - ), - rewards: convertMicroDenomToDenomWithDecimals( - pendingReward.amount, - nativeToken.decimals - ), + amount: BigNumber(delegated.amount), + rewards: BigNumber(pendingReward.amount), })) ) const stakes = diff --git a/packages/stateful/components/wallet/WalletTokenCard.tsx b/packages/stateful/components/wallet/WalletTokenCard.tsx index 1a1df9234..62ccaa12b 100644 --- a/packages/stateful/components/wallet/WalletTokenCard.tsx +++ b/packages/stateful/components/wallet/WalletTokenCard.tsx @@ -71,7 +71,7 @@ export const WalletTokenCard = (props: TokenCardInfo) => { tokenCardLazyInfoSelector({ owner: props.owner.address, token: props.token, - unstakedBalance: props.unstakedBalance, + unstakedBalance: props.unstakedBalance.toString(), }), { usdUnitPrice: undefined, diff --git a/packages/stateful/components/wallet/WalletTokenCardReadonly.tsx b/packages/stateful/components/wallet/WalletTokenCardReadonly.tsx index 3821dcb67..ca9308982 100644 --- a/packages/stateful/components/wallet/WalletTokenCardReadonly.tsx +++ b/packages/stateful/components/wallet/WalletTokenCardReadonly.tsx @@ -14,7 +14,7 @@ export const WalletTokenCardReadonly = (props: TokenCardInfo) => { tokenCardLazyInfoSelector({ owner: props.owner.address, token: props.token, - unstakedBalance: props.unstakedBalance, + unstakedBalance: props.unstakedBalance.toString(), }), { usdUnitPrice: undefined, diff --git a/packages/stateful/components/wallet/WalletTokenLine.tsx b/packages/stateful/components/wallet/WalletTokenLine.tsx index af666a652..45cf6ac2e 100644 --- a/packages/stateful/components/wallet/WalletTokenLine.tsx +++ b/packages/stateful/components/wallet/WalletTokenLine.tsx @@ -15,7 +15,7 @@ export const WalletTokenLine = ( tokenCardLazyInfoSelector({ owner: props.owner.address, token: props.token, - unstakedBalance: props.unstakedBalance, + unstakedBalance: props.unstakedBalance.toString(), }), { usdUnitPrice: undefined, diff --git a/packages/stateful/components/wallet/WalletTokenLineReadonly.tsx b/packages/stateful/components/wallet/WalletTokenLineReadonly.tsx index d24cc30a7..8ca6fe38c 100644 --- a/packages/stateful/components/wallet/WalletTokenLineReadonly.tsx +++ b/packages/stateful/components/wallet/WalletTokenLineReadonly.tsx @@ -15,7 +15,7 @@ export const WalletTokenLineReadonly = ( tokenCardLazyInfoSelector({ owner: props.owner.address, token: props.token, - unstakedBalance: props.unstakedBalance, + unstakedBalance: props.unstakedBalance.toString(), }), { usdUnitPrice: undefined, diff --git a/packages/stateful/creators/TokenBased/GovernanceConfigurationInput.tsx b/packages/stateful/creators/TokenBased/GovernanceConfigurationInput.tsx index 4e1b0009e..0cdfa008b 100644 --- a/packages/stateful/creators/TokenBased/GovernanceConfigurationInput.tsx +++ b/packages/stateful/creators/TokenBased/GovernanceConfigurationInput.tsx @@ -1,4 +1,5 @@ import { Add } from '@mui/icons-material' +import { BigNumber } from 'bignumber.js' import clsx from 'clsx' import cloneDeep from 'lodash.clonedeep' import { useCallback, useEffect, useRef, useState } from 'react' @@ -277,7 +278,7 @@ export const GovernanceConfigurationInput = ({ const existingTokenSupply = existingGovernanceTokenSupply.state === 'hasValue' ? typeof existingGovernanceTokenSupply.contents === 'number' - ? BigInt(existingGovernanceTokenSupply.contents).toString() + ? BigNumber(existingGovernanceTokenSupply.contents).toString() : existingGovernanceTokenSupply.contents?.total_supply : undefined setValue('creator.data.existingTokenSupply', existingTokenSupply) diff --git a/packages/stateful/package.json b/packages/stateful/package.json index 701927edd..39637b426 100644 --- a/packages/stateful/package.json +++ b/packages/stateful/package.json @@ -49,6 +49,7 @@ "@mui/material": "^5.10.3", "@tanstack/react-query": "^5.40.0", "@walletconnect/browser-utils": "^1.8.0", + "bignumber.js": "^9.1.2", "buffer": "^6.0.3", "chain-registry": "^1.59.4", "clsx": "^1.1.1", diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/hooks/makeUsePublishProposal.ts b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/hooks/makeUsePublishProposal.ts index 818ccf033..0aed76431 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/hooks/makeUsePublishProposal.ts +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/hooks/makeUsePublishProposal.ts @@ -1,4 +1,5 @@ import { coins } from '@cosmjs/stargate' +import { BigNumber } from 'bignumber.js' import { useCallback, useEffect, useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -60,13 +61,15 @@ export const makeUsePublishProposal = 'native' in depositInfo.contents.denom ? depositInfo.contents.denom.native : undefined - const requiredProposalDeposit = Number( + const requiredProposalDeposit = BigNumber( depositInfo.valueMaybe()?.amount ?? '0' ) // For checking allowance and increasing if necessary. const cw20DepositTokenAllowanceResponseLoadable = useCachedLoadable( - depositInfoCw20TokenAddress && requiredProposalDeposit && walletAddress + depositInfoCw20TokenAddress && + requiredProposalDeposit.isPositive() && + walletAddress ? Cw20BaseSelectors.allowanceSelector({ chainId, contractAddress: depositInfoCw20TokenAddress, @@ -87,7 +90,9 @@ export const makeUsePublishProposal = : undefined const cw20DepositTokenBalanceLoadable = useCachedLoadable( - requiredProposalDeposit && walletAddress && depositInfoCw20TokenAddress + requiredProposalDeposit.isPositive() && + walletAddress && + depositInfoCw20TokenAddress ? Cw20BaseSelectors.balanceSelector({ chainId, contractAddress: depositInfoCw20TokenAddress, @@ -101,7 +106,9 @@ export const makeUsePublishProposal = : undefined const nativeDepositTokenBalanceLoadable = useCachedLoadable( - requiredProposalDeposit && walletAddress && depositInfoNativeTokenDenom + requiredProposalDeposit.isPositive() && + walletAddress && + depositInfoNativeTokenDenom ? nativeDenomBalanceSelector({ chainId, walletAddress, @@ -117,16 +124,15 @@ export const makeUsePublishProposal = // True if deposit is needed and cannot be paid. const depositUnsatisfied = // Requires deposit. - requiredProposalDeposit > 0 && + requiredProposalDeposit.isPositive() && // Has cw20 deposit and insufficient balance. ((!!depositInfoCw20TokenAddress && (!cw20DepositTokenBalance || - Number(cw20DepositTokenBalance.balance) < requiredProposalDeposit)) || + requiredProposalDeposit.gt(cw20DepositTokenBalance.balance))) || // Has native deposit and insufficient balance. (!!depositInfoNativeTokenDenom && (!nativeDepositTokenBalance || - Number(nativeDepositTokenBalance.amount) < - requiredProposalDeposit))) + requiredProposalDeposit.gt(nativeDepositTokenBalance.amount)))) const increaseCw20DepositAllowance = Cw20BaseHooks.useIncreaseAllowance({ contractAddress: depositInfoCw20TokenAddress ?? '', @@ -256,22 +262,22 @@ export const makeUsePublishProposal = throw new Error(t('error.loadingData')) } - const remainingAllowanceNeeded = - requiredProposalDeposit - + const remainingAllowanceNeeded = requiredProposalDeposit.minus( // If allowance expired, none. - (expirationExpired( + expirationExpired( cw20DepositTokenAllowanceResponse.expires, (await (await getSigningClient()).getBlock()).header.height ) ? 0 - : Number(cw20DepositTokenAllowanceResponse.allowance)) + : cw20DepositTokenAllowanceResponse.allowance + ) // Request to increase the contract's allowance for the proposal // deposit if needed. if (remainingAllowanceNeeded) { try { await increaseCw20DepositAllowance({ - amount: BigInt(remainingAllowanceNeeded).toString(), + amount: remainingAllowanceNeeded.toString(), spender: // If pre-propose address set, give that one deposit allowance // instead of proposal module. @@ -296,7 +302,7 @@ export const makeUsePublishProposal = const proposeFunds = requiredProposalDeposit && depositInfoNativeTokenDenom ? coins( - BigInt(requiredProposalDeposit).toString(), + requiredProposalDeposit.toString(), depositInfoNativeTokenDenom ) : undefined diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/hooks/makeUsePublishProposal.ts b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/hooks/makeUsePublishProposal.ts index 2f3d470f3..5a4f5a48b 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/hooks/makeUsePublishProposal.ts +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/hooks/makeUsePublishProposal.ts @@ -1,4 +1,5 @@ import { coins } from '@cosmjs/stargate' +import { BigNumber } from 'bignumber.js' import { useCallback, useEffect, useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -59,13 +60,15 @@ export const makeUsePublishProposal = 'native' in depositInfo.contents.denom ? depositInfo.contents.denom.native : undefined - const requiredProposalDeposit = Number( + const requiredProposalDeposit = BigNumber( depositInfo.valueMaybe()?.amount ?? '0' ) // For checking allowance and increasing if necessary. const cw20DepositTokenAllowanceResponseLoadable = useCachedLoadable( - depositInfoCw20TokenAddress && requiredProposalDeposit && walletAddress + depositInfoCw20TokenAddress && + requiredProposalDeposit.isPositive() && + walletAddress ? Cw20BaseSelectors.allowanceSelector({ chainId, contractAddress: depositInfoCw20TokenAddress, @@ -86,7 +89,9 @@ export const makeUsePublishProposal = : undefined const cw20DepositTokenBalanceLoadable = useCachedLoadable( - requiredProposalDeposit && walletAddress && depositInfoCw20TokenAddress + requiredProposalDeposit.isPositive() && + walletAddress && + depositInfoCw20TokenAddress ? Cw20BaseSelectors.balanceSelector({ chainId, contractAddress: depositInfoCw20TokenAddress, @@ -100,7 +105,9 @@ export const makeUsePublishProposal = : undefined const nativeDepositTokenBalanceLoadable = useCachedLoadable( - requiredProposalDeposit && walletAddress && depositInfoNativeTokenDenom + requiredProposalDeposit.isPositive() && + walletAddress && + depositInfoNativeTokenDenom ? nativeDenomBalanceSelector({ chainId, walletAddress, @@ -116,16 +123,15 @@ export const makeUsePublishProposal = // True if deposit is needed and cannot be paid. const depositUnsatisfied = // Requires deposit. - requiredProposalDeposit > 0 && + requiredProposalDeposit.isPositive() && // Has cw20 deposit and insufficient balance. ((!!depositInfoCw20TokenAddress && (!cw20DepositTokenBalance || - Number(cw20DepositTokenBalance.balance) < requiredProposalDeposit)) || + requiredProposalDeposit.gt(cw20DepositTokenBalance.balance))) || // Has native deposit and insufficient balance. (!!depositInfoNativeTokenDenom && (!nativeDepositTokenBalance || - Number(nativeDepositTokenBalance.amount) < - requiredProposalDeposit))) + requiredProposalDeposit.gt(nativeDepositTokenBalance.amount)))) const increaseCw20DepositAllowance = Cw20BaseHooks.useIncreaseAllowance({ contractAddress: depositInfoCw20TokenAddress ?? '', @@ -227,22 +233,22 @@ export const makeUsePublishProposal = throw new Error(t('error.loadingData')) } - const remainingAllowanceNeeded = - requiredProposalDeposit - + const remainingAllowanceNeeded = requiredProposalDeposit.minus( // If allowance expired, none. - (expirationExpired( + expirationExpired( cw20DepositTokenAllowanceResponse.expires, (await (await getSigningClient()).getBlock()).header.height ) ? 0 - : Number(cw20DepositTokenAllowanceResponse.allowance)) + : cw20DepositTokenAllowanceResponse.allowance + ) // Request to increase the contract's allowance for the proposal // deposit if needed. if (remainingAllowanceNeeded) { try { await increaseCw20DepositAllowance({ - amount: BigInt(remainingAllowanceNeeded).toString(), + amount: remainingAllowanceNeeded.toString(), spender: // If pre-propose address set, give that one deposit allowance // instead of proposal module. @@ -267,7 +273,7 @@ export const makeUsePublishProposal = const proposeFunds = requiredProposalDeposit && depositInfoNativeTokenDenom ? coins( - BigInt(requiredProposalDeposit).toString(), + requiredProposalDeposit.toString(), depositInfoNativeTokenDenom ) : undefined diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx index e2b90a8f6..329c326ed 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx @@ -1,3 +1,4 @@ +import { BigNumber } from 'bignumber.js' import { useCallback, useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -179,10 +180,7 @@ export const ProfileCardMemberInfo = ({ ...(claimsPending ?? []).map(({ amount, release_at }) => ({ token: governanceToken, status: UnstakingTaskStatus.Unstaking, - amount: convertMicroDenomToDenomWithDecimals( - amount, - governanceToken.decimals - ), + amount: BigNumber(amount), date: blocksPerYearLoadable.state === 'hasValue' ? convertExpirationToDate( @@ -197,10 +195,7 @@ export const ProfileCardMemberInfo = ({ ...(claimsAvailable ?? []).map(({ amount, release_at }) => ({ token: governanceToken, status: UnstakingTaskStatus.ReadyToClaim, - amount: convertMicroDenomToDenomWithDecimals( - amount, - governanceToken.decimals - ), + amount: BigNumber(amount), date: blocksPerYearLoadable.state === 'hasValue' ? convertExpirationToDate( diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx index f3fd63e06..8db5179d3 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx @@ -1,3 +1,4 @@ +import { BigNumber } from 'bignumber.js' import { useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -352,10 +353,7 @@ const InnerStakingModal = ({ ? { loading: true } : { loading: false, - data: convertMicroDenomToDenomWithDecimals( - loadingUnstakedBalance.data, - governanceToken.decimals - ), + data: BigNumber(loadingUnstakedBalance.data), } } loadingUnstakableTokens={ @@ -363,10 +361,7 @@ const InnerStakingModal = ({ ? { loading: true } : { loading: false, - data: convertMicroDenomToDenomWithDecimals( - loadingWalletStakedValue.data, - governanceToken.decimals - ), + data: BigNumber(loadingWalletStakedValue.data), } } onAction={onAction} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx index d07003b2a..7693d6cea 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx @@ -1,3 +1,4 @@ +import { BigNumber } from 'bignumber.js' import { useCallback, useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -136,7 +137,7 @@ export const ProfileCardMemberInfo = ({ ...(claimsPending ?? []).map(({ release_at }) => ({ token, status: UnstakingTaskStatus.Unstaking, - amount: Number(1), + amount: BigNumber(1), date: convertExpirationToDate( blocksPerYear, release_at, @@ -148,7 +149,7 @@ export const ProfileCardMemberInfo = ({ ...(claimsAvailable ?? []).map(({ release_at }) => ({ token, status: UnstakingTaskStatus.ReadyToClaim, - amount: Number(1), + amount: BigNumber(1), date: convertExpirationToDate( blocksPerYear, release_at, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/ProfileCardMemberInfo.tsx index 434234449..f9d0997e9 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/ProfileCardMemberInfo.tsx @@ -1,3 +1,4 @@ +import { BigNumber } from 'bignumber.js' import { useCallback, useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -137,10 +138,7 @@ export const ProfileCardMemberInfo = ({ ...(claimsPending ?? []).map(({ amount, release_at }) => ({ token: governanceToken, status: UnstakingTaskStatus.Unstaking, - amount: convertMicroDenomToDenomWithDecimals( - amount, - governanceToken.decimals - ), + amount: BigNumber(amount), date: convertExpirationToDate( blocksPerYear, release_at, @@ -152,10 +150,7 @@ export const ProfileCardMemberInfo = ({ ...(claimsAvailable ?? []).map(({ amount, release_at }) => ({ token: governanceToken, status: UnstakingTaskStatus.ReadyToClaim, - amount: convertMicroDenomToDenomWithDecimals( - amount, - governanceToken.decimals - ), + amount: BigNumber(amount), date: convertExpirationToDate( blocksPerYear, release_at, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx index 96d7386a6..9c0d287fa 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx @@ -1,4 +1,5 @@ import { coins } from '@cosmjs/stargate' +import { BigNumber } from 'bignumber.js' import { useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -237,10 +238,7 @@ const InnerStakingModal = ({ ? { loading: true } : { loading: false, - data: convertMicroDenomToDenomWithDecimals( - loadingUnstakedBalance.data, - governanceToken.decimals - ), + data: BigNumber(loadingUnstakedBalance.data), } } loadingUnstakableTokens={ @@ -248,10 +246,7 @@ const InnerStakingModal = ({ ? { loading: true } : { loading: false, - data: convertMicroDenomToDenomWithDecimals( - loadingWalletStakedValue.data, - governanceToken.decimals - ), + data: BigNumber(loadingWalletStakedValue.data), } } onAction={onAction} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx index 42db582b0..9c3bf4885 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx @@ -1,3 +1,4 @@ +import { BigNumber } from 'bignumber.js' import { useCallback, useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -138,7 +139,7 @@ export const ProfileCardMemberInfo = ({ ...(claimsPending ?? []).map(({ release_at }) => ({ token, status: UnstakingTaskStatus.Unstaking, - amount: Number(1), + amount: BigNumber(1), date: convertExpirationToDate( blocksPerYear, release_at, @@ -150,7 +151,7 @@ export const ProfileCardMemberInfo = ({ ...(claimsAvailable ?? []).map(({ release_at }) => ({ token, status: UnstakingTaskStatus.ReadyToClaim, - amount: Number(1), + amount: BigNumber(1), date: convertExpirationToDate( blocksPerYear, release_at, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx index b787ce088..7efcec019 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx @@ -1,3 +1,4 @@ +import { BigNumber } from 'bignumber.js' import { useCallback, useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -137,10 +138,7 @@ export const ProfileCardMemberInfo = ({ ...(claimsPending ?? []).map(({ amount, release_at }) => ({ token: governanceToken, status: UnstakingTaskStatus.Unstaking, - amount: convertMicroDenomToDenomWithDecimals( - amount, - governanceToken.decimals - ), + amount: BigNumber(amount), date: convertExpirationToDate( blocksPerYear, release_at, @@ -152,10 +150,7 @@ export const ProfileCardMemberInfo = ({ ...(claimsAvailable ?? []).map(({ amount, release_at }) => ({ token: governanceToken, status: UnstakingTaskStatus.ReadyToClaim, - amount: convertMicroDenomToDenomWithDecimals( - amount, - governanceToken.decimals - ), + amount: BigNumber(amount), date: convertExpirationToDate( blocksPerYear, release_at, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx index d1bf20420..1d8352351 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx @@ -1,4 +1,5 @@ import { coins } from '@cosmjs/stargate' +import { BigNumber } from 'bignumber.js' import { useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -237,10 +238,7 @@ const InnerStakingModal = ({ ? { loading: true } : { loading: false, - data: convertMicroDenomToDenomWithDecimals( - loadingUnstakedBalance.data, - governanceToken.decimals - ), + data: BigNumber(loadingUnstakedBalance.data), } } loadingUnstakableTokens={ @@ -248,10 +246,7 @@ const InnerStakingModal = ({ ? { loading: true } : { loading: false, - data: convertMicroDenomToDenomWithDecimals( - loadingWalletStakedValue.data, - governanceToken.decimals - ), + data: BigNumber(loadingWalletStakedValue.data), } } onAction={onAction} diff --git a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx index 15003379b..ce318686f 100644 --- a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx @@ -1,5 +1,6 @@ import { coins } from '@cosmjs/stargate' import { useQueries } from '@tanstack/react-query' +import { BigNumber } from 'bignumber.js' import { useCallback, useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -23,7 +24,6 @@ import { BaseStakingModalProps, TokenInputOption } from '@dao-dao/types' import { CHAIN_GAS_MULTIPLIER, convertDenomToMicroDenomStringWithDecimals, - convertMicroDenomToDenomWithDecimals, makeCombineQueryResultsIntoLoadingDataWithError, processError, tokensEqual, @@ -262,10 +262,7 @@ const InnerStakingModal = ({ ? { loading: true } : { loading: false, - data: convertMicroDenomToDenomWithDecimals( - selectedVaultUnstakedTokens, - selectedVault.bondToken.decimals - ), + data: BigNumber(selectedVaultUnstakedTokens), } } loadingUnstakableTokens={ @@ -275,10 +272,7 @@ const InnerStakingModal = ({ ? { loading: true } : { loading: false, - data: convertMicroDenomToDenomWithDecimals( - selectedVaultStakedTokens, - selectedVault.bondToken.decimals - ), + data: BigNumber(selectedVaultStakedTokens), } } onAction={onAction} diff --git a/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx b/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx index 6467ae193..0c21d8b47 100644 --- a/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx +++ b/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx @@ -1,3 +1,4 @@ +import { BigNumber } from 'bignumber.js' import clsx from 'clsx' import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -11,6 +12,7 @@ import { UnstakingTaskStatus, } from '@dao-dao/types' import { + convertMicroDenomToDenomWithDecimals, formatPercentOf100, humanReadableList, secondsToWdhms, @@ -57,22 +59,28 @@ export const ProfileCardMemberInfoTokens = ({ () => unstakingTasks.reduce( (acc, task) => - acc + - // Only include balance of ready to claim tasks. - (task.status === UnstakingTaskStatus.ReadyToClaim ? task.amount : 0), - 0 - ) ?? 0, + acc.plus( + // Only include balance of ready to claim tasks. + task.status === UnstakingTaskStatus.ReadyToClaim + ? task.amount + : BigNumber(0) + ), + BigNumber(0) + ), [unstakingTasks] ) const totalUnstakingBalance = useMemo( () => unstakingTasks.reduce( (acc, task) => - acc + - // Only include balance of unstaking tasks. - (task.status === UnstakingTaskStatus.Unstaking ? task.amount : 0), - 0 - ) ?? 0, + acc.plus( + // Only include balance of unstaking tasks. + task.status === UnstakingTaskStatus.Unstaking + ? task.amount + : BigNumber(0) + ), + BigNumber(0) + ), [unstakingTasks] ) const unstakingBalanceByToken = useMemo( @@ -80,11 +88,15 @@ export const ProfileCardMemberInfoTokens = ({ unstakingTasks.reduce( (acc, task) => ({ ...acc, - [task.token.denomOrAddress]: - (acc[task.token.denomOrAddress] || 0) + - (task.status === UnstakingTaskStatus.Unstaking ? task.amount : 0), + [task.token.denomOrAddress]: ( + acc[task.token.denomOrAddress] || BigNumber(0) + ).plus( + task.status === UnstakingTaskStatus.Unstaking + ? task.amount + : BigNumber(0) + ), }), - {} as Partial> + {} as Partial> ), [unstakingTasks] ) @@ -223,18 +235,22 @@ export const ProfileCardMemberInfoTokens = ({ {/* Show unstaking balance if any are unstaking or claimable or if they are a member. */} {!hideUnstaking && - (isMember || totalUnstakingBalance > 0 || claimableBalance > 0) && ( + (isMember || + totalUnstakingBalance.isPositive() || + claimableBalance.isPositive()) && (

{t('title.unstakingTokens')}

- {claimableBalance > 0 && ( + {claimableBalance.isPositive() && ( 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 87f85e88a..6ea73617d 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/NewAttribute.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/NewAttribute.tsx @@ -2,6 +2,7 @@ import { Add, Close } from '@mui/icons-material' import { useFieldArray, useFormContext } from 'react-hook-form' import { useTranslation } from 'react-i18next' +import { HugeDecimal } from '@dao-dao/math' import { Button, IconButton, @@ -12,10 +13,7 @@ import { useChainContext, } from '@dao-dao/stateless' import { GenericToken } from '@dao-dao/types' -import { - convertMicroDenomToDenomWithDecimals, - validateRequired, -} from '@dao-dao/utils' +import { validateRequired } from '@dao-dao/utils' import { NewSurveyFormData } from '../../types' @@ -131,8 +129,7 @@ export const NewAttribute = ({ error: errors?.attributes?.[attributeIndex]?.tokens?.[tokenIndex] ?.amount, - step: convertMicroDenomToDenomWithDecimals( - 1, + step: HugeDecimal.one.toHumanReadableNumber( selectedToken?.decimals ?? 0 ), }} diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Complete.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Complete.tsx index 48d7dfd50..c35165b21 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Complete.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Complete.tsx @@ -11,6 +11,7 @@ import { import { useForm } from 'react-hook-form' import { useTranslation } from 'react-i18next' +import { HugeDecimal } from '@dao-dao/math' import { Button, CosmosMessageDisplay, @@ -33,11 +34,7 @@ import { StatefulProposalListProps, } from '@dao-dao/types' import { Boolean } from '@dao-dao/types/contracts/DaoVotingCw721Staked' -import { - convertMicroDenomToDenomWithDecimals, - decodedMessagesString, - validateRequired, -} from '@dao-dao/utils' +import { decodedMessagesString, validateRequired } from '@dao-dao/utils' import { NewProposalData } from '../../../../../../../../proposal-module-adapter/adapters/DaoProposalSingle/types' import { CompleteRatings, SurveyWithMetadata } from '../../../../types' @@ -461,8 +458,7 @@ export const InnerComplete = ({ ...acc, [denomOrAddress]: (acc[denomOrAddress] ?? 0) + - convertMicroDenomToDenomWithDecimals( - amount, + HugeDecimal.from(amount).toHumanReadableNumber( tokenMap[denomOrAddress]?.token.decimals ?? 0 ), }), diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Rate.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Rate.tsx index f12b35bbc..2ef4e4a39 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Rate.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Rate.tsx @@ -3,6 +3,7 @@ import { ComponentType, useEffect, useMemo } from 'react' import { FormProvider, useForm } from 'react-hook-form' import { useTranslation } from 'react-i18next' +import { HugeDecimal } from '@dao-dao/math' import { Button, Checkbox, @@ -21,7 +22,6 @@ import { TransProps, } from '@dao-dao/types' import { - convertMicroDenomToDenomWithDecimals, formatDateTimeTz, makeValidateAddress, transformIpfsUrlToHttpsIfNecessary, @@ -284,8 +284,7 @@ export const Rate = ({ ...acc, [denomOrAddress]: (acc[denomOrAddress] ?? 0) + - convertMicroDenomToDenomWithDecimals( - amount, + HugeDecimal.from(amount).toHumanReadableNumber( tokenMap[denomOrAddress]?.token.decimals ?? 0 ), }), diff --git a/packages/stateless/components/PayEntityDisplay.tsx b/packages/stateless/components/PayEntityDisplay.tsx index 957912b5f..8b6138fea 100644 --- a/packages/stateless/components/PayEntityDisplay.tsx +++ b/packages/stateless/components/PayEntityDisplay.tsx @@ -4,8 +4,8 @@ import { } from '@mui/icons-material' import clsx from 'clsx' +import { HugeDecimal } from '@dao-dao/math' import { PayEntityDisplayProps, PayEntityDisplayRowProps } from '@dao-dao/types' -import { convertMicroDenomToDenomWithDecimals } from '@dao-dao/utils' import { useDetectWrap } from '../hooks' import { TokenAmountDisplay } from './token' @@ -41,7 +41,7 @@ const PayEntityDisplayRow = ({ > ( !isNaN(highestOffer.amount) && highestOffer.offerToken && ( {/* leading-5 to match link-text's line-height. */} {/* leading-5 to match link-text's line-height. */} ( amount={ lazyInfo.loading ? { loading: true } - : convertMicroDenomToDenomWithDecimals( - lazyInfo.data.totalBalance, - token.decimals - ) + : HugeDecimal.from( + lazyInfo.data.totalBalance + ).toHumanReadableNumber(token.decimals) } className="body-text truncate text-right font-mono" decimals={token.decimals} @@ -119,10 +118,10 @@ export const TokenLine = ( amount={ lazyInfo.loading || !lazyInfo.data.usdUnitPrice?.usdPrice ? { loading: true } - : convertMicroDenomToDenomWithDecimals( - lazyInfo.data.totalBalance, - token.decimals - ) * lazyInfo.data.usdUnitPrice.usdPrice + : HugeDecimal.from( + lazyInfo.data.totalBalance + ).toHumanReadableNumber(token.decimals) * + lazyInfo.data.usdUnitPrice.usdPrice } className="caption-text font-mono" dateFetched={ diff --git a/packages/stateless/components/token/UnstakingLine.tsx b/packages/stateless/components/token/UnstakingLine.tsx index 7696f3170..08c0c59cc 100644 --- a/packages/stateless/components/token/UnstakingLine.tsx +++ b/packages/stateless/components/token/UnstakingLine.tsx @@ -2,12 +2,9 @@ import clsx from 'clsx' import { ReactNode } from 'react' import TimeAgo from 'react-timeago' +import { HugeDecimal } from '@dao-dao/math' import { UnstakingTask, UnstakingTaskStatus } from '@dao-dao/types' -import { - convertMicroDenomToDenomWithDecimals, - formatDate, - formatDateTimeTz, -} from '@dao-dao/utils' +import { formatDate, formatDateTimeTz } from '@dao-dao/utils' import { useTranslatedTimeDeltaFormatter } from '../../hooks' import { Tooltip } from '../tooltip' @@ -51,7 +48,9 @@ export const UnstakingLine = ({
/

HugeDecimal.from(amount).toHumanReadableNumber(decimals) - // TODO(huge): replace with HugeDecimal export const convertDenomToMicroDenomWithDecimals = ( amount: HugeDecimal.Value, diff --git a/packages/utils/dao.ts b/packages/utils/dao.ts index 51c31a5bf..0e01e4ae7 100644 --- a/packages/utils/dao.ts +++ b/packages/utils/dao.ts @@ -1,5 +1,6 @@ import { TFunction } from 'react-i18next' +import { HugeDecimal } from '@dao-dao/math' import { Account, AccountType, @@ -14,10 +15,7 @@ import { import { InstantiateMsg as DaoDaoCoreInstantiateMsg } from '@dao-dao/types/contracts/DaoDaoCore' import { getSupportedChainConfig } from './chain' -import { - convertDurationToHumanReadableString, - convertMicroDenomToDenomWithDecimals, -} from './conversion' +import { convertDurationToHumanReadableString } from './conversion' export const getParentDaoBreadcrumbs = ( getDaoPath: (coreAddress: string) => string, @@ -199,12 +197,13 @@ export const getHumanReadableRewardDistributionLabel = ( : 'paused' in distribution.active_epoch.emission_rate ? t('title.paused') : t('info.amountEveryDuration', { - amount: convertMicroDenomToDenomWithDecimals( - distribution.active_epoch.emission_rate.linear.amount, - distribution.token.decimals - ).toLocaleString(undefined, { - maximumFractionDigits: distribution.token.decimals, - }), + amount: HugeDecimal.from( + distribution.active_epoch.emission_rate.linear.amount + ) + .toHumanReadableNumber(distribution.token.decimals) + .toLocaleString(undefined, { + maximumFractionDigits: distribution.token.decimals, + }), duration: convertDurationToHumanReadableString( t, distribution.active_epoch.emission_rate.linear.duration diff --git a/packages/utils/token.ts b/packages/utils/token.ts index df4e36be1..76707283d 100644 --- a/packages/utils/token.ts +++ b/packages/utils/token.ts @@ -1,5 +1,6 @@ import cloneDeep from 'lodash.clonedeep' +import { HugeDecimal } from '@dao-dao/math' import { GenericToken, GenericTokenSource, @@ -10,7 +11,6 @@ import { } from '@dao-dao/types' import { getChainForChainName, getIbcTransferInfoFromChannel } from './chain' -import { convertMicroDenomToDenomWithDecimals } from './conversion' import { objectMatchesStructure } from './objectMatchesStructure' export const tokensEqual = ( @@ -133,17 +133,15 @@ export const sortTokensValueDescending: SortFn< const aPrice = a.lazyInfo.loading || !a.lazyInfo.data.usdUnitPrice?.usdPrice ? undefined - : convertMicroDenomToDenomWithDecimals( - a.lazyInfo.data.totalBalance, - a.token.decimals - ) * a.lazyInfo.data.usdUnitPrice.usdPrice + : HugeDecimal.from(a.lazyInfo.data.totalBalance) + .times(a.lazyInfo.data.usdUnitPrice.usdPrice) + .toHumanReadableNumber(a.token.decimals) const bPrice = b.lazyInfo.loading || !b.lazyInfo.data.usdUnitPrice?.usdPrice ? undefined - : convertMicroDenomToDenomWithDecimals( - b.lazyInfo.data.totalBalance, - b.token.decimals - ) * b.lazyInfo.data.usdUnitPrice.usdPrice + : HugeDecimal.from(b.lazyInfo.data.totalBalance) + .times(b.lazyInfo.data.usdUnitPrice.usdPrice) + .toHumanReadableNumber(b.token.decimals) // If prices are equal, sort alphabetically by symbol. return aPrice === bPrice From c98f80dcca94157e6d2dbe55c3a7eca47930115a Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Fri, 27 Sep 2024 12:34:46 -0400 Subject: [PATCH 05/26] replaced remaining conversion utils with HugeDecimal --- packages/math/HugeDecimal.ts | 8 ++++-- .../core/actions/AuthzGrantRevoke/index.tsx | 5 ++-- .../actions/CommunityPoolDeposit/index.tsx | 6 +---- .../actions/ConfigureRebalancer/index.tsx | 9 +++---- .../CreateRewardDistribution/index.tsx | 9 +++---- .../actions/CreateValenceAccount/index.tsx | 3 +-- .../actions/core/actions/Execute/index.tsx | 10 +++---- .../actions/FundRewardDistribution/index.tsx | 5 ++-- .../core/actions/Instantiate/index.tsx | 3 +-- .../core/actions/Instantiate2/index.tsx | 3 +-- .../core/actions/ManageStaking/index.tsx | 5 ++-- .../actions/core/actions/Spend/index.tsx | 7 +++-- .../UpdateRewardDistribution/index.tsx | 5 ++-- .../token_swap/PerformTokenSwap/index.tsx | 6 ++--- .../stateful/InstantiateTokenSwap.tsx | 15 +++++------ .../components/dao/DaoTokenDepositModal.tsx | 10 +++---- .../components/dao/DaoTxTreasuryHistory.tsx | 1 + .../gov/GovProposalStatusAndInfo.tsx | 5 ++-- .../vesting/VestingStakingModal.tsx | 13 +++++---- .../components/wallet/WalletStakingModal.tsx | 10 +++---- .../creators/TokenBased/getInstantiateInfo.ts | 5 ++-- .../actions/UpdatePreProposeConfig/index.tsx | 5 ++-- .../daoCreation/getInstantiateInfo.ts | 10 +++---- .../actions/UpdatePreProposeConfig/index.tsx | 5 ++-- .../actions/UpdateProposalConfigV1/index.tsx | 5 ++-- .../daoCreation/getInstantiateInfo.ts | 10 +++---- .../actions/Mint/index.tsx | 5 ++-- .../components/StakingModal.tsx | 27 +++++++++---------- .../hooks/useMainDaoInfoCards.tsx | 9 ++++--- .../actions/Mint/index.tsx | 9 +++---- .../components/StakingModal.tsx | 14 ++++------ .../actions/Mint/BitSongFantokenMintAction.ts | 9 +++---- .../actions/Mint/MintAction.ts | 5 ++-- .../components/StakingModal.tsx | 14 ++++------ .../components/StakingModal.tsx | 9 +++---- .../stateful/pages/CreateSurvey.tsx | 10 +++---- .../components/inputs/NumberInput.tsx | 12 +++------ packages/utils/conversion.ts | 13 --------- 38 files changed, 125 insertions(+), 189 deletions(-) diff --git a/packages/math/HugeDecimal.ts b/packages/math/HugeDecimal.ts index 55134bdab..5dd98a1cd 100644 --- a/packages/math/HugeDecimal.ts +++ b/packages/math/HugeDecimal.ts @@ -45,7 +45,7 @@ export class HugeDecimal { /** * Create a HugeDecimal from a value that is already in its base/raw format, * which means it does not have decimals and describes an amount of some base - * unit. For example: `1000000 untrn`. + * unit. For example: `1000000untrn`. * * @param n the value * @returns a HugeDecimal instance @@ -62,7 +62,7 @@ export class HugeDecimal { /** * Create a HugeDecimal from a value that is already in human-readable format, * which means it has decimals and describes a human-readable token amount. - * For example: `1.000000 NTRN`. + * For example: `1.000000 $NTRN`. * * @param n the value * @param decimals the number of decimals @@ -155,6 +155,10 @@ export class HugeDecimal { return new HugeDecimal(this.value.pow(valueToBigNumber(n))) } + abs() { + return new HugeDecimal(this.value.abs()) + } + /** * Returns a human-readable number with `decimals` decimal places. * diff --git a/packages/stateful/actions/core/actions/AuthzGrantRevoke/index.tsx b/packages/stateful/actions/core/actions/AuthzGrantRevoke/index.tsx index dbca51b13..3067e327d 100644 --- a/packages/stateful/actions/core/actions/AuthzGrantRevoke/index.tsx +++ b/packages/stateful/actions/core/actions/AuthzGrantRevoke/index.tsx @@ -42,7 +42,6 @@ import { } from '@dao-dao/types/protobuf/codegen/cosmwasm/wasm/v1/authz' import { Any } from '@dao-dao/types/protobuf/codegen/google/protobuf/any' import { - convertDenomToMicroDenomStringWithDecimals, getChainAddressForActionOptions, isDecodedStargateMsg, maybeMakePolytoneExecuteMessages, @@ -163,7 +162,7 @@ export class AuthzGrantRevokeAction extends ActionBase { // MaxFundsLimit // CombinedLimit amounts: funds.map(({ denom, amount, decimals }) => ({ - amount: convertDenomToMicroDenomStringWithDecimals(amount, decimals), + amount: HugeDecimal.fromHumanReadable(amount, decimals).toString(), denom, })), }) @@ -177,7 +176,7 @@ export class AuthzGrantRevokeAction extends ActionBase { msg: msgTypeUrl, // SendAuthorization spendLimit: funds.map(({ denom, amount, decimals }) => ({ - amount: convertDenomToMicroDenomStringWithDecimals(amount, decimals), + amount: HugeDecimal.fromHumanReadable(amount, decimals).toString(), denom, })), allowList: [], diff --git a/packages/stateful/actions/core/actions/CommunityPoolDeposit/index.tsx b/packages/stateful/actions/core/actions/CommunityPoolDeposit/index.tsx index 8321a4a9b..2d92e34cd 100644 --- a/packages/stateful/actions/core/actions/CommunityPoolDeposit/index.tsx +++ b/packages/stateful/actions/core/actions/CommunityPoolDeposit/index.tsx @@ -19,7 +19,6 @@ import { } from '@dao-dao/types/actions' import { MsgFundCommunityPool } from '@dao-dao/types/protobuf/codegen/cosmos/distribution/v1beta1/tx' import { - convertDenomToMicroDenomStringWithDecimals, getChainAddressForActionOptions, isDecodedStargateMsg, maybeMakePolytoneExecuteMessages, @@ -117,10 +116,7 @@ export class CommunityPoolDepositAction extends ActionBase = ( tokens.map( (token): GenericTokenBalance => ({ token, - balance: convertDenomToMicroDenomStringWithDecimals( + balance: HugeDecimal.fromHumanReadable( existingCreateValenceAccountActionData?.funds .find(({ denom }) => denom === token.denomOrAddress) ?.amount?.toString() || 0, token.decimals - ), + ).toString(), }) ), }), @@ -488,13 +487,13 @@ export class ConfigureRebalancerAction extends ActionBase d.denomOrAddress === denom )?.decimals ?? 0 - ) + ).toString() : undefined, // BPS bps: percent * 100, diff --git a/packages/stateful/actions/core/actions/CreateRewardDistribution/index.tsx b/packages/stateful/actions/core/actions/CreateRewardDistribution/index.tsx index 73b7bed4e..803879911 100644 --- a/packages/stateful/actions/core/actions/CreateRewardDistribution/index.tsx +++ b/packages/stateful/actions/core/actions/CreateRewardDistribution/index.tsx @@ -26,7 +26,6 @@ import { InstantiateMsg, } from '@dao-dao/types/contracts/DaoRewardsDistributor' import { - convertDenomToMicroDenomStringWithDecimals, convertDurationToDurationWithUnits, convertDurationWithUnitsToDuration, encodeJsonToBase64, @@ -244,10 +243,10 @@ export class CreateRewardDistributionAction extends ActionBase ({ denom, - amount: convertDenomToMicroDenomStringWithDecimals(amount, decimals), + amount: HugeDecimal.fromHumanReadable(amount, decimals).toString(), })) // Add service fee to funds. diff --git a/packages/stateful/actions/core/actions/Execute/index.tsx b/packages/stateful/actions/core/actions/Execute/index.tsx index 5c3d238f7..a65b2914a 100644 --- a/packages/stateful/actions/core/actions/Execute/index.tsx +++ b/packages/stateful/actions/core/actions/Execute/index.tsx @@ -24,7 +24,6 @@ import { import { MsgExecuteContract as SecretMsgExecuteContract } from '@dao-dao/types/protobuf/codegen/secret/compute/v1beta1/msg' import { bech32DataToAddress, - convertDenomToMicroDenomStringWithDecimals, decodeJsonFromBase64, encodeJsonToBase64, getAccountAddress, @@ -188,10 +187,10 @@ export class ExecuteAction extends ActionBase { contractAddress: funds[0].denom, msg: { send: { - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( funds[0].amount, funds[0].decimals - ), + ).toString(), [isSecret ? 'recipient' : 'contract']: address, msg: encodeJsonToBase64(msg), ...(isSecret && { @@ -209,10 +208,7 @@ export class ExecuteAction extends ActionBase { funds: funds .map(({ denom, amount, decimals }) => ({ denom, - amount: convertDenomToMicroDenomStringWithDecimals( - amount, - decimals - ), + amount: HugeDecimal.fromHumanReadable(amount, decimals).toString(), })) // Neutron errors with `invalid coins` if the funds list is not // alphabetized. diff --git a/packages/stateful/actions/core/actions/FundRewardDistribution/index.tsx b/packages/stateful/actions/core/actions/FundRewardDistribution/index.tsx index ced3c3177..5ad0e9c5a 100644 --- a/packages/stateful/actions/core/actions/FundRewardDistribution/index.tsx +++ b/packages/stateful/actions/core/actions/FundRewardDistribution/index.tsx @@ -23,7 +23,6 @@ import { ProcessedMessage, } from '@dao-dao/types/actions' import { - convertDenomToMicroDenomStringWithDecimals, encodeJsonToBase64, getDaoRewardDistributors, makeExecuteSmartContractMessage, @@ -150,10 +149,10 @@ export class FundRewardDistributionAction extends ActionBase { const convertedFunds = funds .map(({ denom, amount, decimals }) => ({ denom, - amount: convertDenomToMicroDenomStringWithDecimals(amount, decimals), + amount: HugeDecimal.fromHumanReadable(amount, decimals).toString(), })) // Neutron errors with `invalid coins` if the funds list is not // alphabetized. diff --git a/packages/stateful/actions/core/actions/Instantiate2/index.tsx b/packages/stateful/actions/core/actions/Instantiate2/index.tsx index f7e75eb28..b0a843538 100644 --- a/packages/stateful/actions/core/actions/Instantiate2/index.tsx +++ b/packages/stateful/actions/core/actions/Instantiate2/index.tsx @@ -30,7 +30,6 @@ import { } from '@dao-dao/types/actions' import { MsgInstantiateContract2 } from '@dao-dao/types/protobuf/codegen/cosmwasm/wasm/v1/tx' import { - convertDenomToMicroDenomStringWithDecimals, decodeJsonFromBase64, getAccountAddress, isDecodedStargateMsg, @@ -206,7 +205,7 @@ export class Instantiate2Action extends ActionBase { const convertedFunds = funds .map(({ denom, amount, decimals }) => ({ denom, - amount: convertDenomToMicroDenomStringWithDecimals(amount, decimals), + amount: HugeDecimal.fromHumanReadable(amount, decimals).toString(), })) // Neutron errors with `invalid coins` if the funds list is not // alphabetized. diff --git a/packages/stateful/actions/core/actions/ManageStaking/index.tsx b/packages/stateful/actions/core/actions/ManageStaking/index.tsx index f1c4aa083..6d03adb1a 100644 --- a/packages/stateful/actions/core/actions/ManageStaking/index.tsx +++ b/packages/stateful/actions/core/actions/ManageStaking/index.tsx @@ -47,7 +47,6 @@ import { } from '@dao-dao/types/protobuf/codegen/cosmos/staking/v1beta1/tx' import { StakingActionType, - convertDenomToMicroDenomStringWithDecimals, getChainAddressForActionOptions, getNativeTokenForChainId, isDecodedStargateMsg, @@ -335,10 +334,10 @@ export class ManageStakingAction extends ActionBase { ) const nativeToken = getNativeTokenForChainId(chainId) const amount = coin( - convertDenomToMicroDenomStringWithDecimals( + HugeDecimal.fromHumanReadable( macroAmount, nativeToken.decimals - ), + ).toString(), nativeToken.denomOrAddress ) diff --git a/packages/stateful/actions/core/actions/Spend/index.tsx b/packages/stateful/actions/core/actions/Spend/index.tsx index bf8e2bfed..72943b287 100644 --- a/packages/stateful/actions/core/actions/Spend/index.tsx +++ b/packages/stateful/actions/core/actions/Spend/index.tsx @@ -50,7 +50,6 @@ import { MsgTransfer } from '@dao-dao/types/protobuf/codegen/ibc/applications/tr import { MsgTransfer as NeutronMsgTransfer } from '@dao-dao/types/protobuf/codegen/neutron/transfer/v1/tx' import { MAINNET, - convertDenomToMicroDenomStringWithDecimals, convertDurationWithUnitsToSeconds, decodeMessage, getAccountAddress, @@ -254,10 +253,10 @@ const StatefulSpendComponent: ComponentType< const amountIn = selectedToken && amount - ? convertDenomToMicroDenomStringWithDecimals( + ? HugeDecimal.fromHumanReadable( amount, selectedToken.token.decimals - ) + ).toString() : undefined // Get Skip route for IBC transfer. @@ -592,7 +591,7 @@ export class SpendAction extends ActionBase { type: cw20 ? TokenType.Cw20 : TokenType.Native, }) ) - const amount = convertDenomToMicroDenomStringWithDecimals(_amount, decimals) + const amount = HugeDecimal.fromHumanReadable(_amount, decimals).toString() // Gov module community pool spend. if (this.options.context.type === ActionContextType.Gov) { diff --git a/packages/stateful/actions/core/actions/UpdateRewardDistribution/index.tsx b/packages/stateful/actions/core/actions/UpdateRewardDistribution/index.tsx index 6f6cee22e..11486c9e9 100644 --- a/packages/stateful/actions/core/actions/UpdateRewardDistribution/index.tsx +++ b/packages/stateful/actions/core/actions/UpdateRewardDistribution/index.tsx @@ -16,7 +16,6 @@ import { ProcessedMessage, } from '@dao-dao/types/actions' import { - convertDenomToMicroDenomStringWithDecimals, convertDurationToDurationWithUnits, convertDurationWithUnitsToDuration, getDaoRewardDistributors, @@ -144,10 +143,10 @@ export class UpdateRewardDistributionAction extends ActionBase { } // Convert amount to micro amount. - const amount = convertDenomToMicroDenomStringWithDecimals( + const amount = HugeDecimal.fromHumanReadable( selfParty.amount, selfParty.decimals - ) + ).toString() return selfParty.type === 'cw20' ? makeWasmMessage({ diff --git a/packages/stateful/actions/core/actions/token_swap/stateful/InstantiateTokenSwap.tsx b/packages/stateful/actions/core/actions/token_swap/stateful/InstantiateTokenSwap.tsx index 0c8791754..0ac84a77a 100644 --- a/packages/stateful/actions/core/actions/token_swap/stateful/InstantiateTokenSwap.tsx +++ b/packages/stateful/actions/core/actions/token_swap/stateful/InstantiateTokenSwap.tsx @@ -4,6 +4,7 @@ import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' import { constSelector, useRecoilValueLoadable } from 'recoil' +import { HugeDecimal } from '@dao-dao/math' import { genericTokenBalancesSelector } from '@dao-dao/state' import { DaoDaoCoreSelectors } from '@dao-dao/state/recoil' import { Loader, useActionOptions, useCachedLoading } from '@dao-dao/stateless' @@ -15,8 +16,6 @@ import { } from '@dao-dao/types' import { InstantiateMsg } from '@dao-dao/types/contracts/CwTokenSwap' import { - convertDenomToMicroDenomStringWithDecimals, - convertDenomToMicroDenomWithDecimals, getNativeTokenForChainId, instantiateSmartContract, isValidBech32Address, @@ -78,19 +77,19 @@ export const InstantiateTokenSwap: ActionComponent< ? { cw20: { contract_addr: selfParty.denomOrAddress, - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( selfParty.amount, selfParty.decimals - ), + ).toString(), }, } : { native: { denom: selfParty.denomOrAddress, - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( selfParty.amount, selfParty.decimals - ), + ).toString(), }, }, }, @@ -101,7 +100,7 @@ export const InstantiateTokenSwap: ActionComponent< ? { cw20: { contract_addr: counterparty.denomOrAddress, - amount: convertDenomToMicroDenomWithDecimals( + amount: HugeDecimal.fromHumanReadable( counterparty.amount, counterparty.decimals ).toString(), @@ -110,7 +109,7 @@ export const InstantiateTokenSwap: ActionComponent< : { native: { denom: counterparty.denomOrAddress, - amount: convertDenomToMicroDenomWithDecimals( + amount: HugeDecimal.fromHumanReadable( counterparty.amount, counterparty.decimals ).toString(), diff --git a/packages/stateful/components/dao/DaoTokenDepositModal.tsx b/packages/stateful/components/dao/DaoTokenDepositModal.tsx index 4a1fbda9e..785011f1b 100644 --- a/packages/stateful/components/dao/DaoTokenDepositModal.tsx +++ b/packages/stateful/components/dao/DaoTokenDepositModal.tsx @@ -18,11 +18,7 @@ import { useDaoInfoContext, } from '@dao-dao/stateless' import { Account } from '@dao-dao/types' -import { - CHAIN_GAS_MULTIPLIER, - convertDenomToMicroDenomStringWithDecimals, - processError, -} from '@dao-dao/utils' +import { CHAIN_GAS_MULTIPLIER, processError } from '@dao-dao/utils' import { Cw20BaseHooks, useWallet } from '../../hooks' import { ConnectWallet } from '../ConnectWallet' @@ -105,10 +101,10 @@ export const DaoTokenDepositModal = ({ setLoading(true) try { - const microAmount = convertDenomToMicroDenomStringWithDecimals( + const microAmount = HugeDecimal.fromHumanReadable( amount, token.decimals - ) + ).toString() if (token.type === 'native') { const signingClient = await getSigningStargateClient() diff --git a/packages/stateful/components/dao/DaoTxTreasuryHistory.tsx b/packages/stateful/components/dao/DaoTxTreasuryHistory.tsx index a3c090af5..167c7b082 100644 --- a/packages/stateful/components/dao/DaoTxTreasuryHistory.tsx +++ b/packages/stateful/components/dao/DaoTxTreasuryHistory.tsx @@ -4,6 +4,7 @@ import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useRecoilCallback, useRecoilValue } from 'recoil' +import { HugeDecimal } from '@dao-dao/math' import { TransformedTreasuryTransaction, blockHeightSelector, diff --git a/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx b/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx index a1d6e9dac..e60fac1df 100644 --- a/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx +++ b/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx @@ -36,7 +36,6 @@ import { ProposalStatus } from '@dao-dao/types/protobuf/codegen/cosmos/gov/v1bet import { MsgDeposit } from '@dao-dao/types/protobuf/codegen/cosmos/gov/v1beta1/tx' import { CHAIN_GAS_MULTIPLIER, - convertDenomToMicroDenomStringWithDecimals, formatPercentOf100, getDisplayNameForChainId, processError, @@ -235,10 +234,10 @@ const InnerGovProposalStatusAndInfo = ({ proposalId, depositor: walletAddress, amount: coins( - convertDenomToMicroDenomStringWithDecimals( + HugeDecimal.fromHumanReadable( depositValue, depositToken.decimals - ), + ).toString(), depositToken.denomOrAddress ), }, diff --git a/packages/stateful/components/vesting/VestingStakingModal.tsx b/packages/stateful/components/vesting/VestingStakingModal.tsx index b1487626c..3880d1ae8 100644 --- a/packages/stateful/components/vesting/VestingStakingModal.tsx +++ b/packages/stateful/components/vesting/VestingStakingModal.tsx @@ -26,7 +26,6 @@ import { VestingInfo, } from '@dao-dao/types' import { - convertDenomToMicroDenomStringWithDecimals, getDaoProposalSinglePrefill, getNativeTokenForChainId, processError, @@ -118,10 +117,10 @@ export const VestingStakingModal = ({ try { if (mode === StakingMode.Stake) { const data = { - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( amount, nativeToken.decimals - ), + ).toString(), validator, } @@ -153,10 +152,10 @@ export const VestingStakingModal = ({ } } else if (mode === StakingMode.Unstake) { const data = { - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( amount, nativeToken.decimals - ), + ).toString(), validator, } @@ -192,10 +191,10 @@ export const VestingStakingModal = ({ return } - const convertedAmount = convertDenomToMicroDenomStringWithDecimals( + const convertedAmount = HugeDecimal.fromHumanReadable( amount, nativeToken.decimals - ) + ).toString() if (recipientIsDao) { await goToDaoProposal(recipient, 'create', { prefill: getDaoProposalSinglePrefill({ diff --git a/packages/stateful/components/wallet/WalletStakingModal.tsx b/packages/stateful/components/wallet/WalletStakingModal.tsx index b5fc51f2f..7495ec0d5 100644 --- a/packages/stateful/components/wallet/WalletStakingModal.tsx +++ b/packages/stateful/components/wallet/WalletStakingModal.tsx @@ -20,11 +20,7 @@ import { StakingMode, cwMsgToEncodeObject, } from '@dao-dao/types' -import { - CHAIN_GAS_MULTIPLIER, - convertDenomToMicroDenomStringWithDecimals, - processError, -} from '@dao-dao/utils' +import { CHAIN_GAS_MULTIPLIER, processError } from '@dao-dao/utils' import { useAwaitNextBlock, @@ -121,10 +117,10 @@ export const WalletStakingModal = (props: WalletStakingModalProps) => { try { const signingClient = await getSigningStargateClient() - const microAmount = convertDenomToMicroDenomStringWithDecimals( + const microAmount = HugeDecimal.fromHumanReadable( amount, nativeToken.decimals - ) + ).toString() if (mode === StakingMode.Stake) { await signingClient.signAndBroadcast( diff --git a/packages/stateful/creators/TokenBased/getInstantiateInfo.ts b/packages/stateful/creators/TokenBased/getInstantiateInfo.ts index 821fceb0d..08bb47d8e 100644 --- a/packages/stateful/creators/TokenBased/getInstantiateInfo.ts +++ b/packages/stateful/creators/TokenBased/getInstantiateInfo.ts @@ -8,7 +8,6 @@ import { ExecuteMsg as BtsgFtFactoryExecuteMsg } from '@dao-dao/types/contracts/ import { InitialBalance } from '@dao-dao/types/contracts/DaoVotingTokenStaked' import { NEW_DAO_TOKEN_DECIMALS, - convertDenomToMicroDenomStringWithDecimals, convertDurationWithUnitsToDuration, isSecretNetwork, } from '@dao-dao/utils' @@ -125,10 +124,10 @@ export const getInstantiateInfo: DaoCreatorGetInstantiateInfo = ({ issue: { symbol: symbol.toLowerCase(), name, - max_supply: convertDenomToMicroDenomStringWithDecimals( + max_supply: HugeDecimal.fromHumanReadable( maxSupply, NEW_DAO_TOKEN_DECIMALS - ), + ).toString(), uri: metadataUrl || '', initial_balances: microInitialBalances, initial_dao_balance: microInitialTreasuryBalance, diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/index.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/index.tsx index 5d8e06873..dd0c6e329 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/index.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/index.tsx @@ -30,7 +30,6 @@ import { } from '@dao-dao/types/contracts/DaoPreProposeMultiple' import { DAO_PRE_PROPOSE_MULTIPLE_CONTRACT_NAMES, - convertDenomToMicroDenomStringWithDecimals, getNativeTokenForChainId, isFeatureSupportedByVersion, isValidBech32Address, @@ -241,10 +240,10 @@ export class DaoProposalMultipleUpdatePreProposeConfigAction extends ActionBase< update_config: { deposit_info: depositRequired ? { - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( depositInfo.amount, depositInfo.token?.decimals ?? 0 - ), + ).toString(), denom: depositInfo.type === 'voting_module_token' ? { diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/daoCreation/getInstantiateInfo.ts b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/daoCreation/getInstantiateInfo.ts index d2076bf35..3acef33a5 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/daoCreation/getInstantiateInfo.ts +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/daoCreation/getInstantiateInfo.ts @@ -1,7 +1,7 @@ +import { HugeDecimal } from '@dao-dao/math' import { DaoCreationGetInstantiateInfo, TokenType } from '@dao-dao/types' import { TokenBasedCreatorId, - convertDenomToMicroDenomStringWithDecimals, convertDurationWithUnitsToDuration, convertVetoConfigToCosmos, isSecretNetwork, @@ -60,10 +60,10 @@ export const getInstantiateInfo: DaoCreationGetInstantiateInfo< ...commonConfig, deposit: proposalDeposit.enabled ? { - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( proposalDeposit.amount, proposalDeposit.token?.decimals ?? 0 - ), + ).toString(), denom: proposalDeposit.type === 'voting_module_token' ? { @@ -103,10 +103,10 @@ export const getInstantiateInfo: DaoCreationGetInstantiateInfo< ...commonConfig, deposit: proposalDeposit.enabled ? { - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( proposalDeposit.amount, proposalDeposit.token?.decimals ?? 0 - ), + ).toString(), denom: proposalDeposit.type === 'voting_module_token' ? { diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/index.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/index.tsx index 465232e28..2d433fad8 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/index.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/index.tsx @@ -31,7 +31,6 @@ import { import { ContractName, DAO_PRE_PROPOSE_SINGLE_CONTRACT_NAMES, - convertDenomToMicroDenomStringWithDecimals, getNativeTokenForChainId, isFeatureSupportedByVersion, isValidBech32Address, @@ -250,10 +249,10 @@ export class DaoProposalSingleUpdatePreProposeConfigAction extends ActionBase { contractAddress: this.governanceToken.denomOrAddress, msg: { mint: { - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( amount, this.governanceToken.decimals - ), + ).toString(), recipient: to, }, }, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx index 2abebd780..00cc982dd 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx @@ -22,12 +22,7 @@ import { useCachedLoadable, } from '@dao-dao/stateless' import { BaseStakingModalProps, StakingMode } from '@dao-dao/types' -import { - convertDenomToMicroDenomStringWithDecimals, - convertDenomToMicroDenomWithDecimals, - encodeJsonToBase64, - processError, -} from '@dao-dao/utils' +import { encodeJsonToBase64, processError } from '@dao-dao/utils' import { SuspenseLoader } from '../../../../components' import { @@ -128,7 +123,7 @@ const InnerStakingModal = ({ const walletStakedBalance = walletStakedBalanceLoadable.state === 'hasValue' && walletStakedBalanceLoadable.contents - ? Number(walletStakedBalanceLoadable.contents.balance) + ? walletStakedBalanceLoadable.contents.balance : undefined const [amount, setAmount] = useState(0) @@ -174,10 +169,10 @@ const InnerStakingModal = ({ try { await doCw20SendAndExecute({ - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( amount, governanceToken.decimals - ), + ).toString(), contract: stakingContractToExecute, msg: encodeJsonToBase64({ [isOraichainCustomStaking ? 'bond' : 'stake']: {}, @@ -232,13 +227,15 @@ const InnerStakingModal = ({ // down, so division and multiplication don't commute. Handle the common // case here where someone is attempting to unstake all of their funds. if ( - Math.abs( - walletStakedBalance - - convertDenomToMicroDenomWithDecimals( + HugeDecimal.from(walletStakedBalance) + .minus( + HugeDecimal.fromHumanReadable( amountToUnstake, governanceToken.decimals ) - ) <= 1 + ) + .abs() + .lte(1) ) { amountToUnstake = HugeDecimal.from( walletStakedBalance @@ -246,10 +243,10 @@ const InnerStakingModal = ({ } try { - const convertedAmount = convertDenomToMicroDenomStringWithDecimals( + const convertedAmount = HugeDecimal.fromHumanReadable( amountToUnstake, governanceToken.decimals - ) + ).toString() if (isOraichainCustomStaking) { await doOraichainUnbond({ amount: convertedAmount, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useMainDaoInfoCards.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useMainDaoInfoCards.tsx index 6428a6385..224abb4e4 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useMainDaoInfoCards.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useMainDaoInfoCards.tsx @@ -1,11 +1,11 @@ import { useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' +import { HugeDecimal } from '@dao-dao/math' import { indexerQueries } from '@dao-dao/state' import { TokenAmountDisplay } from '@dao-dao/stateless' import { DaoInfoCard } from '@dao-dao/types' import { - convertDenomToMicroDenomWithDecimals, convertDurationToHumanReadableString, formatPercentOf100, isSecretNetwork, @@ -77,9 +77,10 @@ export const useMainDaoInfoCards = (): DaoInfoCard[] => { totalVotingWeight === undefined ? undefined : formatPercentOf100( - (totalVotingWeight / - convertDenomToMicroDenomWithDecimals(supply, decimals)) * - 100 + HugeDecimal.from(totalVotingWeight) + .div(HugeDecimal.fromHumanReadable(supply, decimals)) + .times(100) + .toNumber() ), }, { diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/index.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/index.tsx index 5f0636a64..b8c36499b 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/index.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/index.tsx @@ -16,10 +16,7 @@ import { ProcessedMessage, } from '@dao-dao/types/actions' import { MsgMint } from '@dao-dao/types/protobuf/codegen/osmosis/tokenfactory/v1beta1/tx' -import { - convertDenomToMicroDenomStringWithDecimals, - isDecodedStargateMsg, -} from '@dao-dao/utils' +import { isDecodedStargateMsg } from '@dao-dao/utils' import { useGovernanceTokenInfo } from '../../hooks' import { @@ -83,10 +80,10 @@ export class MintAction extends ActionBase { value: MsgMint.fromPartial({ sender: this.options.address, amount: coin( - convertDenomToMicroDenomStringWithDecimals( + HugeDecimal.fromHumanReadable( amount, this.governanceToken.decimals - ), + ).toString(), this.governanceToken.denomOrAddress ), }), diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx index 346f5d6c7..86a693680 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx @@ -15,11 +15,7 @@ import { StakingModal as StatelessStakingModal, } from '@dao-dao/stateless' import { BaseStakingModalProps, StakingMode } from '@dao-dao/types' -import { - CHAIN_GAS_MULTIPLIER, - convertDenomToMicroDenomStringWithDecimals, - processError, -} from '@dao-dao/utils' +import { CHAIN_GAS_MULTIPLIER, processError } from '@dao-dao/utils' import { SuspenseLoader } from '../../../../components' import { @@ -111,10 +107,10 @@ const InnerStakingModal = ({ CHAIN_GAS_MULTIPLIER, undefined, coins( - convertDenomToMicroDenomStringWithDecimals( + HugeDecimal.fromHumanReadable( amount, governanceToken.decimals - ), + ).toString(), governanceToken.denomOrAddress ) ) @@ -149,10 +145,10 @@ const InnerStakingModal = ({ try { await doUnstake({ - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( amount, governanceToken.decimals - ), + ).toString(), }) // New balances will not appear until the next block. diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/BitSongFantokenMintAction.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/BitSongFantokenMintAction.ts index dc9b1c3b1..0d951cff7 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/BitSongFantokenMintAction.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/BitSongFantokenMintAction.ts @@ -16,10 +16,7 @@ import { ProcessedMessage, } from '@dao-dao/types/actions' import { MsgMint } from '@dao-dao/types/protobuf/codegen/bitsong/fantoken/v1beta1/tx' -import { - convertDenomToMicroDenomStringWithDecimals, - isDecodedStargateMsg, -} from '@dao-dao/utils' +import { isDecodedStargateMsg } from '@dao-dao/utils' import { Component } from './Component' import { MintData } from './MintComponent' @@ -80,10 +77,10 @@ export class BitSongFantokenMintAction extends ActionBase { throw new Error('Action not ready') } - const amount = convertDenomToMicroDenomStringWithDecimals( + const amount = HugeDecimal.fromHumanReadable( _amount, this.governanceToken.decimals - ) + ).toString() return makeStargateMessage({ stargate: { diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintAction.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintAction.ts index 61d6e3e6b..81f70cfab 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintAction.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintAction.ts @@ -10,7 +10,6 @@ import { ProcessedMessage, } from '@dao-dao/types/actions' import { - convertDenomToMicroDenomStringWithDecimals, makeExecuteSmartContractMessage, objectMatchesStructure, } from '@dao-dao/utils' @@ -78,10 +77,10 @@ export class MintAction extends ActionBase { throw new Error('Action not ready') } - const amount = convertDenomToMicroDenomStringWithDecimals( + const amount = HugeDecimal.fromHumanReadable( _amount, this.governanceToken.decimals - ) + ).toString() return [ // Set DAO minter allowance to the amount we're about to mint. diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx index 147efddb0..ed9bbd9b3 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx @@ -15,11 +15,7 @@ import { StakingModal as StatelessStakingModal, } from '@dao-dao/stateless' import { BaseStakingModalProps, StakingMode } from '@dao-dao/types' -import { - CHAIN_GAS_MULTIPLIER, - convertDenomToMicroDenomStringWithDecimals, - processError, -} from '@dao-dao/utils' +import { CHAIN_GAS_MULTIPLIER, processError } from '@dao-dao/utils' import { SuspenseLoader } from '../../../../components' import { @@ -111,10 +107,10 @@ const InnerStakingModal = ({ CHAIN_GAS_MULTIPLIER, undefined, coins( - convertDenomToMicroDenomStringWithDecimals( + HugeDecimal.fromHumanReadable( amount, governanceToken.decimals - ), + ).toString(), governanceToken.denomOrAddress ) ) @@ -149,10 +145,10 @@ const InnerStakingModal = ({ try { await doUnstake({ - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( amount, governanceToken.decimals - ), + ).toString(), }) // New balances will not appear until the next block. diff --git a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx index ad180968b..b2023a84f 100644 --- a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx @@ -26,7 +26,6 @@ import { } from '@dao-dao/types' import { CHAIN_GAS_MULTIPLIER, - convertDenomToMicroDenomStringWithDecimals, makeCombineQueryResultsIntoLoadingDataWithError, processError, tokensEqual, @@ -170,10 +169,10 @@ const InnerStakingModal = ({ CHAIN_GAS_MULTIPLIER, undefined, coins( - convertDenomToMicroDenomStringWithDecimals( + HugeDecimal.fromHumanReadable( amount, selectedVault.bondToken.decimals - ), + ).toString(), selectedVault.bondToken.denomOrAddress ) ) @@ -208,10 +207,10 @@ const InnerStakingModal = ({ try { await doUnstake({ - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( amount, selectedVault.bondToken.decimals - ), + ).toString(), }) // New balances will not appear until the next block. diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/CreateSurvey.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/CreateSurvey.tsx index fc6786aa7..158d747fd 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/CreateSurvey.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/CreateSurvey.tsx @@ -4,6 +4,7 @@ import { useCallback, useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' +import { HugeDecimal } from '@dao-dao/math' import { genericTokenBalancesSelector } from '@dao-dao/state' import { Loader, @@ -13,7 +14,6 @@ import { useDaoNavHelpers, } from '@dao-dao/stateless' import { TokenType, WidgetId } from '@dao-dao/types' -import { convertDenomToMicroDenomStringWithDecimals } from '@dao-dao/utils' import { SuspenseLoader } from '../../../../../../../components' import { useCw20CommonGovernanceTokenInfoIfExists } from '../../../../../../../voting-module-adapter/react/hooks/useCw20CommonGovernanceTokenInfoIfExists' @@ -107,18 +107,18 @@ export const CreateSurvey = () => { if (cw20Decimals !== undefined) { cw20Tokens.push({ address: denomOrAddress, - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( amount, cw20Decimals - ), + ).toString(), }) } else if (nativeDecimals !== undefined) { nativeTokens.push({ denom: denomOrAddress, - amount: convertDenomToMicroDenomStringWithDecimals( + amount: HugeDecimal.fromHumanReadable( amount, nativeDecimals - ), + ).toString(), }) } else { // Should never happen, but just in case. diff --git a/packages/stateless/components/inputs/NumberInput.tsx b/packages/stateless/components/inputs/NumberInput.tsx index 89525ffee..f2128bef4 100644 --- a/packages/stateless/components/inputs/NumberInput.tsx +++ b/packages/stateless/components/inputs/NumberInput.tsx @@ -5,10 +5,7 @@ import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { NumberInputProps } from '@dao-dao/types' -import { - convertDenomToMicroDenomWithDecimals, - toAccessibleImageUrl, -} from '@dao-dao/utils' +import { toAccessibleImageUrl } from '@dao-dao/utils' import { IconButton } from '../icon_buttons' @@ -174,13 +171,12 @@ export const NumberInput = < typeof value !== 'number' && (typeof value !== 'string' || value.trim() === '') ? NaN - : // On first load, setValueAs seems to be called with the first value, which is probably default loaded from a save. We - // don't want to transform this first value. + : // On first load, setValueAs seems to be called with the first value, which is probably default loaded from a save. We don't want to transform this first value. transformDecimals && value !== untransformedValue - ? convertDenomToMicroDenomWithDecimals( + ? HugeDecimal.fromHumanReadable( value, transformDecimals - ) + ).toNumber() : Number(value) return newValue diff --git a/packages/utils/conversion.ts b/packages/utils/conversion.ts index 624090248..13b0f58e4 100644 --- a/packages/utils/conversion.ts +++ b/packages/utils/conversion.ts @@ -3,7 +3,6 @@ import { UseQueryResult } from '@tanstack/react-query' import { TFunction } from 'next-i18next' import { Loadable } from 'recoil' -import { HugeDecimal } from '@dao-dao/math' import { CachedLoadable, Duration, @@ -17,18 +16,6 @@ import { Expiration } from '@dao-dao/types/contracts/common' import { getChainForChainId } from './chain' import { IPFS_GATEWAY_TEMPLATE, SITE_URL } from './constants' -// TODO(huge): replace with HugeDecimal -export const convertDenomToMicroDenomWithDecimals = ( - amount: HugeDecimal.Value, - decimals: number -) => HugeDecimal.fromHumanReadable(amount, decimals).toNumber() - -// TODO(huge): replace with HugeDecimal -export const convertDenomToMicroDenomStringWithDecimals = ( - amount: HugeDecimal.Value, - decimals: number -): string => HugeDecimal.fromHumanReadable(amount, decimals).toString() - export function convertFromMicroDenom(denom: string) { return denom?.substring(1).toUpperCase() } From adba1515702fc5aea72a54a4b000a8c6b810b6de Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Sat, 28 Sep 2024 15:21:49 -0400 Subject: [PATCH 06/26] accept HugeDecimal and auto-convert decimals in TokenAmountDisplay --- packages/math/HugeDecimal.ts | 14 +++++ .../contracts/DaoRewardsDistributor.extra.ts | 17 +++--- .../contracts/NeutronVotingRegistry.extra.ts | 21 ++++---- packages/state/recoil/selectors/chain.ts | 2 +- .../CreateRewardDistribution/Component.tsx | 8 ++- .../ManageStaking/Component.stories.tsx | 8 +-- .../core/actions/ManageStaking/Component.tsx | 12 ++--- .../core/actions/ManageStaking/index.tsx | 6 +-- .../actions/ManageVesting/CancelVesting.tsx | 5 +- .../actions/ManageVesting/RegisterSlash.tsx | 9 +--- .../actions/core/actions/Spend/Component.tsx | 8 ++- .../WithdrawRewardDistribution/Component.tsx | 14 ++--- .../WithdrawRewardDistribution/index.tsx | 3 +- .../components/SelfRelayExecuteModal.tsx | 25 +++------ .../components/TokenAmountDisplay.tsx | 25 ++++++--- .../dao/DaoRewardsDistributorClaimCard.tsx | 4 +- .../components/dao/DaoVotingVaultCard.tsx | 20 +++---- .../components/dao/MainDaoInfoCards.tsx | 9 +--- .../stateful/components/gov/GovInfoBar.tsx | 7 +-- .../hooks/useProposalDaoInfoCards.tsx | 4 +- .../hooks/useProposalDaoInfoCards.tsx | 4 +- .../components/StakingModal.tsx | 8 +-- .../components/StakingModal.tsx | 8 +-- .../hooks/useMainDaoInfoCards.tsx | 7 +-- .../components/StakingModal.tsx | 8 +-- .../hooks/useMainDaoInfoCards.tsx | 7 +-- .../hooks/useMainDaoInfoCards.tsx | 5 +- .../ProfileCardMemberInfoTokens.tsx | 9 ++-- .../stateless/components/PayEntityDisplay.tsx | 2 +- .../stateless/components/ValidatorPicker.tsx | 14 ++--- packages/stateless/components/dao/DaoCard.tsx | 5 +- .../components/dao/DaoMemberCard.tsx | 6 +-- .../components/modals/TokenDepositModal.tsx | 2 +- .../components/nft/NftCard.stories.tsx | 3 +- packages/stateless/components/nft/NftCard.tsx | 11 ++-- .../components/token/StakingModal.stories.tsx | 4 +- .../components/token/StakingModal.tsx | 48 ++++++++--------- .../components/token/TokenAmountDisplay.tsx | 12 ++++- .../components/token/TokenCard.stories.tsx | 8 +-- .../stateless/components/token/TokenCard.tsx | 52 +++++-------------- .../stateless/components/token/TokenLine.tsx | 15 ++---- .../components/token/UnstakingLine.tsx | 9 +--- .../components/vesting/VestingPaymentCard.tsx | 22 ++------ .../components/vesting/VestingPaymentLine.tsx | 17 ++---- packages/types/chain.ts | 12 +++-- packages/types/components/StakingModal.ts | 2 +- .../types/components/TokenAmountDisplay.ts | 10 +++- packages/types/dao.ts | 8 +-- packages/types/nft.ts | 4 +- packages/utils/chain.ts | 3 +- packages/utils/nft.ts | 3 +- 51 files changed, 222 insertions(+), 327 deletions(-) diff --git a/packages/math/HugeDecimal.ts b/packages/math/HugeDecimal.ts index 5dd98a1cd..d29f6bae0 100644 --- a/packages/math/HugeDecimal.ts +++ b/packages/math/HugeDecimal.ts @@ -201,4 +201,18 @@ export class HugeDecimal { toCoins(denom: string): Coin[] { return [this.toCoin(denom)] } + + /** + * Returns the USD value. + * + * @param decimals the number of decimals + * @param usdUnitPrice the USD unit price of the token (with decimals) + * @returns USD value + */ + toUsdValue(decimals: number, usdUnitPrice: BigNumber.Value) { + return this.value + .div(BigNumber(10).pow(decimals)) + .times(usdUnitPrice) + .toNumber() + } } diff --git a/packages/state/query/queries/contracts/DaoRewardsDistributor.extra.ts b/packages/state/query/queries/contracts/DaoRewardsDistributor.extra.ts index e971c8d8e..7feb09201 100644 --- a/packages/state/query/queries/contracts/DaoRewardsDistributor.extra.ts +++ b/packages/state/query/queries/contracts/DaoRewardsDistributor.extra.ts @@ -261,9 +261,9 @@ export const fetchPendingDaoRewards = async ( return distributions.map((distribution) => ({ distribution, - rewards: Number( + rewards: HugeDecimal.from( pending_rewards.find((pending) => pending.id === distribution.id) - ?.pending_rewards || '0' + ?.pending_rewards || 0 ), })) } @@ -301,15 +301,14 @@ export const fetchPendingDaoRewards = async ( // Sum all pending rewards for this token. const allPendingRewards = distributions.reduce( (acc, { distribution, rewards }) => - acc + (tokenSourcesEqual(token, distribution.token) ? rewards : 0), - 0 + acc.plus( + tokenSourcesEqual(token, distribution.token) ? rewards : 0 + ), + HugeDecimal.zero ) - const balance = HugeDecimal.from( - allPendingRewards - ).toHumanReadableNumber(token.decimals) - - const usdValue = balance * usdPrice + const balance = allPendingRewards.toHumanReadableNumber(token.decimals) + const usdValue = allPendingRewards.toUsdValue(token.decimals, usdPrice) return { token, diff --git a/packages/state/query/queries/contracts/NeutronVotingRegistry.extra.ts b/packages/state/query/queries/contracts/NeutronVotingRegistry.extra.ts index e7b45f98a..8a89d7774 100644 --- a/packages/state/query/queries/contracts/NeutronVotingRegistry.extra.ts +++ b/packages/state/query/queries/contracts/NeutronVotingRegistry.extra.ts @@ -1,5 +1,6 @@ import { QueryClient, queryOptions } from '@tanstack/react-query' +import { HugeDecimal } from '@dao-dao/math' import { VotingVaultWithInfo } from '@dao-dao/types' import { neutronVaultQueries } from './NeutronVault' @@ -38,15 +39,17 @@ export const fetchNeutronVaultsWithInfo = async ( address: vault.address, }) ), - totalPower: ( - await queryClient.fetchQuery( - neutronVaultQueries.totalPowerAtHeight({ - chainId, - contractAddress: vault.address, - args: {}, - }) - ) - ).power, + totalPower: HugeDecimal.from( + ( + await queryClient.fetchQuery( + neutronVaultQueries.totalPowerAtHeight({ + chainId, + contractAddress: vault.address, + args: {}, + }) + ) + ).power + ), }) ) ) diff --git a/packages/state/recoil/selectors/chain.ts b/packages/state/recoil/selectors/chain.ts index 2ecc0fc11..ebfdae11e 100644 --- a/packages/state/recoil/selectors/chain.ts +++ b/packages/state/recoil/selectors/chain.ts @@ -735,7 +735,7 @@ export const validatorsSelector = selectorFamily>({ return validators .map((validator) => cosmosValidatorToValidator(validator)) - .sort((a, b) => b.tokens - a.tokens) + .sort((a, b) => b.tokens.minus(a.tokens).toNumber()) }, }) diff --git a/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx index 91ab965bc..a65226761 100644 --- a/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx @@ -160,11 +160,9 @@ export const CreateRewardDistributionComponent: ActionComponent< description: t('title.balance') + ': ' + - HugeDecimal.from(balance) - .toHumanReadableNumber(token.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: token.decimals, - }), + HugeDecimal.from(balance).toHumanReadableString( + token.decimals + ), })), } } diff --git a/packages/stateful/actions/core/actions/ManageStaking/Component.stories.tsx b/packages/stateful/actions/core/actions/ManageStaking/Component.stories.tsx index 0224ac993..43e021f72 100644 --- a/packages/stateful/actions/core/actions/ManageStaking/Component.stories.tsx +++ b/packages/stateful/actions/core/actions/ManageStaking/Component.stories.tsx @@ -42,7 +42,7 @@ const stakes: TokenStake[] = [ details: '', commission: 0.05, status: 'BOND_STATUS_BONDED', - tokens: 5, + tokens: HugeDecimal.fromHumanReadable(5, token.decimals), }, rewards: HugeDecimal.fromHumanReadable(1.23, token.decimals), }, @@ -57,7 +57,7 @@ const stakes: TokenStake[] = [ details: '', commission: 0.05, status: 'BOND_STATUS_BONDED', - tokens: 6.2, + tokens: HugeDecimal.fromHumanReadable(6, token.decimals), }, rewards: HugeDecimal.fromHumanReadable(4.56, token.decimals), }, @@ -72,7 +72,7 @@ const stakes: TokenStake[] = [ details: '', commission: 0.05, status: 'BOND_STATUS_BONDED', - tokens: 7, + tokens: HugeDecimal.fromHumanReadable(7, token.decimals), }, rewards: HugeDecimal.fromHumanReadable(7.89, token.decimals), }, @@ -95,7 +95,7 @@ Default.args = { details: '', commission: 0.05, status: 'BOND_STATUS_BONDED', - tokens: 9, + tokens: HugeDecimal.fromHumanReadable(9, token.decimals), }, ], executed: false, diff --git a/packages/stateful/actions/core/actions/ManageStaking/Component.tsx b/packages/stateful/actions/core/actions/ManageStaking/Component.tsx index 823ea2604..e6a1d2672 100644 --- a/packages/stateful/actions/core/actions/ManageStaking/Component.tsx +++ b/packages/stateful/actions/core/actions/ManageStaking/Component.tsx @@ -54,17 +54,17 @@ export const getStakeActions = ( }, ] -export interface ManageStakingOptions { +export type ManageStakingOptions = { nativeBalance: string stakes: TokenStake[] validators: Validator[] executed: boolean - claimedRewards?: number + claimedRewards?: HugeDecimal nativeUnstakingDurationSeconds: number AddressInput: ComponentType> } -export interface ManageStakingData { +export type ManageStakingData = { chainId: string type: StakingActionType validator: string @@ -386,13 +386,13 @@ export const ManageStakingComponent: ActionComponent<

{ // rewards. If in wallet context, will be undefined. const executedTxLoadable = useExecutedProposalTxLoadable() - let claimedRewards: number | undefined + let claimedRewards: HugeDecimal | undefined if ( executedTxLoadable.state === 'hasValue' && executedTxLoadable.contents && @@ -190,9 +190,7 @@ const InnerComponent: ActionComponent = (props) => { )[0] if (coin) { - claimedRewards = HugeDecimal.from( - coin.amount ?? 0 - ).toHumanReadableNumber(nativeToken.decimals) + claimedRewards = HugeDecimal.from(coin) } } } diff --git a/packages/stateful/actions/core/actions/ManageVesting/CancelVesting.tsx b/packages/stateful/actions/core/actions/ManageVesting/CancelVesting.tsx index db4eafea5..30e1ba6fc 100644 --- a/packages/stateful/actions/core/actions/ManageVesting/CancelVesting.tsx +++ b/packages/stateful/actions/core/actions/ManageVesting/CancelVesting.tsx @@ -2,7 +2,6 @@ import { ComponentType } from 'react' import { useFormContext } from 'react-hook-form' import { useTranslation } from 'react-i18next' -import { HugeDecimal } from '@dao-dao/math' import { Button, ChainProvider, @@ -123,9 +122,7 @@ export const CancelVesting: ActionComponent = ({

= ({ description: t('title.balance') + ': ' + - HugeDecimal.from(balance) - .toHumanReadableNumber(token.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: token.decimals, - }), + HugeDecimal.from(balance).toHumanReadableString( + token.decimals + ), })), } } diff --git a/packages/stateful/actions/core/actions/WithdrawRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/WithdrawRewardDistribution/Component.tsx index 54d30066d..7d76d7799 100644 --- a/packages/stateful/actions/core/actions/WithdrawRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/WithdrawRewardDistribution/Component.tsx @@ -2,7 +2,6 @@ import { ArrowDropDown } from '@mui/icons-material' import { useFormContext } from 'react-hook-form' import { useTranslation } from 'react-i18next' -import { HugeDecimal } from '@dao-dao/math' import { FilterableItemPopup, InputErrorMessage, @@ -83,9 +82,7 @@ export const WithdrawRewardDistributionComponent: ActionComponent< label: getHumanReadableRewardDistributionLabel(t, distribution), description: ( {t('info.tokensWillBeWithdrawn', { - amount: HugeDecimal.from(selectedDistribution.remaining) - .toHumanReadableNumber(selectedDistribution.token.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: - selectedDistribution.token.decimals, - }), + amount: selectedDistribution.remaining.toHumanReadableString( + selectedDistribution.token.decimals + ), tokenSymbol: selectedDistribution.token.symbol, })}

diff --git a/packages/stateful/actions/core/actions/WithdrawRewardDistribution/index.tsx b/packages/stateful/actions/core/actions/WithdrawRewardDistribution/index.tsx index f07288f5d..419ecf3a1 100644 --- a/packages/stateful/actions/core/actions/WithdrawRewardDistribution/index.tsx +++ b/packages/stateful/actions/core/actions/WithdrawRewardDistribution/index.tsx @@ -1,3 +1,4 @@ +import { HugeDecimal } from '@dao-dao/math' import { daoRewardsDistributorExtraQueries, daoRewardsDistributorQueries, @@ -89,7 +90,7 @@ export class WithdrawRewardDistributionAction extends ActionBase => ({ ...distribution, - remaining: Number( + remaining: HugeDecimal.from( await this.options.queryClient.fetchQuery( daoRewardsDistributorQueries.undistributedRewards({ chainId: this.options.chain.chain_id, diff --git a/packages/stateful/components/SelfRelayExecuteModal.tsx b/packages/stateful/components/SelfRelayExecuteModal.tsx index 2690a23b1..d7f51923d 100644 --- a/packages/stateful/components/SelfRelayExecuteModal.tsx +++ b/packages/stateful/components/SelfRelayExecuteModal.tsx @@ -1209,7 +1209,7 @@ export const SelfRelayExecuteModal = ({ ? t('error.insufficientWalletBalance', { amount: HugeDecimal.from( fundTokenWithBalance.balance - ).toHumanReadableNumber( + ).toHumanReadableString( fundTokenWithBalance.token.decimals ), tokenSymbol: @@ -1251,18 +1251,7 @@ export const SelfRelayExecuteModal = ({ ) : (
diff --git a/packages/stateful/components/TokenAmountDisplay.tsx b/packages/stateful/components/TokenAmountDisplay.tsx index b92a4f36a..a7417fc34 100644 --- a/packages/stateful/components/TokenAmountDisplay.tsx +++ b/packages/stateful/components/TokenAmountDisplay.tsx @@ -1,11 +1,18 @@ +import { useQueryClient } from '@tanstack/react-query' + import { HugeDecimal } from '@dao-dao/math' -import { genericTokenSelector } from '@dao-dao/state/recoil' +import { tokenQueries } from '@dao-dao/state/query' import { TokenAmountDisplay as StatelessTokenAmountDisplay, - useCachedLoading, useChain, } from '@dao-dao/stateless' -import { StatefulTokenAmountDisplayProps, TokenType } from '@dao-dao/types' +import { + GenericToken, + StatefulTokenAmountDisplayProps, + TokenType, +} from '@dao-dao/types' + +import { useQueryLoadingData } from '../hooks' /** * Automatically show a native coin token amount. @@ -15,9 +22,13 @@ export const TokenAmountDisplay = ({ ...props }: StatefulTokenAmountDisplayProps) => { const { chain_id: chainId } = useChain() + const queryClient = useQueryClient() - const loadingGenericToken = useCachedLoading( - genericTokenSelector({ + const loadingGenericToken = useQueryLoadingData< + GenericToken, + GenericToken | undefined + >( + tokenQueries.info(queryClient, { type: TokenType.Native, denomOrAddress: denom, chainId, @@ -32,9 +43,7 @@ export const TokenAmountDisplay = ({ ? { loading: true } : { loading: false, - data: HugeDecimal.from(amount).toHumanReadableNumber( - loadingGenericToken.data.decimals - ), + data: HugeDecimal.from(amount), } } decimals={ diff --git a/packages/stateful/components/dao/DaoRewardsDistributorClaimCard.tsx b/packages/stateful/components/dao/DaoRewardsDistributorClaimCard.tsx index 66918a5f7..79fe7dff1 100644 --- a/packages/stateful/components/dao/DaoRewardsDistributorClaimCard.tsx +++ b/packages/stateful/components/dao/DaoRewardsDistributorClaimCard.tsx @@ -47,12 +47,12 @@ export const DaoRewardsDistributorClaimCard = ({ } const claimableDistributions = rewards.data.distributions.filter( - ({ rewards }) => rewards > 0 + ({ rewards }) => rewards.isPositive() ) if ( claimableDistributions.length === 0 || - claimableDistributions.every(({ rewards }) => rewards === 0) + claimableDistributions.every(({ rewards }) => rewards.isZero()) ) { toast.error(t('error.noRewardsToClaim')) return diff --git a/packages/stateful/components/dao/DaoVotingVaultCard.tsx b/packages/stateful/components/dao/DaoVotingVaultCard.tsx index fc7ca7a71..049567337 100644 --- a/packages/stateful/components/dao/DaoVotingVaultCard.tsx +++ b/packages/stateful/components/dao/DaoVotingVaultCard.tsx @@ -1,3 +1,4 @@ +import { HugeDecimal } from '@dao-dao/math' import { neutronVaultQueries } from '@dao-dao/state' import { DaoVotingVaultCard as StatelessDaoVotingVaultCard, @@ -35,9 +36,10 @@ export const DaoVotingVaultCard = (props: StatefulDaoVotingVaultCardProps) => { data: props.totalVotingPower.data === 0 ? 0 - : (Number(props.vault.totalPower) / - props.totalVotingPower.data) * - 100, + : props.vault.totalPower + .div(props.totalVotingPower.data) + .times(100) + .toNumber(), } } walletVotingPowerPercent={ @@ -52,12 +54,12 @@ export const DaoVotingVaultCard = (props: StatefulDaoVotingVaultCardProps) => { } : { loading: false, - data: - props.vault.totalPower === '0' - ? 0 - : (Number(loadingWalletVotingPower.data.power) / - Number(props.vault.totalPower)) * - 100, + data: props.vault.totalPower.isZero() + ? 0 + : HugeDecimal.from(loadingWalletVotingPower.data.power) + .div(props.vault.totalPower) + .times(100) + .toNumber(), } } {...props} diff --git a/packages/stateful/components/dao/MainDaoInfoCards.tsx b/packages/stateful/components/dao/MainDaoInfoCards.tsx index f0af07c0b..daf938026 100644 --- a/packages/stateful/components/dao/MainDaoInfoCards.tsx +++ b/packages/stateful/components/dao/MainDaoInfoCards.tsx @@ -106,12 +106,7 @@ const InnerMainDaoInfoCards = () => { value: ( { diff --git a/packages/stateful/components/gov/GovInfoBar.tsx b/packages/stateful/components/gov/GovInfoBar.tsx index 84cf532aa..d665286ff 100644 --- a/packages/stateful/components/gov/GovInfoBar.tsx +++ b/packages/stateful/components/gov/GovInfoBar.tsx @@ -26,12 +26,7 @@ export const GovInfoBar = () => { value: ( { '' ) : depositInfo.data && depositTokenInfo.data ? ( { '' ) : depositInfo.data && depositTokenInfo.data ? ( setAmount(newAmount)} token={governanceToken} unstakingDuration={unstakingDuration ?? null} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx index 86a693680..35c688d9b 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx @@ -244,13 +244,7 @@ const InnerStakingModal = ({ } onAction={onAction} onClose={onClose} - proposalDeposit={ - maxDeposit - ? HugeDecimal.from(maxDeposit).toHumanReadableNumber( - governanceToken.decimals - ) - : undefined - } + proposalDeposit={maxDeposit ? HugeDecimal.from(maxDeposit) : undefined} setAmount={(newAmount) => setAmount(newAmount)} token={governanceToken} unstakingDuration={unstakingDuration ?? null} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useMainDaoInfoCards.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useMainDaoInfoCards.tsx index 62b0b94f3..223624889 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useMainDaoInfoCards.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useMainDaoInfoCards.tsx @@ -72,12 +72,7 @@ export const useMainDaoInfoCards = (): DaoInfoCard[] => { amount={ loadingTotalStakedValue.loading ? { loading: true } - : { - loading: false, - data: HugeDecimal.from( - loadingTotalStakedValue.data - ).toHumanReadableNumber(decimals), - } + : HugeDecimal.from(loadingTotalStakedValue.data) } decimals={decimals} symbol={symbol} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx index ed9bbd9b3..dff764eac 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx @@ -244,13 +244,7 @@ const InnerStakingModal = ({ } onAction={onAction} onClose={onClose} - proposalDeposit={ - maxDeposit - ? HugeDecimal.from(maxDeposit).toHumanReadableNumber( - governanceToken.decimals - ) - : undefined - } + proposalDeposit={maxDeposit ? HugeDecimal.from(maxDeposit) : undefined} setAmount={(newAmount) => setAmount(newAmount)} token={governanceToken} unstakingDuration={unstakingDuration ?? null} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useMainDaoInfoCards.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useMainDaoInfoCards.tsx index 93109c95f..b97491378 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useMainDaoInfoCards.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useMainDaoInfoCards.tsx @@ -80,12 +80,7 @@ export const useMainDaoInfoCards = (): DaoInfoCard[] => { amount={ loadingTotalStakedValue.loading ? { loading: true } - : { - loading: false, - data: HugeDecimal.from( - loadingTotalStakedValue.data - ).toHumanReadableNumber(decimals), - } + : HugeDecimal.from(loadingTotalStakedValue.data) } decimals={decimals} symbol={symbol} diff --git a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useMainDaoInfoCards.tsx b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useMainDaoInfoCards.tsx index 956e6b701..16935b205 100644 --- a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useMainDaoInfoCards.tsx +++ b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useMainDaoInfoCards.tsx @@ -1,6 +1,5 @@ import { useTranslation } from 'react-i18next' -import { HugeDecimal } from '@dao-dao/math' import { TokenAmountDisplay } from '@dao-dao/stateless' import { DaoInfoCard } from '@dao-dao/types' @@ -24,9 +23,7 @@ export const useMainDaoInfoCards = (): DaoInfoCard[] => { }), value: ( diff --git a/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx b/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx index 4edf7b597..c243b8e3c 100644 --- a/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx +++ b/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx @@ -256,14 +256,15 @@ export const ProfileCardMemberInfoTokens = ({ ? loadingTokens.data : loadingTokens.data.filter( ({ token }) => - !!unstakingBalanceByToken[token.denomOrAddress] + unstakingBalanceByToken[token.denomOrAddress] ) ).map(({ token }) => ( diff --git a/packages/stateless/components/PayEntityDisplay.tsx b/packages/stateless/components/PayEntityDisplay.tsx index 8b6138fea..610cfad48 100644 --- a/packages/stateless/components/PayEntityDisplay.tsx +++ b/packages/stateless/components/PayEntityDisplay.tsx @@ -41,7 +41,7 @@ const PayEntityDisplayRow = ({ > {balanceLabel}

{ description: `Description of NFT #${id}`, highestOffer: { // Random price between 0 and 10000 with up to 6 decimals. - amount: Math.floor(Math.random() * (10000 * 1e6) + 1e6) / 1e6, + amount: HugeDecimal.from(Math.floor(Math.random() * (10000 * 1e6) + 1e6)), }, externalLink: { href: '/dog_nft.png', diff --git a/packages/stateless/components/nft/NftCard.tsx b/packages/stateless/components/nft/NftCard.tsx index 16a7c7f33..e7ce6a654 100644 --- a/packages/stateless/components/nft/NftCard.tsx +++ b/packages/stateless/components/nft/NftCard.tsx @@ -11,7 +11,6 @@ import { ComponentType, forwardRef, useState } from 'react' import { useTranslation } from 'react-i18next' import ReactPlayer from 'react-player' -import { HugeDecimal } from '@dao-dao/math' import { ButtonLinkProps, ButtonPopupSection, @@ -272,15 +271,11 @@ export const NftCard = forwardRef(

{t('title.highestOffer')}

- {typeof highestOffer.amount === 'number' && - !isNaN(highestOffer.amount) && + {highestOffer.amount && + highestOffer.amount.isPositive() && highestOffer.offerToken && ( maxTx.toHumanReadableNumber(token.decimals)) { return t('error.cannotStakeMoreThanYouHave') } } @@ -251,7 +253,7 @@ interface StakeUnstakeModesBodyProps { tokenSymbol: string tokenDecimals: number unstakingDuration: Duration | null - proposalDeposit?: number + proposalDeposit?: HugeDecimal } const StakeUnstakeModesBody = ({ @@ -275,9 +277,7 @@ const StakeUnstakeModesBody = ({ max={ loadingMax.loading ? undefined - : HugeDecimal.from(loadingMax.data).toHumanReadableNumber( - tokenDecimals - ) + : loadingMax.data.toHumanReadableNumber(tokenDecimals) } min={1 / 10 ** tokenDecimals} onChange={(e: ChangeEvent) => @@ -290,19 +290,14 @@ const StakeUnstakeModesBody = ({ unit={`$${tokenSymbol}`} value={amount} /> - {!loadingMax.loading && loadingMax.data.lt(amount) && ( - - {t('error.cannotStakeMoreThanYouHave')} - - )} + {!loadingMax.loading && + loadingMax.data.toHumanReadableNumber(tokenDecimals) < amount && ( + + {t('error.cannotStakeMoreThanYouHave')} + + )} { details: '', commission: 0.05, status: 'BOND_STATUS_BONDED', - tokens: 7, + tokens: HugeDecimal.fromHumanReadable(7, 6), }, rewards: HugeDecimal.fromHumanReadable(1.23, 6), }, @@ -74,7 +74,7 @@ export const makeProps = (isGovernanceToken = false): TokenCardProps => { details: '', commission: 0.05, status: 'BOND_STATUS_BONDED', - tokens: 7, + tokens: HugeDecimal.fromHumanReadable(7, 6), }, rewards: HugeDecimal.fromHumanReadable(4.56, 6), }, @@ -89,7 +89,7 @@ export const makeProps = (isGovernanceToken = false): TokenCardProps => { details: '', commission: 0.05, status: 'BOND_STATUS_BONDED', - tokens: 7, + tokens: HugeDecimal.fromHumanReadable(7, 6), }, rewards: HugeDecimal.fromHumanReadable(7.89, 6), }, @@ -104,7 +104,7 @@ export const makeProps = (isGovernanceToken = false): TokenCardProps => { details: '', commission: 0.05, status: 'BOND_STATUS_BONDED', - tokens: 7, + tokens: HugeDecimal.fromHumanReadable(7, 6), }, rewards: HugeDecimal.fromHumanReadable(10.11, 6), }, diff --git a/packages/stateless/components/token/TokenCard.tsx b/packages/stateless/components/token/TokenCard.tsx index cfafa551d..7f0c94c8f 100644 --- a/packages/stateless/components/token/TokenCard.tsx +++ b/packages/stateless/components/token/TokenCard.tsx @@ -221,9 +221,7 @@ export const TokenCard = ({
{/* leading-5 to match link-text's line-height. */} 0 && (
@@ -258,15 +254,13 @@ export const TokenCard = ({ {/* Only display `unstakedBalance` if total is loading or if different from total. While loading, the total above will hide. */} {(lazyInfo.loading || - lazyInfo.data.totalBalance !== unstakedBalance) && ( + !lazyInfo.data.totalBalance.eq(unstakedBalance)) && (

{t('info.availableBalance')}

{/* leading-5 to match link-text's line-height. */} {t('title.staked')}

@@ -405,11 +387,7 @@ export const TokenCard = ({ ( ( amount={ lazyInfo.loading || !lazyInfo.data.usdUnitPrice?.usdPrice ? { loading: true } - : HugeDecimal.from( - lazyInfo.data.totalBalance - ).toHumanReadableNumber(token.decimals) * - lazyInfo.data.usdUnitPrice.usdPrice + : lazyInfo.data.totalBalance.toUsdValue( + token.decimals, + lazyInfo.data.usdUnitPrice.usdPrice + ) } className="caption-text font-mono" dateFetched={ diff --git a/packages/stateless/components/token/UnstakingLine.tsx b/packages/stateless/components/token/UnstakingLine.tsx index 08c0c59cc..430bd717f 100644 --- a/packages/stateless/components/token/UnstakingLine.tsx +++ b/packages/stateless/components/token/UnstakingLine.tsx @@ -2,7 +2,6 @@ import clsx from 'clsx' import { ReactNode } from 'react' import TimeAgo from 'react-timeago' -import { HugeDecimal } from '@dao-dao/math' import { UnstakingTask, UnstakingTaskStatus } from '@dao-dao/types' import { formatDate, formatDateTimeTz } from '@dao-dao/utils' @@ -48,9 +47,7 @@ export const UnstakingLine = ({
{t('title.staked')}

{t('info.pendingRewards')}

/

& { config: SupportedChainConfig } -export interface Validator { +export type Validator = { address: string moniker: string website: string details: string commission: number status: string - tokens: number + tokens: HugeDecimal } -export interface Delegation { +export type Delegation = { validator: Validator delegated: Coin pendingReward: Coin } -export interface UnbondingDelegation { +export type UnbondingDelegation = { validator: Validator balance: Coin startedAtHeight: number finishesAt: Date } -export interface NativeDelegationInfo { +export type NativeDelegationInfo = { delegations: Delegation[] unbondingDelegations: UnbondingDelegation[] } diff --git a/packages/types/components/StakingModal.ts b/packages/types/components/StakingModal.ts index efdccd7a8..d1a2891b1 100644 --- a/packages/types/components/StakingModal.ts +++ b/packages/types/components/StakingModal.ts @@ -35,7 +35,7 @@ export interface StakingModalProps { // Token that is being staked. token: GenericToken // Proposal deposit for the token that is being staked. - proposalDeposit?: number + proposalDeposit?: HugeDecimal // Is there an error? error?: string | undefined // Are we ready to stake? Ex: is wallet connected? diff --git a/packages/types/components/TokenAmountDisplay.ts b/packages/types/components/TokenAmountDisplay.ts index 14059e727..b00d4f73a 100644 --- a/packages/types/components/TokenAmountDisplay.ts +++ b/packages/types/components/TokenAmountDisplay.ts @@ -1,5 +1,7 @@ import { ComponentPropsWithoutRef } from 'react' +import { HugeDecimal } from '@dao-dao/math' + import { Coin } from '../contracts' import { LoadingData } from '../misc' @@ -7,7 +9,13 @@ export type TokenAmountDisplayProps = Omit< ComponentPropsWithoutRef<'p'>, 'children' > & { - amount: number | LoadingData + /** + * The amount to display. If a HugeDecimal instance is passed, it is assumed + * to be a raw number and will be converted to a human-readable number using + * the decimals passed. If a number is passed, it must be a human-readable + * number (with decimals). + */ + amount: number | HugeDecimal | LoadingData prefix?: string prefixClassName?: string suffix?: string diff --git a/packages/types/dao.ts b/packages/types/dao.ts index 88170469b..368539187 100644 --- a/packages/types/dao.ts +++ b/packages/types/dao.ts @@ -12,6 +12,8 @@ import { UseFormSetValue, } from 'react-hook-form' +import { HugeDecimal } from '@dao-dao/math' + import { Account } from './account' import { SupportedChainConfig, WithChainId } from './chain' import { SecretModuleInstantiateInfo } from './clients' @@ -593,7 +595,7 @@ export type VotingVaultInfo = export type VotingVaultWithInfo = VotingVault & { info: VotingVaultInfo - totalPower: string + totalPower: HugeDecimal } /** @@ -635,7 +637,7 @@ export type DaoRewardDistributionWithRemaining = DaoRewardDistribution & { /** * Remaining rewards to be distributed. */ - remaining: number + remaining: HugeDecimal } /** @@ -653,7 +655,7 @@ export type PendingDaoRewards = { /** * Pending rewards for the distribution. */ - rewards: number + rewards: HugeDecimal }[] /** * Total pending rewards across all distributions, merged by token. diff --git a/packages/types/nft.ts b/packages/types/nft.ts index aabdac3ba..38af6043a 100644 --- a/packages/types/nft.ts +++ b/packages/types/nft.ts @@ -1,5 +1,7 @@ import { ComponentType, ReactNode, RefAttributes } from 'react' +import { HugeDecimal } from '@dao-dao/math' + import { ChainId, WithChainId } from './chain' import { ButtonLinkProps, @@ -88,7 +90,7 @@ export type NftCardInfo = { metadata?: Record highestOffer?: { offerToken?: GenericToken | null - amount?: number | null + amount?: HugeDecimal | null amountUsd?: number | null } name: string diff --git a/packages/utils/chain.ts b/packages/utils/chain.ts index 6e0807406..20817af82 100644 --- a/packages/utils/chain.ts +++ b/packages/utils/chain.ts @@ -6,6 +6,7 @@ import uniq from 'lodash.uniq' import RIPEMD160 from 'ripemd160' import semverGte from 'semver/functions/gte' +import { HugeDecimal } from '@dao-dao/math' import { Account, BaseChainConfig, @@ -147,7 +148,7 @@ export const cosmosValidatorToValidator = ({ ? Number(commission.commissionRates.rate) : -1, status: bondStatusToJSON(status), - tokens: Number(tokens), + tokens: HugeDecimal.from(tokens), }) export const getImageUrlForChainId = (chainId: string): string => { diff --git a/packages/utils/nft.ts b/packages/utils/nft.ts index 1ae46d7d9..50503ab4a 100644 --- a/packages/utils/nft.ts +++ b/packages/utils/nft.ts @@ -1,5 +1,6 @@ // If name is only a number, prefix with collection name. Fallback to token ID +import { HugeDecimal } from '@dao-dao/math' import { GenericToken, NftCardInfo, @@ -110,7 +111,7 @@ export const nftCardInfoFromStargazeIndexerNft = ( ? { offerToken, amountUsd: token.highestOffer?.offerPrice?.amountUsd, - amount: Number(token.highestOffer?.offerPrice?.amount), + amount: HugeDecimal.from(token.highestOffer?.offerPrice?.amount || -1), } : undefined, fetchedTimestamp: timestamp, From f8c143a276acdbd5dd6224cb725c64ba492b2161 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Sun, 29 Sep 2024 18:33:07 -0400 Subject: [PATCH 07/26] added international formatting and compaction to HugeDecimal, and use in TokenAmountDisplay --- packages/math/HugeDecimal.test.ts | 141 ++++++++++++++++++ packages/math/HugeDecimal.ts | 121 ++++++++++++++- packages/math/jest.config.js | 1 + packages/math/package.json | 4 +- .../CreateRewardDistribution/Component.tsx | 8 +- .../core/actions/ManageStaking/Component.tsx | 8 +- .../actions/core/actions/Spend/Component.tsx | 8 +- .../WithdrawRewardDistribution/Component.tsx | 9 +- .../components/SelfRelayExecuteModal.tsx | 7 +- .../components/dao/MainDaoInfoCards.tsx | 1 - .../stateful/components/gov/GovInfoBar.tsx | 1 - .../creators/TokenBased/getInstantiateInfo.ts | 4 +- packages/stateful/jest.config.js | 1 + .../stateless/pages/ViewSurvey/Complete.tsx | 1 - .../stateless/pages/ViewSurvey/Rate.tsx | 1 - .../RetroactiveCompensation/Renderer/utils.ts | 2 + packages/stateless/components/dao/DaoCard.tsx | 1 - .../dao/DaoRewardsDistributorClaimCard.tsx | 10 +- .../components/token/StakingModal.tsx | 4 +- .../components/token/TokenAmountDisplay.tsx | 134 ++++------------- .../types/components/TokenAmountDisplay.ts | 38 ++--- packages/utils/jest.config.js | 1 + 22 files changed, 353 insertions(+), 153 deletions(-) create mode 100644 packages/math/HugeDecimal.test.ts create mode 100644 packages/math/jest.config.js create mode 100644 packages/stateful/jest.config.js create mode 100644 packages/utils/jest.config.js diff --git a/packages/math/HugeDecimal.test.ts b/packages/math/HugeDecimal.test.ts new file mode 100644 index 000000000..bd01039ca --- /dev/null +++ b/packages/math/HugeDecimal.test.ts @@ -0,0 +1,141 @@ +import { HugeDecimal } from './HugeDecimal' + +test('HugeDecimal', () => { + expect( + HugeDecimal.fromHumanReadable( + 1234.5678, + 6 + ).toInternationalizedHumanReadableString({ + decimals: 6, + }) + ).toBe('1,234.5678') + + expect( + HugeDecimal.fromHumanReadable( + 1234.5678, + 6 + ).toInternationalizedHumanReadableString({ + decimals: 6, + minDecimals: 6, + }) + ).toBe('1,234.567800') + + expect( + HugeDecimal.fromHumanReadable( + 1234.5678, + 6 + ).toInternationalizedHumanReadableString({ + decimals: 6, + showFullAmount: false, + }) + ).toBe('1.23K') + + expect( + HugeDecimal.fromHumanReadable( + 1234.5678, + 6 + ).toInternationalizedHumanReadableString({ + decimals: 6, + showFullAmount: false, + minDecimals: 0, + }) + ).toBe('1.23K') + + expect( + HugeDecimal.fromHumanReadable( + 1234.5678, + 6 + ).toInternationalizedHumanReadableString({ + decimals: 6, + showFullAmount: false, + minDecimals: 4, + }) + ).toBe('1.2346K') + + expect( + HugeDecimal.fromHumanReadable( + 1234.5678, + 6 + ).toInternationalizedHumanReadableString({ + decimals: 6, + showFullAmount: false, + minDecimals: 8, + }) + ).toBe('1.23456780K') + + expect( + HugeDecimal.fromHumanReadable( + 0.001, + 6 + ).toInternationalizedHumanReadableString({ + decimals: 6, + showFullAmount: false, + }) + ).toBe('0.001') + + expect( + HugeDecimal.fromHumanReadable(1, 6).toInternationalizedHumanReadableString({ + decimals: 6, + showFullAmount: false, + }) + ).toBe('1') + + expect( + HugeDecimal.from( + '4901849977581594372686' + ).toInternationalizedHumanReadableString({ + decimals: 6, + showFullAmount: true, + }) + ).toBe('4,901,849,977,581,594.372686') + + expect( + HugeDecimal.from( + '4901849977581594372686' + ).toInternationalizedHumanReadableString({ + decimals: 6, + showFullAmount: true, + minDecimals: 8, + }) + ).toBe('4,901,849,977,581,594.37268600') + + expect( + HugeDecimal.from( + '4901849977581594372686' + ).toInternationalizedHumanReadableString({ + decimals: 0, + showFullAmount: false, + }) + ).toBe('4,901,849,977.58T') + + expect( + HugeDecimal.from( + // (BigInt(Number.MAX_SAFE_INTEGER) * 10000n).toString() + '90071992547409910000' + ).toInternationalizedHumanReadableString({ + decimals: 0, + showFullAmount: false, + }) + ).toBe('90,071,992.55T') + + expect( + HugeDecimal.from( + // (BigInt(Number.MAX_SAFE_INTEGER) * 10000n).toString() + '90071992547409910000' + ).toInternationalizedHumanReadableString({ + decimals: 6, + showFullAmount: false, + }) + ).toBe('90.07T') + + expect( + HugeDecimal.from( + // (BigInt(Number.MAX_SAFE_INTEGER) * 10000n).toString() + '90071992547409910000' + ).toInternationalizedHumanReadableString({ + decimals: 6, + showFullAmount: false, + minDecimals: 6, + }) + ).toBe('90.071993T') +}) diff --git a/packages/math/HugeDecimal.ts b/packages/math/HugeDecimal.ts index d29f6bae0..55765b70b 100644 --- a/packages/math/HugeDecimal.ts +++ b/packages/math/HugeDecimal.ts @@ -99,14 +99,24 @@ export class HugeDecimal { return this.value.toNumber() } + /** + * Returns a string in base-10 in normal notation. + */ toString() { - return this.value.toString() + return this.value.toString(10) } valueOf() { return this.value.valueOf() } + /** + * Returns the integer value of this HugeDecimal with decimals truncated. + */ + trunc() { + return new HugeDecimal(this.value.integerValue(BigNumber.ROUND_DOWN)) + } + plus(n: HugeDecimal.Value) { return new HugeDecimal(this.value.plus(valueToBigNumber(n))) } @@ -159,6 +169,19 @@ export class HugeDecimal { return new HugeDecimal(this.value.abs()) } + /** + * Returns a human-readable BigNumber instance with `decimals` decimal places. + * This is only meant to be used internally for formatting, since we don't + * want to encourage creating HugeDecimal instances with decimal places. + * Ideally, the underlying value is always in raw integer format. + * + * @param decimals the number of decimal places + * @returns human-readable BigNumber instance + */ + private toHumanReadable(decimals: number): HugeDecimal { + return new HugeDecimal(this.value.div(BigNumber(10).pow(decimals))) + } + /** * Returns a human-readable number with `decimals` decimal places. * @@ -166,7 +189,7 @@ export class HugeDecimal { * @returns human-readable number */ toHumanReadableNumber(decimals: number): number { - return this.value.div(BigNumber(10).pow(decimals)).toNumber() + return this.toHumanReadable(decimals).value.toNumber() } /** @@ -176,7 +199,97 @@ export class HugeDecimal { * @returns human-readable string */ toHumanReadableString(decimals: number): string { - return this.value.div(BigNumber(10).pow(decimals)).toFormat(decimals) + return this.toHumanReadable(decimals).value.toFormat(decimals) + } + + /** + * Returns an internationalized human-readable string with abbreviation of + * large numbers. + * + * @returns an internationalized human-readable string + */ + toInternationalizedHumanReadableString({ + decimals, + showFullAmount = true, + minDecimals = 0, + }: { + /** + * The number of decimals used to make this number human-readable. + */ + decimals: number + /** + * Whether or not to show the full amount. Large numbers will be abbreviated + * if this is false. Defaults to true. + */ + showFullAmount?: boolean + /** + * The minimum number of decimal places to show. Defaults to the smallest + * number of non-zero decimal places less than or equal to `decimals`. + */ + minDecimals?: number + }): string { + // Get the decimal separator for the current locale. + const decimalSeparator = (1.1).toLocaleString()[1] + + // Use BigInt for integer part of the number, and add the decimals manually. + // If the number is too large to fit within the size of Number, must use + // this BigInt approach even when not showing the full amount. + if (showFullAmount || this.gte(Number.MAX_SAFE_INTEGER)) { + const human = this.toHumanReadable(decimals) + + const int = human.trunc() + const dec = human.minus(int) + + // Show at least minDecimals, up to the exact number of decimal places in + // the original number, if showing the full amount. If not showing the + // full amount, this number must be very large (based on the conditional + // above), and thus none (0) of the actual decimal places are shown, since + // the large part of the number will be abbreviated (e.g. 1,234.5678 gets + // converted into 1.23K, and the 0.5678 are hidden). + const decimalPlacesToShow = showFullAmount + ? Math.max( + dec.value.toFormat({ decimalSeparator }).split(decimalSeparator)[1] + ?.length ?? 0, + minDecimals + ) + : 0 + + const intStr = BigInt(int.toString()).toLocaleString( + undefined, + showFullAmount + ? undefined + : { + notation: 'compact', + // Cap minDecimals to 20, which is the maximum allowed. + maximumFractionDigits: Math.min( + Math.max(2, minDecimals), + 20 + ) as 20, + } + ) + const decStr = + decimalPlacesToShow > 0 + ? decimalSeparator + + dec.value + .toFormat(decimalPlacesToShow, { decimalSeparator }) + .split(decimalSeparator)[1] + : '' + + return intStr + decStr + } + // If entire number can fit within the size of Number and not showing the + // full amount, use Number for compact internationalized formatting. + else { + const human = this.toHumanReadableNumber(decimals) + return human.toLocaleString(undefined, { + notation: 'compact', + minimumFractionDigits: minDecimals || (human >= 1000 ? 2 : undefined), + maximumFractionDigits: Math.max( + minDecimals, + human >= 1000 ? 2 : decimals + ), + }) + } } /** @@ -187,7 +300,7 @@ export class HugeDecimal { */ toCoin(denom: string): Coin { return { - amount: this.value.toString(), + amount: this.toString(), denom, } } diff --git a/packages/math/jest.config.js b/packages/math/jest.config.js new file mode 100644 index 000000000..50200e3d7 --- /dev/null +++ b/packages/math/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest.config') diff --git a/packages/math/package.json b/packages/math/package.json index 43b21c0db..da8542580 100644 --- a/packages/math/package.json +++ b/packages/math/package.json @@ -4,13 +4,15 @@ "license": "BSD-3-Clause-Clear", "scripts": { "format": "eslint . --fix", - "lint": "eslint ." + "lint": "eslint .", + "test": "jest" }, "dependencies": { "bignumber.js": "^9.1.2" }, "devDependencies": { "@dao-dao/config": "2.5.0-rc.3", + "jest": "^29.1.1", "typescript": "5.3.3" }, "prettier": "@dao-dao/config/prettier", diff --git a/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx index a65226761..75e04307d 100644 --- a/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx @@ -160,9 +160,11 @@ export const CreateRewardDistributionComponent: ActionComponent< description: t('title.balance') + ': ' + - HugeDecimal.from(balance).toHumanReadableString( - token.decimals - ), + HugeDecimal.from( + balance + ).toInternationalizedHumanReadableString({ + decimals: token.decimals, + }), })), } } diff --git a/packages/stateful/actions/core/actions/ManageStaking/Component.tsx b/packages/stateful/actions/core/actions/ManageStaking/Component.tsx index e6a1d2672..98e2a32ab 100644 --- a/packages/stateful/actions/core/actions/ManageStaking/Component.tsx +++ b/packages/stateful/actions/core/actions/ManageStaking/Component.tsx @@ -180,7 +180,9 @@ export const ManageStakingComponent: ActionComponent< (sourceValidatorStaked.isZero() ? t('error.nothingStaked') : t('error.stakeInsufficient', { - amount: maxAmount.toHumanReadableString(nativeToken.decimals), + amount: maxAmount.toInternationalizedHumanReadableString({ + decimals: nativeToken.decimals, + }), tokenSymbol: nativeToken.symbol, })) ) @@ -202,7 +204,9 @@ export const ManageStakingComponent: ActionComponent< (sourceValidatorStaked.isZero() ? t('error.nothingStaked') : t('error.stakeInsufficient', { - amount: maxAmount.toHumanReadableString(nativeToken.decimals), + amount: maxAmount.toInternationalizedHumanReadableString({ + decimals: nativeToken.decimals, + }), tokenSymbol: nativeToken.symbol, })) ) diff --git a/packages/stateful/actions/core/actions/Spend/Component.tsx b/packages/stateful/actions/core/actions/Spend/Component.tsx index 38d696c89..3baabd1af 100644 --- a/packages/stateful/actions/core/actions/Spend/Component.tsx +++ b/packages/stateful/actions/core/actions/Spend/Component.tsx @@ -416,9 +416,11 @@ export const SpendComponent: ActionComponent = ({ description: t('title.balance') + ': ' + - HugeDecimal.from(balance).toHumanReadableString( - token.decimals - ), + HugeDecimal.from( + balance + ).toInternationalizedHumanReadableString({ + decimals: token.decimals, + }), })), } } diff --git a/packages/stateful/actions/core/actions/WithdrawRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/WithdrawRewardDistribution/Component.tsx index 7d76d7799..55ed50e17 100644 --- a/packages/stateful/actions/core/actions/WithdrawRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/WithdrawRewardDistribution/Component.tsx @@ -116,9 +116,12 @@ export const WithdrawRewardDistributionComponent: ActionComponent< {selectedDistribution && (

{t('info.tokensWillBeWithdrawn', { - amount: selectedDistribution.remaining.toHumanReadableString( - selectedDistribution.token.decimals - ), + amount: + selectedDistribution.remaining.toInternationalizedHumanReadableString( + { + decimals: selectedDistribution.token.decimals, + } + ), tokenSymbol: selectedDistribution.token.symbol, })}

diff --git a/packages/stateful/components/SelfRelayExecuteModal.tsx b/packages/stateful/components/SelfRelayExecuteModal.tsx index d7f51923d..1edeb801d 100644 --- a/packages/stateful/components/SelfRelayExecuteModal.tsx +++ b/packages/stateful/components/SelfRelayExecuteModal.tsx @@ -1209,9 +1209,10 @@ export const SelfRelayExecuteModal = ({ ? t('error.insufficientWalletBalance', { amount: HugeDecimal.from( fundTokenWithBalance.balance - ).toHumanReadableString( - fundTokenWithBalance.token.decimals - ), + ).toInternationalizedHumanReadableString({ + decimals: + fundTokenWithBalance.token.decimals, + }), tokenSymbol: fundTokenWithBalance.token.symbol, }) diff --git a/packages/stateful/components/dao/MainDaoInfoCards.tsx b/packages/stateful/components/dao/MainDaoInfoCards.tsx index daf938026..4860d1db0 100644 --- a/packages/stateful/components/dao/MainDaoInfoCards.tsx +++ b/packages/stateful/components/dao/MainDaoInfoCards.tsx @@ -114,7 +114,6 @@ const InnerMainDaoInfoCards = () => { : new Date(tvlLoading.data.timestamp) } estimatedUsdValue - hideApprox /> ), }, diff --git a/packages/stateful/components/gov/GovInfoBar.tsx b/packages/stateful/components/gov/GovInfoBar.tsx index d665286ff..ca1d2fe10 100644 --- a/packages/stateful/components/gov/GovInfoBar.tsx +++ b/packages/stateful/components/gov/GovInfoBar.tsx @@ -34,7 +34,6 @@ export const GovInfoBar = () => { : new Date(tvlLoading.data.timestamp) } estimatedUsdValue - hideApprox /> ), }, diff --git a/packages/stateful/creators/TokenBased/getInstantiateInfo.ts b/packages/stateful/creators/TokenBased/getInstantiateInfo.ts index 08bb47d8e..fddb08dd7 100644 --- a/packages/stateful/creators/TokenBased/getInstantiateInfo.ts +++ b/packages/stateful/creators/TokenBased/getInstantiateInfo.ts @@ -76,7 +76,9 @@ export const getInstantiateInfo: DaoCreatorGetInstantiateInfo = ({ // members. amount: HugeDecimal.from( (weight / members.length / 100) * initialSupply - ).toHumanReadableString(NEW_DAO_TOKEN_DECIMALS), + ).toInternationalizedHumanReadableString({ + decimals: NEW_DAO_TOKEN_DECIMALS, + }), })) ) // To prevent rounding issues, treasury balance becomes the remaining tokens diff --git a/packages/stateful/jest.config.js b/packages/stateful/jest.config.js new file mode 100644 index 000000000..50200e3d7 --- /dev/null +++ b/packages/stateful/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest.config') diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Complete.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Complete.tsx index c35165b21..8df82a3fd 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Complete.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Complete.tsx @@ -537,7 +537,6 @@ export const InnerComplete = ({ className="caption-text text-right" dateFetched={tokenPrices[0]?.timestamp} estimatedUsdValue - hideApprox prefix="= " />
diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Rate.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Rate.tsx index 2ef4e4a39..1c2306f02 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Rate.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Rate.tsx @@ -427,7 +427,6 @@ export const Rate = ({ className="caption-text text-right" dateFetched={tokenPrices[0]?.timestamp} estimatedUsdValue - hideApprox prefix="= " />
diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/utils.ts b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/utils.ts index 168e703a8..996b7f7f8 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/utils.ts +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/utils.ts @@ -90,6 +90,7 @@ export const computeCompensation = ( denomOrAddress: denom, amount: HugeDecimal.from(amount) .times(proportionalCompensation) + .trunc() .toString(), }) ) @@ -99,6 +100,7 @@ export const computeCompensation = ( denomOrAddress: address, amount: HugeDecimal.from(amount) .times(proportionalCompensation) + .trunc() .toString(), }) ) diff --git a/packages/stateless/components/dao/DaoCard.tsx b/packages/stateless/components/dao/DaoCard.tsx index 2f0cc7975..99ee75a1b 100644 --- a/packages/stateless/components/dao/DaoCard.tsx +++ b/packages/stateless/components/dao/DaoCard.tsx @@ -121,7 +121,6 @@ export const DaoCard = ({ ? { loading: true } : lazyData.data.tokenWithBalance.balance } - hideApprox {...(showingEstimatedUsdValue ? { estimatedUsdValue: true, diff --git a/packages/stateless/components/dao/DaoRewardsDistributorClaimCard.tsx b/packages/stateless/components/dao/DaoRewardsDistributorClaimCard.tsx index 009affcc6..54e91ccd7 100644 --- a/packages/stateless/components/dao/DaoRewardsDistributorClaimCard.tsx +++ b/packages/stateless/components/dao/DaoRewardsDistributorClaimCard.tsx @@ -59,12 +59,10 @@ export const DaoRewardsDistributorClaimCard = ({ )} dateFetched={totalTimestamp} decimals={2} - hideApprox hideSymbol - maxDecimals={2} minAmount={hasRewards ? 0.01 : undefined} - minDecimals={2} prefix="$" + showAllDecimals />
@@ -140,7 +138,7 @@ export const DaoRewardsDistributorClaimCard = ({ token.imageUrl || getFallbackImage(token.denomOrAddress) } - minDecimals={token.decimals} + showAllDecimals showFullAmount suffix={' $' + token.symbol} suffixClassName="whitespace-pre text-text-tertiary" @@ -156,12 +154,10 @@ export const DaoRewardsDistributorClaimCard = ({ )} dateFetched={timestamp} decimals={2} - hideApprox hideSymbol - maxDecimals={2} minAmount={usdValue > 0 ? 0.01 : undefined} - minDecimals={2} prefix="$" + showAllDecimals />
diff --git a/packages/stateless/components/token/StakingModal.tsx b/packages/stateless/components/token/StakingModal.tsx index 41823ab0b..e27b339b4 100644 --- a/packages/stateless/components/token/StakingModal.tsx +++ b/packages/stateless/components/token/StakingModal.tsx @@ -338,7 +338,9 @@ const StakeUnstakeModesBody = ({ decimals={tokenDecimals} label={t('button.stakeAllButProposalDeposit', { proposalDeposit: - proposalDeposit.toHumanReadableString(tokenDecimals), + proposalDeposit.toInternationalizedHumanReadableString({ + decimals: tokenDecimals, + }), tokenSymbol, })} loadingMax={ diff --git a/packages/stateless/components/token/TokenAmountDisplay.tsx b/packages/stateless/components/token/TokenAmountDisplay.tsx index ad6f0f516..b3245037c 100644 --- a/packages/stateless/components/token/TokenAmountDisplay.tsx +++ b/packages/stateless/components/token/TokenAmountDisplay.tsx @@ -7,7 +7,6 @@ import { formatTime, getDisplayNameForChainId, toAccessibleImageUrl, - toFixedDown, } from '@dao-dao/utils' import { ChainLogo } from '../chain/ChainLogo' @@ -29,11 +28,6 @@ import { Tooltip } from '../tooltip/Tooltip' // The only token amounts we intentionally don't show with full decimals are USD // value estimates (i.e. USDC) (max 2). -// Default maximum decimals to use in a USD estimate. -const USD_ESTIMATE_DEFAULT_MAX_DECIMALS = 2 -// Maximum decimals to use in a large compacted value. -const LARGE_COMPACT_MAX_DECIMALS = 2 - export const TokenAmountDisplay = ({ amount: _amount, decimals: _decimals = 0, @@ -41,12 +35,10 @@ export const TokenAmountDisplay = ({ prefixClassName, suffix, suffixClassName, - minDecimals, - maxDecimals, - hideApprox, dateFetched, minAmount: _minAmount, - showFullAmount, + showFullAmount = false, + showAllDecimals, iconUrl, iconClassName, showChainId, @@ -62,11 +54,11 @@ export const TokenAmountDisplay = ({ const tokenTranslation = estimatedUsdValue ? 'format.estUsdValue' : 'format.token' - const decimals = estimatedUsdValue - ? USD_ESTIMATE_DEFAULT_MAX_DECIMALS - : _decimals + const decimals = estimatedUsdValue ? 2 : _decimals const minAmount = estimatedUsdValue ? 0.01 : _minAmount + showAllDecimals ||= estimatedUsdValue + const translateOrOmitSymbol = (translationKey: string, amount: string) => hideSymbol ? amount @@ -90,108 +82,40 @@ export const TokenAmountDisplay = ({ ) } - // Extract amount from loaded value. + // Extract amount from loaded value and convert to HugeDecimal. let amount = - typeof _amount === 'number' + _amount instanceof HugeDecimal ? _amount - : _amount instanceof HugeDecimal - ? _amount.toHumanReadableNumber(decimals) + : typeof _amount === 'number' + ? HugeDecimal.fromHumanReadable(_amount, decimals) : _amount ? _amount.data instanceof HugeDecimal - ? _amount.data.toHumanReadableNumber(decimals) - : _amount.data - : 0 + ? _amount.data + : HugeDecimal.fromHumanReadable(_amount.data, decimals) + : HugeDecimal.zero // If amount too small, set to min and add `< ` to prefix. - const amountBelowMin = !!minAmount && amount < minAmount + const amountBelowMin = !!minAmount && amount.lt(minAmount) if (amountBelowMin) { - amount = minAmount + amount = HugeDecimal.fromHumanReadable(minAmount, decimals) prefix = `< ${prefix || ''}` } - const options: Intl.NumberFormatOptions = { - // Always show all decimals if USD estimate. - minimumFractionDigits: estimatedUsdValue - ? USD_ESTIMATE_DEFAULT_MAX_DECIMALS - : minDecimals, - maximumFractionDigits: decimals, - } + const minDecimals = showAllDecimals ? decimals : 0 - const maxCompactDecimals = - maxDecimals ?? - (estimatedUsdValue ? USD_ESTIMATE_DEFAULT_MAX_DECIMALS : decimals) - const compactOptions: Intl.NumberFormatOptions & { - roundingPriority: string - } = { - ...options, - notation: 'compact', - maximumFractionDigits: maxCompactDecimals, - // notation=compact seems to set maximumSignificantDigits if undefined. - // Because we are rounding toward more precision above, set - // maximumSignificantDigits to 1 so that notation=compact does not override - // it and display extra decimals in case maximumFractionDigits is less. This - // appears to work fine on both Chrome and Safari, which is good enough for - // now. This is a crazy hack. - maximumSignificantDigits: 1, - // Safari (and potentially other non-Chrome browsers) uses only 1 decimal - // when notation=compact. roundingPriority=morePrecision tells the formatter - // to resolve decimal contraint conflicts with the result with greater - // precision. - roundingPriority: 'morePrecision', - } + const amountDisplay = amount.toInternationalizedHumanReadableString({ + decimals, + showFullAmount, + minDecimals, + }) - const full = toFixedDown(amount, decimals).toLocaleString(undefined, options) + const display = translateOrOmitSymbol(tokenTranslation, amountDisplay) - // Abbreviated number. Example: 1,000,000 => 1M, or 1.2345 => 1.23. - let compact = toFixedDown(amount, maxCompactDecimals).toLocaleString( - undefined, - compactOptions - ) - - const largeNumber = amount >= 1000 - - // If this is a large number that is compacted, and minDecimals/maxDecimals - // are not being overridden, use fewer decimals because compact notation looks - // bad with too many decimals. We first needed to use the same decimals to - // compare and see if compact had any effect. If compact changed nothing, we - // want to keep the original decimals. - if ( + // Show full value in tooltip if compacted and not an estimated USD value. + const shouldShowFullTooltip = !showFullAmount && - largeNumber && - full !== compact && - minDecimals === undefined && - maxDecimals === undefined - ) { - compact = toFixedDown(amount, LARGE_COMPACT_MAX_DECIMALS).toLocaleString( - undefined, - { - ...compactOptions, - maximumFractionDigits: LARGE_COMPACT_MAX_DECIMALS, - } - ) - } - - const wasCompacted = full !== compact - - // If compact is different from full and not a large number, display - // approximation indication (e.g. ~15.34 when the full value is 15.344913). - // When large, the compact notation (e.g. 1.52K or 23.5M) is enough to - // indicate that there is missing info, and we don't need the explicit - // approximation indication. - const display = - (!showFullAmount && - wasCompacted && - !largeNumber && - !hideApprox && + amount.toHumanReadableNumber(decimals) >= 1000 && !estimatedUsdValue - ? '~' - : '') + - translateOrOmitSymbol(tokenTranslation, showFullAmount ? full : compact) - - // Show full value in tooltip if different from compact and not an estimated - // USD value. - const shouldShowFullTooltip = - !showFullAmount && wasCompacted && !estimatedUsdValue && amount > 0 return ( {shouldShowFullTooltip && - // eslint-disable-next-line i18next/no-literal-string - translateOrOmitSymbol('format.token', full)} + translateOrOmitSymbol( + // eslint-disable-next-line i18next/no-literal-string + 'format.token', + amount.toInternationalizedHumanReadableString({ + decimals, + showFullAmount: true, + }) + )} {shouldShowFullTooltip && dateFetched &&
} diff --git a/packages/types/components/TokenAmountDisplay.ts b/packages/types/components/TokenAmountDisplay.ts index b00d4f73a..0b409f119 100644 --- a/packages/types/components/TokenAmountDisplay.ts +++ b/packages/types/components/TokenAmountDisplay.ts @@ -20,26 +20,10 @@ export type TokenAmountDisplayProps = Omit< prefixClassName?: string suffix?: string suffixClassName?: string - /** - * Max decimals to display. - */ - maxDecimals?: number - /** - * Min decimals to display. - */ - minDecimals?: number - /** - * Don't show approximation indication (like a tilde). - */ - hideApprox?: boolean /** * Add to tooltip if present. */ dateFetched?: Date - /** - * Show full amount if true. - */ - showFullAmount?: boolean /** * If present, will add a rounded icon to the left. */ @@ -74,6 +58,15 @@ export type TokenAmountDisplayProps = Omit< * prefix and display this value. */ minAmount?: number + /** + * Show full amount if true. Defaults to false. + */ + showFullAmount?: boolean + /** + * Pad decimal places by appending zeros if the value does not have as + * many decimals as specified. + */ + showAllDecimals?: boolean estimatedUsdValue?: false } // Alow hiding symbol. @@ -89,6 +82,15 @@ export type TokenAmountDisplayProps = Omit< * prefix and display this value. */ minAmount?: number + /** + * Show full amount if true. Defaults to false. + */ + showFullAmount?: boolean + /** + * Pad decimal places by appending zeros if the value does not have as + * many decimals as specified. + */ + showAllDecimals?: boolean estimatedUsdValue?: false } // If USD estimate, disallow symbol, decimals, and minAmount. @@ -97,6 +99,8 @@ export type TokenAmountDisplayProps = Omit< hideSymbol?: boolean decimals?: never minAmount?: never + showFullAmount?: never + showAllDecimals?: never estimatedUsdValue: true } ) @@ -107,8 +111,6 @@ export type StatefulTokenAmountDisplayProps = Pick< | 'prefixClassName' | 'suffix' | 'suffixClassName' - | 'maxDecimals' - | 'hideApprox' | 'showFullAmount' | 'iconClassName' | 'onClick' diff --git a/packages/utils/jest.config.js b/packages/utils/jest.config.js new file mode 100644 index 000000000..50200e3d7 --- /dev/null +++ b/packages/utils/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest.config') From 1ff1e8f86272542255d0add4185bd36b4d8e871c Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Sun, 29 Sep 2024 23:26:34 -0400 Subject: [PATCH 08/26] fix HugeDecimal.isPositive implementation --- packages/math/HugeDecimal.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/math/HugeDecimal.ts b/packages/math/HugeDecimal.ts index 55765b70b..701ec84bd 100644 --- a/packages/math/HugeDecimal.ts +++ b/packages/math/HugeDecimal.ts @@ -133,8 +133,11 @@ export class HugeDecimal { return new HugeDecimal(this.value.div(valueToBigNumber(n))) } + /** + * Returns whether or not the value is greater than zero. + */ isPositive() { - return this.value.isPositive() + return this.value.gt(0) } isZero() { From 9162c9160e0c6ff80a42c931b411dfed0dd2a518 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Sun, 29 Sep 2024 23:26:46 -0400 Subject: [PATCH 09/26] add available suffix to unstaked tokens --- .../components/ProfileCardMemberInfoTokens.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx b/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx index c243b8e3c..a85fdf2de 100644 --- a/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx +++ b/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx @@ -206,6 +206,7 @@ export const ProfileCardMemberInfoTokens = ({ : 'text-text-tertiary' )} decimals={token.decimals} + suffix={` ${t('info.available')}`} symbol={token.symbol} /> )) From ac187dfc36ee13770913df9fd6d91c9bcc6ecb78 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Sun, 29 Sep 2024 23:32:20 -0400 Subject: [PATCH 10/26] added more tests for abbreviation formatting --- packages/math/HugeDecimal.test.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/math/HugeDecimal.test.ts b/packages/math/HugeDecimal.test.ts index bd01039ca..8b1b29aa9 100644 --- a/packages/math/HugeDecimal.test.ts +++ b/packages/math/HugeDecimal.test.ts @@ -138,4 +138,24 @@ test('HugeDecimal', () => { minDecimals: 6, }) ).toBe('90.071993T') + + expect( + HugeDecimal.fromHumanReadable( + 11000027, + 6 + ).toInternationalizedHumanReadableString({ + decimals: 6, + showFullAmount: false, + }) + ).toBe('11.00M') + + expect( + HugeDecimal.fromHumanReadable( + 12345678, + 6 + ).toInternationalizedHumanReadableString({ + decimals: 6, + showFullAmount: false, + }) + ).toBe('12.35M') }) From 02dad2b50e37c02adf8919d80f07272097187eb7 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Sun, 29 Sep 2024 23:56:17 -0400 Subject: [PATCH 11/26] improve some number formatting --- packages/stateless/components/token/TokenAmountDisplay.tsx | 7 ++++--- packages/stateless/components/token/TokenCard.tsx | 5 +++++ packages/stateless/components/token/TokenLine.tsx | 1 - 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/stateless/components/token/TokenAmountDisplay.tsx b/packages/stateless/components/token/TokenAmountDisplay.tsx index b3245037c..c2e05082b 100644 --- a/packages/stateless/components/token/TokenAmountDisplay.tsx +++ b/packages/stateless/components/token/TokenAmountDisplay.tsx @@ -95,7 +95,8 @@ export const TokenAmountDisplay = ({ : HugeDecimal.zero // If amount too small, set to min and add `< ` to prefix. - const amountBelowMin = !!minAmount && amount.lt(minAmount) + const amountBelowMin = + !!minAmount && amount.isPositive() && amount.lt(minAmount) if (amountBelowMin) { amount = HugeDecimal.fromHumanReadable(minAmount, decimals) prefix = `< ${prefix || ''}` @@ -148,7 +149,7 @@ export const TokenAmountDisplay = ({ >
@@ -263,6 +264,7 @@ export const TokenCard = ({ amount={unstakedBalance} className="leading-5 text-text-body" decimals={token.decimals} + showFullAmount symbol={tokenSymbol} /> @@ -312,6 +314,7 @@ export const TokenCard = ({ amount={lazyInfo.loading ? { loading: true } : totalStaked} className="caption-text text-right font-mono text-text-body" decimals={token.decimals} + showFullAmount symbol={tokenSymbol} />
@@ -377,6 +380,7 @@ export const TokenCard = ({ @@ -391,6 +395,7 @@ export const TokenCard = ({ } className="caption-text text-right font-mono text-text-body" decimals={token.decimals} + showFullAmount symbol={tokenSymbol} />
diff --git a/packages/stateless/components/token/TokenLine.tsx b/packages/stateless/components/token/TokenLine.tsx index 76d40df7b..3a635b184 100644 --- a/packages/stateless/components/token/TokenLine.tsx +++ b/packages/stateless/components/token/TokenLine.tsx @@ -102,7 +102,6 @@ export const TokenLine = ( className="body-text truncate text-right font-mono" decimals={token.decimals} hideSymbol - showFullAmount wrapperClassName="justify-end" /> From ea688971f6b0ab215d69c784a57924dfec175c69 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Mon, 30 Sep 2024 01:23:07 -0400 Subject: [PATCH 12/26] use HugeDecimal in StakingModal and PercentButton --- packages/math/HugeDecimal.ts | 60 +++++++++--- .../CreateRewardDistribution/Component.tsx | 26 ++--- .../FundRewardDistribution/Component.tsx | 32 ++++--- .../actions/core/actions/Spend/Component.tsx | 43 +++++---- .../actions/core/actions/Spend/README.md | 6 -- .../actions/core/actions/Spend/index.tsx | 17 ++-- .../components/dao/DaoTokenDepositModal.tsx | 20 ++-- .../vesting/VestingStakingModal.tsx | 24 ++--- .../components/wallet/WalletStakingModal.tsx | 16 +--- .../components/StakingModal.tsx | 62 +++++------- .../components/StakingModal.tsx | 46 ++++----- .../components/StakingModal.tsx | 44 ++++----- .../components/StakingModal.tsx | 32 +++---- .../inputs/PercentButton.stories.tsx | 9 +- .../components/inputs/PercentButton.tsx | 96 ++++++++++--------- .../modals/TokenDepositModal.stories.tsx | 4 +- .../components/modals/TokenDepositModal.tsx | 33 ++++--- .../components/token/StakingModal.stories.tsx | 6 +- .../components/token/StakingModal.tsx | 80 ++++++---------- packages/types/components/StakingModal.ts | 8 +- 20 files changed, 314 insertions(+), 350 deletions(-) diff --git a/packages/math/HugeDecimal.ts b/packages/math/HugeDecimal.ts index 701ec84bd..b91427efd 100644 --- a/packages/math/HugeDecimal.ts +++ b/packages/math/HugeDecimal.ts @@ -77,6 +77,26 @@ export class HugeDecimal { ) } + /** + * Returns a HugeDecimal whose value is the maximum of the arguments. + * + * @param n values + * @returns a HugeDecimal instance + */ + static max(...n: HugeDecimal.Value[]) { + return new HugeDecimal(BigNumber.max(...n.map(valueToBigNumber))) + } + + /** + * Returns a HugeDecimal whose value is the minimum of the arguments. + * + * @param n values + * @returns a HugeDecimal instance + */ + static min(...n: HugeDecimal.Value[]) { + return new HugeDecimal(BigNumber.min(...n.map(valueToBigNumber))) + } + /** * Returns a HugeDecimal instance with value 0. */ @@ -106,6 +126,16 @@ export class HugeDecimal { return this.value.toString(10) } + /** + * Returns a string in normal notation with exactly `decimals` decimal places. + * + * @param decimals decimals + * @returns a string + */ + toFixed(decimals: number) { + return this.value.toFixed(decimals, BigNumber.ROUND_DOWN) + } + valueOf() { return this.value.valueOf() } @@ -173,16 +203,23 @@ export class HugeDecimal { } /** - * Returns a human-readable BigNumber instance with `decimals` decimal places. - * This is only meant to be used internally for formatting, since we don't - * want to encourage creating HugeDecimal instances with decimal places. - * Ideally, the underlying value is always in raw integer format. + * Returns a HugeDecimal whose value is the value of this HugeDecimal negated, + * i.e. multiplied by -1. + */ + negated() { + return new HugeDecimal(this.value.negated()) + } + + /** + * Returns a HugeDecimal instance with `decimals` decimal places. * * @param decimals the number of decimal places - * @returns human-readable BigNumber instance + * @returns HugeDecimal instance */ - private toHumanReadable(decimals: number): HugeDecimal { - return new HugeDecimal(this.value.div(BigNumber(10).pow(decimals))) + toHumanReadable(decimals: number): HugeDecimal { + return new HugeDecimal( + this.value.div(BigNumber(10).pow(decimals)).toFixed(decimals) + ) } /** @@ -257,7 +294,7 @@ export class HugeDecimal { ) : 0 - const intStr = BigInt(int.toString()).toLocaleString( + const intStr = BigInt(int.toFixed(0)).toLocaleString( undefined, showFullAmount ? undefined @@ -303,7 +340,7 @@ export class HugeDecimal { */ toCoin(denom: string): Coin { return { - amount: this.toString(), + amount: this.toFixed(0), denom, } } @@ -326,9 +363,6 @@ export class HugeDecimal { * @returns USD value */ toUsdValue(decimals: number, usdUnitPrice: BigNumber.Value) { - return this.value - .div(BigNumber(10).pow(decimals)) - .times(usdUnitPrice) - .toNumber() + return this.toHumanReadable(decimals).times(usdUnitPrice).toNumber() } } diff --git a/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx index 75e04307d..e66c10a4d 100644 --- a/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx @@ -88,9 +88,7 @@ export const CreateRewardDistributionComponent: ActionComponent< const minAmount = HugeDecimal.one.toHumanReadableNumber(decimals) - const selectedBalance = HugeDecimal.from( - selectedToken?.balance ?? 0 - ).toHumanReadableNumber(decimals) + const selectedBalance = HugeDecimal.from(selectedToken?.balance ?? 0) const warning = !isCreating || tokens.loading || @@ -100,10 +98,11 @@ export const CreateRewardDistributionComponent: ActionComponent< ? undefined : !selectedToken ? t('error.unknownDenom', { denom: denomOrAddress }) - : initialFunds && initialFunds > selectedBalance + : initialFunds && + selectedBalance.toHumanReadable(decimals).lt(initialFunds) ? t('error.insufficientFundsWarning', { - amount: selectedBalance.toLocaleString(undefined, { - maximumFractionDigits: decimals, + amount: selectedBalance.toInternationalizedHumanReadableString({ + decimals, }), tokenSymbol: selectedToken.token.symbol, }) @@ -297,7 +296,7 @@ export const CreateRewardDistributionComponent: ActionComponent< onClick={() => setValue( (fieldNamePrefix + 'initialFunds') as 'initialFunds', - selectedBalance + selectedBalance.toHumanReadableNumber(decimals) ) } showFullAmount @@ -305,20 +304,21 @@ export const CreateRewardDistributionComponent: ActionComponent< />
- {selectedBalance > 0 && ( + {selectedBalance.isPositive() && (
{[10, 25, 50, 75, 100].map((percent) => ( setValue( (fieldNamePrefix + 'initialFunds') as 'initialFunds', - amount + amount.toHumanReadableNumber(selectedToken.token.decimals) ) } /> diff --git a/packages/stateful/actions/core/actions/FundRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/FundRewardDistribution/Component.tsx index 87af28704..d8996b0ff 100644 --- a/packages/stateful/actions/core/actions/FundRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/FundRewardDistribution/Component.tsx @@ -72,16 +72,19 @@ export const FundRewardDistributionComponent: ActionComponent< tokens.data.find((t) => tokensEqual(t.token, selectedDistribution.token) )?.balance || 0 - ).toHumanReadableNumber(selectedDistribution.token.decimals) - : 0 + ) + : HugeDecimal.zero const warning = !isCreating || tokens.loading || tokens.updating || !selectedDistribution ? undefined - : amount && amount > selectedBalance + : amount && + selectedBalance + .toHumanReadable(selectedDistribution.token.decimals) + .lt(amount) ? t('error.insufficientFundsWarning', { - amount: selectedBalance.toLocaleString(undefined, { - maximumFractionDigits: selectedDistribution.token.decimals, + amount: selectedBalance.toInternationalizedHumanReadableString({ + decimals: selectedDistribution.token.decimals, }), tokenSymbol: selectedDistribution.token.symbol, }) @@ -185,7 +188,9 @@ export const FundRewardDistributionComponent: ActionComponent< onClick={() => setValue( (fieldNamePrefix + 'amount') as 'amount', - selectedBalance + selectedBalance.toHumanReadableNumber( + selectedDistribution.token.decimals + ) ) } showFullAmount @@ -193,20 +198,23 @@ export const FundRewardDistributionComponent: ActionComponent< />
- {selectedBalance > 0 && ( + {selectedBalance.isPositive() && (
{[10, 25, 50, 75, 100].map((percent) => ( setValue( (fieldNamePrefix + 'amount') as 'amount', - amount + amount.toHumanReadableNumber( + selectedDistribution.token.decimals + ) ) } /> diff --git a/packages/stateful/actions/core/actions/Spend/Component.tsx b/packages/stateful/actions/core/actions/Spend/Component.tsx index 3baabd1af..1cddd1ed1 100644 --- a/packages/stateful/actions/core/actions/Spend/Component.tsx +++ b/packages/stateful/actions/core/actions/Spend/Component.tsx @@ -121,7 +121,7 @@ export type SpendOptions = { // If this is an IBC transfer, this is the path of chains. ibcPath: LoadingDataWithError // If this is an IBC transfer, show the expected receive amount. - ibcAmountOut: LoadingDataWithError + ibcAmountOut: LoadingDataWithError // If this is an IBC transfer and a multi-TX route exists that unwinds the // tokens correctly but doesn't use PFM, this is the better path. betterNonPfmIbcPath: LoadingData @@ -268,14 +268,13 @@ export const SpendComponent: ActionComponent = ({ token.denomOrAddress === spendDenom && (token.type === TokenType.Cw20) === isCw20 ) - const balance = HugeDecimal.from( - selectedToken?.balance ?? 0 - ).toHumanReadableNumber(selectedToken?.token.decimals ?? 0) const decimals = loadedCustomToken ? token.data.decimals : selectedToken?.token.decimals || 0 + const balance = HugeDecimal.from(selectedToken?.balance ?? 0) + // A warning if the denom was not found in the treasury or the amount is too // high. We don't want to make this an error because often people want to // spend funds that a previous action makes available, so just show a warning. @@ -285,10 +284,10 @@ export const SpendComponent: ActionComponent = ({ ? undefined : !selectedToken ? t('error.unknownDenom', { denom: spendDenom }) - : spendAmount > balance + : balance.toHumanReadable(decimals).lt(spendAmount) ? t('error.insufficientFundsWarning', { - amount: balance.toLocaleString(undefined, { - maximumFractionDigits: decimals, + amount: balance.toInternationalizedHumanReadableString({ + decimals, }), tokenSymbol: symbol, }) @@ -479,27 +478,31 @@ export const SpendComponent: ActionComponent = ({ decimals={selectedToken.token.decimals} iconUrl={selectedToken.token.imageUrl} onClick={() => - setValue((fieldNamePrefix + 'amount') as 'amount', balance) + setValue( + (fieldNamePrefix + 'amount') as 'amount', + balance.toHumanReadableNumber(decimals) + ) } showFullAmount symbol={selectedToken.token.symbol} />
- {balance > 0 && ( + {balance.isPositive() && (
{[10, 25, 50, 75, 100].map((percent) => ( setValue( (fieldNamePrefix + 'amount') as 'amount', - amount + amount.toHumanReadableNumber(decimals) ) } /> @@ -667,11 +670,11 @@ export const SpendComponent: ActionComponent = ({ fee: neutronTransferFee.data .map(({ token, balance }) => t('format.token', { - amount: HugeDecimal.from(balance) - .toHumanReadableNumber(token.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: token.decimals, - }), + amount: HugeDecimal.from( + balance + ).toInternationalizedHumanReadableString({ + decimals: token.decimals, + }), symbol: token.symbol, }) ) @@ -856,7 +859,7 @@ export const SpendComponent: ActionComponent = ({ !ibcAmountOut.loading && !ibcAmountOut.errored && ibcAmountOut.data && - ibcAmountOut.data !== spendAmount && ( + !ibcAmountOut.data.eq(spendAmount) && (
diff --git a/packages/stateful/actions/core/actions/Spend/README.md b/packages/stateful/actions/core/actions/Spend/README.md index 6a4d693ce..3678c9263 100644 --- a/packages/stateful/actions/core/actions/Spend/README.md +++ b/packages/stateful/actions/core/actions/Spend/README.md @@ -29,12 +29,6 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). } ``` -You must set `decimals` correctly for the specified `amount` of funds. The final -message gets generated with `amount * 10^(decimals)` microunits of the specified -`denom`. For example: `amount = 5`, `decimals = 6`, and `denom = "untrn"` => -`5000000untrn` or `5 * 10^6 untrn`, where `untrn` is the microdenom/true -denomination of `NTRN`. - If used in a DAO, `fromChainId` and `from` determine which account has the tokens being sent, which can be the native chain or any supported Polytone or ICA chain. `toChainId` is unrelated, and it determines if the tokens are sent to diff --git a/packages/stateful/actions/core/actions/Spend/index.tsx b/packages/stateful/actions/core/actions/Spend/index.tsx index 72943b287..59c211d1c 100644 --- a/packages/stateful/actions/core/actions/Spend/index.tsx +++ b/packages/stateful/actions/core/actions/Spend/index.tsx @@ -1,4 +1,3 @@ -import { coin, coins } from '@cosmjs/amino' import { useQueryClient } from '@tanstack/react-query' import { ComponentType, useEffect, useState } from 'react' import { useFormContext } from 'react-hook-form' @@ -394,7 +393,7 @@ const StatefulSpendComponent: ComponentType< errored: false, } // Get the amount out from an IBC path. - const ibcAmountOut: LoadingDataWithError = isIbc + const ibcAmountOut: LoadingDataWithError = isIbc ? skipRoute.loading || !selectedToken ? { loading: true, @@ -410,9 +409,7 @@ const StatefulSpendComponent: ComponentType< loading: false, errored: false, updating: skipRoute.updating, - data: HugeDecimal.from( - skipRoute.data.amount_out - ).toHumanReadableNumber(selectedToken.token.decimals), + data: HugeDecimal.from(skipRoute.data.amount_out), } : { loading: false, @@ -591,7 +588,7 @@ export class SpendAction extends ActionBase { type: cw20 ? TokenType.Cw20 : TokenType.Native, }) ) - const amount = HugeDecimal.fromHumanReadable(_amount, decimals).toString() + const amount = HugeDecimal.fromHumanReadable(_amount, decimals) // Gov module community pool spend. if (this.options.context.type === ActionContextType.Gov) { @@ -601,7 +598,7 @@ export class SpendAction extends ActionBase { value: MsgCommunityPoolSpend.fromPartial({ authority: this.options.address, recipient: to, - amount: coins(amount, denom), + amount: amount.toCoins(denom), }), }, }) @@ -671,7 +668,7 @@ export class SpendAction extends ActionBase { value: { sourcePort: 'transfer', sourceChannel, - token: coin(amount, denom), + token: amount.toCoin(denom), sender: from, receiver: to, timeoutTimestamp, @@ -741,7 +738,7 @@ export class SpendAction extends ActionBase { } } else if (!cw20) { msg = { - bank: makeBankMessage(amount, to, denom), + bank: makeBankMessage(amount.toFixed(0), to, denom), } } else { msg = makeWasmMessage({ @@ -752,7 +749,7 @@ export class SpendAction extends ActionBase { msg: { transfer: { recipient: to, - amount, + amount: amount.toFixed(0), }, }, }, diff --git a/packages/stateful/components/dao/DaoTokenDepositModal.tsx b/packages/stateful/components/dao/DaoTokenDepositModal.tsx index 785011f1b..8947e5026 100644 --- a/packages/stateful/components/dao/DaoTokenDepositModal.tsx +++ b/packages/stateful/components/dao/DaoTokenDepositModal.tsx @@ -1,4 +1,3 @@ -import { coins } from '@cosmjs/stargate' import { useCallback, useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -79,7 +78,7 @@ export const DaoTokenDepositModal = ({ } ) - const [amount, setAmount] = useState(0) + const [amount, setAmount] = useState(HugeDecimal.zero) const [loading, setLoading] = useState(false) const transferCw20 = Cw20BaseHooks.useTransfer({ @@ -88,7 +87,7 @@ export const DaoTokenDepositModal = ({ }) const onDeposit = useCallback( - async (amount: number) => { + async (amount: HugeDecimal) => { if (!address) { toast.error(t('error.logInToContinue')) return @@ -101,22 +100,17 @@ export const DaoTokenDepositModal = ({ setLoading(true) try { - const microAmount = HugeDecimal.fromHumanReadable( - amount, - token.decimals - ).toString() - if (token.type === 'native') { const signingClient = await getSigningStargateClient() await signingClient.sendTokens( address, depositAddress, - coins(microAmount, token.denomOrAddress), + amount.toCoins(token.denomOrAddress), CHAIN_GAS_MULTIPLIER ) } else if (token.type === 'cw20') { await transferCw20({ - amount: microAmount, + amount: amount.toFixed(0), recipient: depositAddress, }) } @@ -126,8 +120,8 @@ export const DaoTokenDepositModal = ({ toast.success( t('success.depositedTokenIntoDao', { - amount: amount.toLocaleString(undefined, { - maximumFractionDigits: token.decimals, + amount: amount.toInternationalizedHumanReadableString({ + decimals: token.decimals, }), tokenSymbol: token.symbol, daoName, @@ -136,7 +130,7 @@ export const DaoTokenDepositModal = ({ onClose?.() // Clear amount after a timeout to allow closing. - setTimeout(() => setAmount(0), 500) + setTimeout(() => setAmount(HugeDecimal.zero), 500) } catch (err) { console.error(err) toast.error(processError(err)) diff --git a/packages/stateful/components/vesting/VestingStakingModal.tsx b/packages/stateful/components/vesting/VestingStakingModal.tsx index 3880d1ae8..ae424553b 100644 --- a/packages/stateful/components/vesting/VestingStakingModal.tsx +++ b/packages/stateful/components/vesting/VestingStakingModal.tsx @@ -76,7 +76,7 @@ export const VestingStakingModal = ({ const awaitNextBlock = useAwaitNextBlock() - const [amount, setAmount] = useState(0) + const [amount, setAmount] = useState(HugeDecimal.zero) const [loading, setLoading] = useState(false) const { address: walletAddress = '' } = useWallet({ @@ -103,7 +103,7 @@ export const VestingStakingModal = ({ const onAction = async ( mode: StakingMode, - amount: number, + amount: HugeDecimal, validator?: string, fromValidator?: string ) => { @@ -117,10 +117,7 @@ export const VestingStakingModal = ({ try { if (mode === StakingMode.Stake) { const data = { - amount: HugeDecimal.fromHumanReadable( - amount, - nativeToken.decimals - ).toString(), + amount: amount.toFixed(0), validator, } @@ -152,10 +149,7 @@ export const VestingStakingModal = ({ } } else if (mode === StakingMode.Unstake) { const data = { - amount: HugeDecimal.fromHumanReadable( - amount, - nativeToken.decimals - ).toString(), + amount: amount.toFixed(0), validator, } @@ -191,10 +185,6 @@ export const VestingStakingModal = ({ return } - const convertedAmount = HugeDecimal.fromHumanReadable( - amount, - nativeToken.decimals - ).toString() if (recipientIsDao) { await goToDaoProposal(recipient, 'create', { prefill: getDaoProposalSinglePrefill({ @@ -207,7 +197,7 @@ export const VestingStakingModal = ({ message: JSON.stringify( { redelegate: { - amount: convertedAmount, + amount: amount.toFixed(0), src_validator: fromValidator, dst_validator: validator, }, @@ -224,7 +214,7 @@ export const VestingStakingModal = ({ }) } else { await redelegate({ - amount: convertedAmount, + amount: amount.toFixed(0), srcValidator: fromValidator, dstValidator: validator, }) @@ -296,7 +286,7 @@ export const VestingStakingModal = ({ amount={amount} claimableTokens={ // Tokens are claimable somewhere else. - 0 + HugeDecimal.zero } enableRestaking initialMode={StakingMode.Stake} diff --git a/packages/stateful/components/wallet/WalletStakingModal.tsx b/packages/stateful/components/wallet/WalletStakingModal.tsx index 7495ec0d5..a5c6f0d6a 100644 --- a/packages/stateful/components/wallet/WalletStakingModal.tsx +++ b/packages/stateful/components/wallet/WalletStakingModal.tsx @@ -1,4 +1,3 @@ -import { coin } from '@cosmjs/stargate' import { useQueryClient } from '@tanstack/react-query' import { useState } from 'react' import toast from 'react-hot-toast' @@ -67,7 +66,7 @@ export const WalletStakingModal = (props: WalletStakingModalProps) => { const awaitNextBlock = useAwaitNextBlock() - const [amount, setAmount] = useState(0) + const [amount, setAmount] = useState(HugeDecimal.zero) const [loading, setLoading] = useState(false) const validatorsLoadable = useCachedLoadable( @@ -100,7 +99,7 @@ export const WalletStakingModal = (props: WalletStakingModalProps) => { const onAction = async ( mode: StakingMode, - amount: number, + amount: HugeDecimal, validator?: string | undefined ) => { // Should never happen. @@ -117,11 +116,6 @@ export const WalletStakingModal = (props: WalletStakingModalProps) => { try { const signingClient = await getSigningStargateClient() - const microAmount = HugeDecimal.fromHumanReadable( - amount, - nativeToken.decimals - ).toString() - if (mode === StakingMode.Stake) { await signingClient.signAndBroadcast( walletAddress, @@ -131,7 +125,7 @@ export const WalletStakingModal = (props: WalletStakingModalProps) => { { staking: { delegate: { - amount: coin(microAmount, nativeToken.denomOrAddress), + amount: amount.toCoin(nativeToken.denomOrAddress), validator, }, }, @@ -150,7 +144,7 @@ export const WalletStakingModal = (props: WalletStakingModalProps) => { { staking: { undelegate: { - amount: coin(microAmount, nativeToken.denomOrAddress), + amount: amount.toCoin(nativeToken.denomOrAddress), validator, }, }, @@ -183,7 +177,7 @@ export const WalletStakingModal = (props: WalletStakingModalProps) => { amount={amount} claimableTokens={ // Tokens are claimable somewhere else. - 0 + HugeDecimal.zero } initialMode={StakingMode.Stake} loading={loading} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx index 158f7f898..13df613b4 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx @@ -126,7 +126,7 @@ const InnerStakingModal = ({ ? walletStakedBalanceLoadable.contents.balance : undefined - const [amount, setAmount] = useState(0) + const [amount, setAmount] = useState(HugeDecimal.zero) const doCw20SendAndExecute = Cw20BaseHooks.useSend({ contractAddress: governanceToken.denomOrAddress, @@ -155,7 +155,7 @@ const InnerStakingModal = ({ } const awaitNextBlock = useAwaitNextBlock() - const onAction = async (mode: StakingMode, amount: number) => { + const onAction = async (mode: StakingMode, amount: HugeDecimal) => { if (!isWalletConnected) { toast.error(t('error.logInToContinue')) return @@ -169,10 +169,7 @@ const InnerStakingModal = ({ try { await doCw20SendAndExecute({ - amount: HugeDecimal.fromHumanReadable( - amount, - governanceToken.decimals - ).toString(), + amount: amount.toFixed(0), contract: stakingContractToExecute, msg: encodeJsonToBase64({ [isOraichainCustomStaking ? 'bond' : 'stake']: {}, @@ -186,10 +183,11 @@ const InnerStakingModal = ({ refreshTotals() refreshDaoVotingPower() - setAmount(0) + setAmount(HugeDecimal.zero) + toast.success( - `Staked ${amount.toLocaleString(undefined, { - maximumFractionDigits: governanceToken.decimals, + `Staked ${amount.toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, })} $${governanceToken.symbol}` ) @@ -220,41 +218,31 @@ const InnerStakingModal = ({ // value = amount_staked * total_value / staked_total // // => amount_staked = staked_total * value / total_value - let amountToUnstake = - (Number(totalStakedBalance.total) * amount) / Number(totalValue.total) + let amountToUnstake = amount + .times(totalStakedBalance.total) + .div(totalValue.total) // We have limited precision and on the contract side division rounds // down, so division and multiplication don't commute. Handle the common // case here where someone is attempting to unstake all of their funds. if ( HugeDecimal.from(walletStakedBalance) - .minus( - HugeDecimal.fromHumanReadable( - amountToUnstake, - governanceToken.decimals - ) - ) + .minus(amountToUnstake) .abs() .lte(1) ) { - amountToUnstake = HugeDecimal.from( - walletStakedBalance - ).toHumanReadableNumber(governanceToken.decimals) + amountToUnstake = HugeDecimal.from(walletStakedBalance) } try { - const convertedAmount = HugeDecimal.fromHumanReadable( - amountToUnstake, - governanceToken.decimals - ).toString() if (isOraichainCustomStaking) { await doOraichainUnbond({ - amount: convertedAmount, + amount: amountToUnstake.toFixed(0), stakingToken: governanceToken.denomOrAddress, }) } else { await doUnstake({ - amount: convertedAmount, + amount: amountToUnstake.toFixed(0), }) } @@ -266,10 +254,10 @@ const InnerStakingModal = ({ refreshClaims?.() refreshDaoVotingPower() - setAmount(0) + setAmount(HugeDecimal.zero) toast.success( - `Unstaked ${amount.toLocaleString(undefined, { - maximumFractionDigits: governanceToken.decimals, + `Unstaked ${amount.toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, })} $${governanceToken.symbol}` ) @@ -308,14 +296,14 @@ const InnerStakingModal = ({ refreshTotals() refreshClaims?.() - setAmount(0) + setAmount(HugeDecimal.zero) toast.success( - `Claimed ${HugeDecimal.from(sumClaimsAvailable || 0) - .toHumanReadableNumber(governanceToken.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: governanceToken.decimals, - })} $${governanceToken.symbol}` + `Claimed ${HugeDecimal.from( + sumClaimsAvailable || 0 + ).toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + })} $${governanceToken.symbol}` ) // Close once done. @@ -337,7 +325,7 @@ const InnerStakingModal = ({ return ( setAmount(newAmount)} + setAmount={setAmount} token={governanceToken} unstakingDuration={unstakingDuration ?? null} visible={visible} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx index 35c688d9b..530db0e3e 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx @@ -1,4 +1,3 @@ -import { coins } from '@cosmjs/stargate' import { useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' @@ -35,8 +34,8 @@ export const StakingModal = (props: BaseStakingModalProps) => ( ) const InnerStakingModal = ({ - onClose, visible, + onClose, initialMode = StakingMode.Stake, maxDeposit, }: BaseStakingModalProps) => { @@ -65,7 +64,7 @@ const InnerStakingModal = ({ fetchWalletStakedValue: true, }) - const [amount, setAmount] = useState(0) + const [amount, setAmount] = useState(HugeDecimal.zero) const doStake = DaoVotingNativeStakedHooks.useStake({ contractAddress: votingModuleAddress, @@ -90,7 +89,7 @@ const InnerStakingModal = ({ } const awaitNextBlock = useAwaitNextBlock() - const onAction = async (mode: StakingMode, amount: number) => { + const onAction = async (mode: StakingMode, amount: HugeDecimal) => { if (!isWalletConnected) { toast.error(t('error.logInToContinue')) return @@ -106,13 +105,7 @@ const InnerStakingModal = ({ await doStake( CHAIN_GAS_MULTIPLIER, undefined, - coins( - HugeDecimal.fromHumanReadable( - amount, - governanceToken.decimals - ).toString(), - governanceToken.denomOrAddress - ) + amount.toCoins(governanceToken.denomOrAddress) ) // New balances will not appear until the next block. @@ -122,10 +115,10 @@ const InnerStakingModal = ({ refreshTotals() refreshDaoVotingPower() - setAmount(0) + setAmount(HugeDecimal.zero) toast.success( - `Staked ${amount.toLocaleString(undefined, { - maximumFractionDigits: governanceToken.decimals, + `Staked ${amount.toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, })} $${governanceToken.symbol}` ) @@ -145,10 +138,7 @@ const InnerStakingModal = ({ try { await doUnstake({ - amount: HugeDecimal.fromHumanReadable( - amount, - governanceToken.decimals - ).toString(), + amount: amount.toFixed(0), }) // New balances will not appear until the next block. @@ -159,10 +149,10 @@ const InnerStakingModal = ({ refreshClaims?.() refreshDaoVotingPower() - setAmount(0) + setAmount(HugeDecimal.zero) toast.success( - `Unstaked ${amount.toLocaleString(undefined, { - maximumFractionDigits: governanceToken.decimals, + `Unstaked ${amount.toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, })} $${governanceToken.symbol}` ) @@ -193,14 +183,14 @@ const InnerStakingModal = ({ refreshTotals() refreshClaims?.() - setAmount(0) + setAmount(HugeDecimal.zero) toast.success( - `Claimed ${HugeDecimal.from(sumClaimsAvailable || 0) - .toHumanReadableNumber(governanceToken.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: governanceToken.decimals, - })} $${governanceToken.symbol}` + `Claimed ${HugeDecimal.from( + sumClaimsAvailable || 0 + ).toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + })} $${governanceToken.symbol}` ) // Close once done. @@ -222,7 +212,7 @@ const InnerStakingModal = ({ return ( { + const onAction = async (mode: StakingMode, amount: HugeDecimal) => { if (!isWalletConnected) { toast.error(t('error.logInToContinue')) return @@ -106,13 +105,7 @@ const InnerStakingModal = ({ await doStake( CHAIN_GAS_MULTIPLIER, undefined, - coins( - HugeDecimal.fromHumanReadable( - amount, - governanceToken.decimals - ).toString(), - governanceToken.denomOrAddress - ) + amount.toCoins(governanceToken.denomOrAddress) ) // New balances will not appear until the next block. @@ -122,10 +115,10 @@ const InnerStakingModal = ({ refreshTotals() refreshDaoVotingPower() - setAmount(0) + setAmount(HugeDecimal.zero) toast.success( - `Staked ${amount.toLocaleString(undefined, { - maximumFractionDigits: governanceToken.decimals, + `Staked ${amount.toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, })} $${governanceToken.symbol}` ) @@ -145,10 +138,7 @@ const InnerStakingModal = ({ try { await doUnstake({ - amount: HugeDecimal.fromHumanReadable( - amount, - governanceToken.decimals - ).toString(), + amount: amount.toFixed(0), }) // New balances will not appear until the next block. @@ -159,10 +149,10 @@ const InnerStakingModal = ({ refreshClaims?.() refreshDaoVotingPower() - setAmount(0) + setAmount(HugeDecimal.zero) toast.success( - `Unstaked ${amount.toLocaleString(undefined, { - maximumFractionDigits: governanceToken.decimals, + `Unstaked ${amount.toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, })} $${governanceToken.symbol}` ) @@ -193,14 +183,14 @@ const InnerStakingModal = ({ refreshTotals() refreshClaims?.() - setAmount(0) + setAmount(HugeDecimal.zero) toast.success( - `Claimed ${HugeDecimal.from(sumClaimsAvailable || 0) - .toHumanReadableNumber(governanceToken.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: governanceToken.decimals, - })} $${governanceToken.symbol}` + `Claimed ${HugeDecimal.from( + sumClaimsAvailable || 0 + ).toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + })} $${governanceToken.symbol}` ) // Close once done. @@ -222,7 +212,7 @@ const InnerStakingModal = ({ return ( { + const onAction = async (mode: StakingMode, amount: HugeDecimal) => { if (!selectedVault) { toast.error(t('error.loadingData')) return @@ -168,13 +167,7 @@ const InnerStakingModal = ({ await doStake( CHAIN_GAS_MULTIPLIER, undefined, - coins( - HugeDecimal.fromHumanReadable( - amount, - selectedVault.bondToken.decimals - ).toString(), - selectedVault.bondToken.denomOrAddress - ) + amount.toCoins(selectedVault.bondToken.denomOrAddress) ) // New balances will not appear until the next block. @@ -184,10 +177,10 @@ const InnerStakingModal = ({ refreshTotals() refreshDaoVotingPower() - setAmount(0) + setAmount(HugeDecimal.zero) toast.success( - `Staked ${amount.toLocaleString(undefined, { - maximumFractionDigits: selectedVault.bondToken.decimals, + `Staked ${amount.toInternationalizedHumanReadableString({ + decimals: selectedVault.bondToken.decimals, })} $${selectedVault.bondToken.symbol}` ) @@ -207,10 +200,7 @@ const InnerStakingModal = ({ try { await doUnstake({ - amount: HugeDecimal.fromHumanReadable( - amount, - selectedVault.bondToken.decimals - ).toString(), + amount: amount.toFixed(0), }) // New balances will not appear until the next block. @@ -220,10 +210,10 @@ const InnerStakingModal = ({ refreshTotals() refreshDaoVotingPower() - setAmount(0) + setAmount(HugeDecimal.zero) toast.success( - `Unstaked ${amount.toLocaleString(undefined, { - maximumFractionDigits: selectedVault.bondToken.decimals, + `Unstaked ${amount.toInternationalizedHumanReadableString({ + decimals: selectedVault.bondToken.decimals, })} $${selectedVault.bondToken.symbol}` ) @@ -253,7 +243,7 @@ const InnerStakingModal = ({ return ( const Template: ComponentStory = (args) => { - const [amount, setAmount] = useState(50) + const [amount, setAmount] = useState(HugeDecimal.fromHumanReadable(50, 6)) return } @@ -17,7 +19,6 @@ const Template: ComponentStory = (args) => { export const Default = Template.bind({}) Default.args = { label: '25%', - loadingMax: { loading: false, data: 1234 }, - percent: 0.25, - decimals: 6, + loadingMax: { loading: false, data: HugeDecimal.fromHumanReadable(1234, 6) }, + percent: 25, } diff --git a/packages/stateless/components/inputs/PercentButton.tsx b/packages/stateless/components/inputs/PercentButton.tsx index 0029f7d75..d0315a2de 100644 --- a/packages/stateless/components/inputs/PercentButton.tsx +++ b/packages/stateless/components/inputs/PercentButton.tsx @@ -1,65 +1,71 @@ import clsx from 'clsx' +import { HugeDecimal } from '@dao-dao/math' import { ButtonProps, LoadingData } from '@dao-dao/types' import { Button } from '../buttons' -export interface PercentButtonProps { - label: string - loadingMax: LoadingData +export type PercentButtonProps = { + loadingMax: LoadingData + /** + * Value out of 100 (e.g. 50 = 50%). + */ percent: number - amount: number - setAmount: (newAmount: number) => void - decimals: number + amount: HugeDecimal + setAmount: (newAmount: HugeDecimal) => void + /** + * Override the default label of `{percent}%`. + */ + label?: string className?: string - absoluteOffset?: number + absoluteOffset?: HugeDecimal size?: ButtonProps['size'] } export const PercentButton = ({ - label, loadingMax, percent, amount, setAmount, - decimals, + label = `${percent}%`, className, absoluteOffset, size, -}: PercentButtonProps) => ( - -) + } + pressed={ + // Only show as pressed if percent and amount are both zero or nonzero. + // If one is zero and the other is nonzero, the button should not be + // pressed. This ensures that the button doesn't show as pressed when + // the max is 0, since all percents of 0 are 0. + (percent === 0) === amount.isZero() && + !loadingMax.loading && + newAmount.eq(amount) + } + size={size} + variant="secondary" + > + {label} + + ) +} diff --git a/packages/stateless/components/modals/TokenDepositModal.stories.tsx b/packages/stateless/components/modals/TokenDepositModal.stories.tsx index 0c3b0093c..9971b14b5 100644 --- a/packages/stateless/components/modals/TokenDepositModal.stories.tsx +++ b/packages/stateless/components/modals/TokenDepositModal.stories.tsx @@ -1,6 +1,8 @@ import { ComponentMeta, ComponentStory } from '@storybook/react' import { useState } from 'react' +import { HugeDecimal } from '@dao-dao/math' + import { token } from '../token/TokenCard.stories' import { TokenDepositModal } from './TokenDepositModal' @@ -11,7 +13,7 @@ export default { } as ComponentMeta const Template: ComponentStory = (args) => { - const [amount, setAmount] = useState(1) + const [amount, setAmount] = useState(HugeDecimal.fromHumanReadable(1, 6)) return } diff --git a/packages/stateless/components/modals/TokenDepositModal.tsx b/packages/stateless/components/modals/TokenDepositModal.tsx index 2938dbba4..ecec59ef9 100644 --- a/packages/stateless/components/modals/TokenDepositModal.tsx +++ b/packages/stateless/components/modals/TokenDepositModal.tsx @@ -19,10 +19,10 @@ import { Modal } from './Modal' export type TokenDepositModalProps = Pick & { token: GenericToken loadingBalance: LoadingData - onDeposit: (amount: number) => void | Promise + onDeposit: (amount: HugeDecimal) => void | Promise loading: boolean - amount: number - setAmount: Dispatch> + amount: HugeDecimal + setAmount: Dispatch> connected: boolean ConnectWallet?: ComponentType subtitle?: string | ReactNode @@ -68,7 +68,7 @@ export const TokenDepositModal = ({ disabled } loading={loading} - onClick={() => amount > 0 && onDeposit(amount)} + onClick={() => amount.isPositive() && onDeposit(amount)} > {t('button.deposit')} @@ -122,10 +122,9 @@ export const TokenDepositModal = ({ min={min} onInput={(event) => setAmount( - Number( - Number((event.target as HTMLInputElement).value).toFixed( - token.decimals - ) + HugeDecimal.fromHumanReadable( + (event.target as HTMLInputElement).value, + token.decimals ) ) } @@ -135,10 +134,12 @@ export const TokenDepositModal = ({ onDeposit(amount) } }} - setValue={(_, value) => setAmount(value)} + setValue={(_, value) => + setAmount(HugeDecimal.fromHumanReadable(value, token.decimals)) + } step={min} unit={'$' + tokenSymbol} - value={amount} + value={amount.toHumanReadableNumber(token.decimals)} />
@@ -146,14 +147,18 @@ export const TokenDepositModal = ({ ))} diff --git a/packages/stateless/components/token/StakingModal.stories.tsx b/packages/stateless/components/token/StakingModal.stories.tsx index 6fd4211e4..7450cec84 100644 --- a/packages/stateless/components/token/StakingModal.stories.tsx +++ b/packages/stateless/components/token/StakingModal.stories.tsx @@ -14,14 +14,14 @@ export default { } as ComponentMeta const Template: ComponentStory = (args) => { - const [amount, setAmount] = useState(50) + const [amount, setAmount] = useState(HugeDecimal.fromHumanReadable(50, 6)) return } export const StakeUnstake = Template.bind({}) StakeUnstake.args = { - claimableTokens: 20, + claimableTokens: HugeDecimal.fromHumanReadable(20, 6), loading: false, initialMode: StakingMode.Stake, proposalDeposit: HugeDecimal.fromHumanReadable(5, 6), @@ -41,7 +41,7 @@ StakeUnstake.args = { export const Claim = Template.bind({}) Claim.args = { - claimableTokens: 20, + claimableTokens: HugeDecimal.fromHumanReadable(20, 6), loading: false, initialMode: StakingMode.Claim, proposalDeposit: HugeDecimal.fromHumanReadable(5, 6), diff --git a/packages/stateless/components/token/StakingModal.tsx b/packages/stateless/components/token/StakingModal.tsx index e27b339b4..e05bd4d3e 100644 --- a/packages/stateless/components/token/StakingModal.tsx +++ b/packages/stateless/components/token/StakingModal.tsx @@ -25,20 +25,14 @@ import { TokenAmountDisplay } from './TokenAmountDisplay' export const StakingModal = ({ initialMode, - // macrodenom amount, - // macrodenom setAmount, onClose, - // macrodenom claimableTokens, - // microdenom loadingStakableTokens, - // microdenom loadingUnstakableTokens, unstakingDuration, token, - // microdenom proposalDeposit, loading, error, @@ -89,15 +83,17 @@ export const StakingModal = ({ const invalidAmount = (): string | undefined => { if (mode === StakingMode.Claim) { - return claimableTokens > 0 ? undefined : t('error.cannotTxZeroTokens') + return claimableTokens.isPositive() + ? undefined + : t('error.cannotTxZeroTokens') } - if (amount <= 0) { + if (!amount.isPositive()) { return t('error.cannotTxZeroTokens') } if (maxTx === undefined) { return t('error.loadingData') } - if (amount > maxTx.toHumanReadableNumber(token.decimals)) { + if (amount.gt(maxTx)) { return t('error.cannotStakeMoreThanYouHave') } } @@ -235,7 +231,7 @@ export const StakingModal = ({ } mode={mode} proposalDeposit={proposalDeposit} - setAmount={(amount: number) => setAmount(amount)} + setAmount={setAmount} tokenDecimals={token.decimals} tokenSymbol={token.symbol} unstakingDuration={unstakingDuration} @@ -246,10 +242,10 @@ export const StakingModal = ({ } interface StakeUnstakeModesBodyProps { - amount: number + amount: HugeDecimal mode: StakingMode loadingMax: LoadingData - setAmount: (newAmount: number) => void + setAmount: (newAmount: HugeDecimal) => void tokenSymbol: string tokenDecimals: number unstakingDuration: Duration | null @@ -279,23 +275,26 @@ const StakeUnstakeModesBody = ({ ? undefined : loadingMax.data.toHumanReadableNumber(tokenDecimals) } - min={1 / 10 ** tokenDecimals} + min={HugeDecimal.one.toHumanReadableNumber(tokenDecimals)} onChange={(e: ChangeEvent) => - setAmount(e.target.valueAsNumber) + setAmount( + HugeDecimal.fromHumanReadable(e.target.value, tokenDecimals) + ) } plusMinusButtonSize="lg" - setValue={(_, value) => setAmount(value)} - step={1 / 10 ** tokenDecimals} + setValue={(_, value) => + setAmount(HugeDecimal.fromHumanReadable(value, tokenDecimals)) + } + step={HugeDecimal.one.toHumanReadableNumber(tokenDecimals)} textClassName="font-mono leading-5 symbol-small-body-text" unit={`$${tokenSymbol}`} - value={amount} + value={amount.toHumanReadableNumber(tokenDecimals)} /> - {!loadingMax.loading && - loadingMax.data.toHumanReadableNumber(tokenDecimals) < amount && ( - - {t('error.cannotStakeMoreThanYouHave')} - - )} + {!loadingMax.loading && loadingMax.data.lt(amount) && ( + + {t('error.cannotStakeMoreThanYouHave')} + + )} ))} @@ -332,10 +320,9 @@ const StakeUnstakeModesBody = ({ !loadingMax.loading && loadingMax.data.gt(proposalDeposit) && ( )} @@ -366,7 +344,7 @@ const StakeUnstakeModesBody = ({ ('height' in unstakingDuration ? unstakingDuration.height : unstakingDuration.time) > 0 && ( -
+

{t('title.unstakingPeriod') + `: ${convertDurationToHumanReadableString( @@ -389,7 +367,7 @@ const StakeUnstakeModesBody = ({ } interface ClaimModeBodyProps { - amount: number + amount: HugeDecimal tokenDecimals: number tokenSymbol: string } diff --git a/packages/types/components/StakingModal.ts b/packages/types/components/StakingModal.ts index d1a2891b1..19c8bd640 100644 --- a/packages/types/components/StakingModal.ts +++ b/packages/types/components/StakingModal.ts @@ -17,13 +17,13 @@ export interface StakingModalProps { // The mode to open the staking modal in. initialMode: StakingMode // The number of tokens in question. - amount: number + amount: HugeDecimal // Sets the number of tokens in question. - setAmount: (newAmount: number) => void + setAmount: (newAmount: HugeDecimal) => void // Called when the staking modal is closed. onClose: () => void // The number of tokens that are currently claimable. - claimableTokens: number + claimableTokens: HugeDecimal // The number of tokens that are unstakable. If undefined, will not be shown. // If `validatorPicker` is present, unstakable tokens will depend on the // chosen validator. @@ -43,7 +43,7 @@ export interface StakingModalProps { // Triggered when the stake / unstake / claim button is pressed. onAction: ( mode: StakingMode, - amount: number, + amount: HugeDecimal, validator?: string, // If mode is `StakingMode.Restake`, this will be the validator to unstake // funds from. From 1bf293e59e3816e6d22bbcc01c878b6981388d45 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Mon, 30 Sep 2024 19:01:25 -0400 Subject: [PATCH 13/26] created new HugeDecimalInput and started replacing NumberInputs with it --- packages/math/HugeDecimal.ts | 43 ++-- .../CreateRewardDistribution/Component.tsx | 40 ++- .../CreateRewardDistribution/README.md | 2 +- .../CreateRewardDistribution/index.tsx | 31 ++- .../components/inputs/HugeDecimalInput.tsx | 230 ++++++++++++++++++ .../components/inputs/PercentButton.tsx | 26 +- packages/stateless/components/inputs/index.ts | 1 + packages/types/components/HugeDecimalInput.ts | 72 ++++++ packages/types/components/index.ts | 1 + packages/utils/validation/index.ts | 10 +- 10 files changed, 383 insertions(+), 73 deletions(-) create mode 100644 packages/stateless/components/inputs/HugeDecimalInput.tsx create mode 100644 packages/types/components/HugeDecimalInput.ts diff --git a/packages/math/HugeDecimal.ts b/packages/math/HugeDecimal.ts index b91427efd..cbeeb1d7f 100644 --- a/packages/math/HugeDecimal.ts +++ b/packages/math/HugeDecimal.ts @@ -1,14 +1,14 @@ import { BigNumber } from 'bignumber.js' -const valueToBigNumber = (n: HugeDecimal.Value) => +const valueToBigNumber = (n: HugeDecimal.Value): BigNumber => n instanceof BigNumber ? n : n instanceof HugeDecimal - ? new BigNumber(n['value']) + ? n['value'] : typeof n === 'bigint' ? new BigNumber(n.toString()) : typeof n === 'object' && 'amount' in n - ? new BigNumber(n.amount) + ? valueToBigNumber(n.amount) : new BigNumber(n) interface AmountWrapper { @@ -36,33 +36,34 @@ export class HugeDecimal { * Returns a new instance of a HugeDecimal object with value `n`, where `n` is * a numeric value in base 10. * - * @param n + * @param n the value + * @returns a HugeDecimal instance */ constructor(n: HugeDecimal.Value) { this.value = valueToBigNumber(n) } /** - * Create a HugeDecimal from a value that is already in its base/raw format, - * which means it does not have decimals and describes an amount of some base - * unit. For example: `1000000untrn`. + * Returns a new instance of a HugeDecimal object with value `n`, where `n` is + * a numeric value in base 10. * * @param n the value * @returns a HugeDecimal instance */ static from(n: HugeDecimal.Value) { - const value = valueToBigNumber(n) - if (!value.isInteger()) { - throw new Error('Value is not an integer') + if (n instanceof HugeDecimal) { + return n } - - return new HugeDecimal(value) + return new HugeDecimal(n) } /** - * Create a HugeDecimal from a value that is already in human-readable format, - * which means it has decimals and describes a human-readable token amount. - * For example: `1.000000 $NTRN`. + * Create a HugeDecimal from a value that is in human-readable format, which + * means it has decimals and describes a human-readable token amount. For + * example: `1.000000 $NTRN`. + * + * This will convert the value to its raw integer representation by + * multiplying by 10^decimals and truncating any remaining decimal places. * * @param n the value * @param decimals the number of decimals @@ -70,11 +71,7 @@ export class HugeDecimal { */ static fromHumanReadable(n: HugeDecimal.Value, decimals: number) { // Multiply by 10^decimals to convert to the integer representation. - return new HugeDecimal( - valueToBigNumber(n) - .times(BigNumber(10).pow(decimals)) - .integerValue(BigNumber.ROUND_DOWN) - ) + return HugeDecimal.from(n).times(BigNumber(10).pow(decimals)).trunc() } /** @@ -218,7 +215,7 @@ export class HugeDecimal { */ toHumanReadable(decimals: number): HugeDecimal { return new HugeDecimal( - this.value.div(BigNumber(10).pow(decimals)).toFixed(decimals) + this.div(BigNumber(10).pow(decimals)).toFixed(decimals) ) } @@ -229,7 +226,7 @@ export class HugeDecimal { * @returns human-readable number */ toHumanReadableNumber(decimals: number): number { - return this.toHumanReadable(decimals).value.toNumber() + return this.toHumanReadable(decimals).toNumber() } /** @@ -239,7 +236,7 @@ export class HugeDecimal { * @returns human-readable string */ toHumanReadableString(decimals: number): string { - return this.toHumanReadable(decimals).value.toFormat(decimals) + return this.toHumanReadable(decimals).toString() } /** diff --git a/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx index e66c10a4d..1edbb2278 100644 --- a/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx @@ -3,10 +3,10 @@ import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { + HugeDecimalInput, InputErrorMessage, InputLabel, MarkdownRenderer, - NumberInput, PercentButton, SegmentedControls, SelectInput, @@ -42,7 +42,7 @@ export type CreateRewardDistributionData = { amount: number duration: DurationWithUnits } - initialFunds: number + initialFunds: string openFunding: boolean } @@ -65,7 +65,7 @@ export const CreateRewardDistributionComponent: ActionComponent< address, chain: { bech32_prefix: bech32Prefix }, } = useActionOptions() - const { register, setValue, watch } = + const { register, setValue, watch, getValues } = useFormContext() const denomOrAddress = watch( @@ -86,8 +86,6 @@ export const CreateRewardDistributionComponent: ActionComponent< : tokens.data.find((t) => tokensEqual(t.token, token.data)) const decimals = selectedToken?.token.decimals ?? 0 - const minAmount = HugeDecimal.one.toHumanReadableNumber(decimals) - const selectedBalance = HugeDecimal.from(selectedToken?.balance ?? 0) const warning = !isCreating || @@ -195,23 +193,23 @@ export const CreateRewardDistributionComponent: ActionComponent< /> {!immediate && ( -

- +
@@ -219,20 +217,21 @@ export const CreateRewardDistributionComponent: ActionComponent<
-
@@ -266,19 +265,19 @@ export const CreateRewardDistributionComponent: ActionComponent< {t('info.initialRewardsFundsDescription')}

- @@ -296,7 +295,7 @@ export const CreateRewardDistributionComponent: ActionComponent< onClick={() => setValue( (fieldNamePrefix + 'initialFunds') as 'initialFunds', - selectedBalance.toHumanReadableNumber(decimals) + selectedBalance.toHumanReadableString(decimals) ) } showFullAmount @@ -309,16 +308,13 @@ export const CreateRewardDistributionComponent: ActionComponent< {[10, 25, 50, 75, 100].map((percent) => ( setValue( (fieldNamePrefix + 'initialFunds') as 'initialFunds', - amount.toHumanReadableNumber(selectedToken.token.decimals) + amount.toHumanReadableString(decimals) ) } /> diff --git a/packages/stateful/actions/core/actions/CreateRewardDistribution/README.md b/packages/stateful/actions/core/actions/CreateRewardDistribution/README.md index 491e02692..c0c0e5b8b 100644 --- a/packages/stateful/actions/core/actions/CreateRewardDistribution/README.md +++ b/packages/stateful/actions/core/actions/CreateRewardDistribution/README.md @@ -22,7 +22,7 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). "amount": , "unit": "" }, - "initialFunds": , + "initialFunds": "", "openFunding": } ``` diff --git a/packages/stateful/actions/core/actions/CreateRewardDistribution/index.tsx b/packages/stateful/actions/core/actions/CreateRewardDistribution/index.tsx index 803879911..96d0c1d24 100644 --- a/packages/stateful/actions/core/actions/CreateRewardDistribution/index.tsx +++ b/packages/stateful/actions/core/actions/CreateRewardDistribution/index.tsx @@ -130,7 +130,7 @@ export class CreateRewardDistributionAction extends ActionBase { if (this.options.context.type !== ActionContextType.Dao) { @@ -168,6 +168,11 @@ export class CreateRewardDistributionAction extends ActionBase +>({ + fieldName, + register, + error, + validation, + hidePlusMinus, + numericValue, + value, + getValues, + setValue, + disabled, + sizing, + className, + containerClassName, + required, + ghost, + unit, + unitIconUrl, + textClassName, + unitClassName, + unitIconClassName, + unitContainerClassName, + plusMinusButtonSize = 'sm', + ...props +}: HugeDecimalInputProps) => { + const { t } = useTranslation() + const validate = validation?.reduce( + (a, v) => ({ ...a, [v.toString()]: v }), + {} + ) + + return ( +
+ {!hidePlusMinus && !disabled && setValue && ( +
+ {/* Minus button */} + + setValue( + fieldName ?? '', + HugeDecimal.min( + HugeDecimal.max( + ...(props.min !== undefined ? [props.min] : []), + // Subtract 1 whole number and truncate. + HugeDecimal.from( + (fieldName && getValues ? getValues(fieldName) : value) || + 0 + ) + .minus(1) + .trunc() + ), + ...(props.max !== undefined ? [props.max] : []) + ).toString(), + { + shouldValidate: true, + } + ) + } + size={ + // The larger button size for this NumberInput corresponds to the + // default icon button size. + plusMinusButtonSize === 'lg' ? 'default' : plusMinusButtonSize + } + variant="ghost" + /> + + + setValue( + fieldName ?? '', + HugeDecimal.min( + HugeDecimal.max( + ...(props.min !== undefined ? [props.min] : []), + // Add 1 whole number and truncate. + HugeDecimal.from( + (fieldName && getValues ? getValues(fieldName) : value) || + 0 + ) + .plus(1) + .trunc() + ), + ...(props.max !== undefined ? [props.max] : []) + ).toString(), + { + shouldValidate: true, + } + ) + } + size={ + // The larger button size for this NumberInput corresponds to the + // default icon button size. + plusMinusButtonSize === 'lg' ? 'default' : plusMinusButtonSize + } + variant="ghost" + /> +
+ )} + + { + const value = (target as HTMLInputElement).value + setValue( + fieldName ?? '', + numericValue + ? // Treat empty strings as NaN when manually setting value. + value.trim() === '' + ? NaN + : Number(value) + : value + ) + } + } + type="number" + value={value} + {...props} + {...(fieldName && + register?.(fieldName, { + required: required && t('info.required'), + validate, + valueAsNumber: numericValue, + }))} + /> + + {(unit || unitIconUrl) && ( +
+ {unitIconUrl && ( +
+ )} + +

+ {unit} +

+
+ )} +
+ ) +} diff --git a/packages/stateless/components/inputs/PercentButton.tsx b/packages/stateless/components/inputs/PercentButton.tsx index d0315a2de..632d43e2a 100644 --- a/packages/stateless/components/inputs/PercentButton.tsx +++ b/packages/stateless/components/inputs/PercentButton.tsx @@ -34,25 +34,25 @@ export const PercentButton = ({ }: PercentButtonProps) => { const newAmount = loadingMax.loading ? HugeDecimal.zero - : loadingMax.data - .times(percent) - .div(100) - .plus(absoluteOffset || 0) + : // Cap between 1 and max + HugeDecimal.min( + HugeDecimal.max( + HugeDecimal.one, + loadingMax.data + .times(percent) + .div(100) + .plus(absoluteOffset || 0) + .toFixed(0) + ), + loadingMax.data + ) return (
)} diff --git a/packages/stateful/actions/core/actions/ConfigureRebalancer/Component.tsx b/packages/stateful/actions/core/actions/ConfigureRebalancer/Component.tsx index 60ee4e730..8e738a68a 100644 --- a/packages/stateful/actions/core/actions/ConfigureRebalancer/Component.tsx +++ b/packages/stateful/actions/core/actions/ConfigureRebalancer/Component.tsx @@ -7,12 +7,12 @@ import { HugeDecimal } from '@dao-dao/math' import { Button, ErrorPage, + HugeDecimalInput, IconButton, InputErrorMessage, InputLabel, Loader, MarkdownRenderer, - NumberInput, RebalancerProjector, RebalancerProjectorAsset, SegmentedControls, @@ -124,8 +124,15 @@ export const ConfigureRebalancerComponent: ActionComponent< chain: { bech32_prefix: bech32Prefix }, } = useSupportedChainContext() - const { control, watch, register, setValue, clearErrors, setError } = - useFormContext() + const { + control, + watch, + register, + setValue, + getValues, + clearErrors, + setError, + } = useFormContext() const { fields: tokensFields, append: appendToken, @@ -446,12 +453,13 @@ export const ConfigureRebalancerComponent: ActionComponent<
{/* eslint-disable-next-line i18next/no-literal-string */} - {/* eslint-disable-next-line i18next/no-literal-string */} - {/* eslint-disable-next-line i18next/no-literal-string */} - -
)} diff --git a/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx index 1edbb2278..0dab2ce27 100644 --- a/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx @@ -39,7 +39,7 @@ export type CreateRewardDistributionData = { denomOrAddress: string immediate: boolean rate: { - amount: number + amount: string duration: DurationWithUnits } initialFunds: string diff --git a/packages/stateful/actions/core/actions/CreateRewardDistribution/README.md b/packages/stateful/actions/core/actions/CreateRewardDistribution/README.md index c0c0e5b8b..5ac8c5289 100644 --- a/packages/stateful/actions/core/actions/CreateRewardDistribution/README.md +++ b/packages/stateful/actions/core/actions/CreateRewardDistribution/README.md @@ -19,7 +19,7 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). "denomOrAddress": "", "immediate": , "rate": { - "amount": , + "amount": "", "unit": "" }, "initialFunds": "", diff --git a/packages/stateful/actions/core/actions/CreateRewardDistribution/index.tsx b/packages/stateful/actions/core/actions/CreateRewardDistribution/index.tsx index 96d0c1d24..94004e80e 100644 --- a/packages/stateful/actions/core/actions/CreateRewardDistribution/index.tsx +++ b/packages/stateful/actions/core/actions/CreateRewardDistribution/index.tsx @@ -124,7 +124,7 @@ export class CreateRewardDistributionAction extends ActionBase { const { t } = useTranslation() - const { register, setValue, watch } = + const { register, setValue, watch, getValues } = useFormContext() const address = watch((fieldNamePrefix + 'address') as 'address') @@ -160,17 +160,19 @@ export const FundRewardDistributionComponent: ActionComponent<
- @@ -188,7 +190,7 @@ export const FundRewardDistributionComponent: ActionComponent< onClick={() => setValue( (fieldNamePrefix + 'amount') as 'amount', - selectedBalance.toHumanReadableNumber( + selectedBalance.toHumanReadableString( selectedDistribution.token.decimals ) ) @@ -212,7 +214,7 @@ export const FundRewardDistributionComponent: ActionComponent< setAmount={(amount) => setValue( (fieldNamePrefix + 'amount') as 'amount', - amount.toHumanReadableNumber( + amount.toHumanReadableString( selectedDistribution.token.decimals ) ) diff --git a/packages/stateful/actions/core/actions/FundRewardDistribution/index.tsx b/packages/stateful/actions/core/actions/FundRewardDistribution/index.tsx index 5ad0e9c5a..522d1feb8 100644 --- a/packages/stateful/actions/core/actions/FundRewardDistribution/index.tsx +++ b/packages/stateful/actions/core/actions/FundRewardDistribution/index.tsx @@ -133,7 +133,7 @@ export class FundRewardDistributionAction extends ActionBase = ({
- = ({
- { const { t } = useTranslation() - const { register, watch, setError, clearErrors, setValue } = + const { register, watch, setError, clearErrors, setValue, getValues } = useFormContext() const stakeActions = getStakeActions(t) @@ -176,7 +176,9 @@ export const ManageStakingComponent: ActionComponent< // Logic for undelegating. if (type === StakingActionType.Undelegate) { return ( - sourceValidatorStaked.gte(amount) || + sourceValidatorStaked + .toHumanReadable(nativeToken.decimals) + .gte(amount) || (sourceValidatorStaked.isZero() ? t('error.nothingStaked') : t('error.stakeInsufficient', { @@ -200,7 +202,9 @@ export const ManageStakingComponent: ActionComponent< } return ( - sourceValidatorStaked.gte(amount) || + sourceValidatorStaked + .toHumanReadable(nativeToken.decimals) + .gte(amount) || (sourceValidatorStaked.isZero() ? t('error.nothingStaked') : t('error.stakeInsufficient', { @@ -336,18 +340,18 @@ export const ManageStakingComponent: ActionComponent< {/* If not withdrawing reward or updating withdraw address, show amount input. */} {type !== StakingActionType.WithdrawDelegatorReward && type !== StakingActionType.SetWithdrawAddress && ( - )}
@@ -399,6 +403,15 @@ export const ManageStakingComponent: ActionComponent< } decimals={nativeToken.decimals} iconUrl={nativeToken.imageUrl} + onClick={ + type !== StakingActionType.WithdrawDelegatorReward + ? () => + setValue( + (fieldNamePrefix + 'amount') as 'amount', + maxAmount.toHumanReadableString(nativeToken.decimals) + ) + : undefined + } showFullAmount symbol={nativeToken.symbol} /> diff --git a/packages/stateful/actions/core/actions/ManageStaking/index.tsx b/packages/stateful/actions/core/actions/ManageStaking/index.tsx index 8cc33d253..ecddc2a46 100644 --- a/packages/stateful/actions/core/actions/ManageStaking/index.tsx +++ b/packages/stateful/actions/core/actions/ManageStaking/index.tsx @@ -1,4 +1,4 @@ -import { coin, parseCoins } from '@cosmjs/amino' +import { parseCoins } from '@cosmjs/amino' import { useQueryClient } from '@tanstack/react-query' import { useFormContext } from 'react-hook-form' import { useTranslation } from 'react-i18next' @@ -313,7 +313,7 @@ export class ManageStakingAction extends ActionBase { // Default to first validator if exists. validator: firstValidator, toValidator: '', - amount: 1, + amount: '1', withdrawAddress: this.options.address, } } @@ -331,13 +331,10 @@ export class ManageStakingAction extends ActionBase { chainId ) const nativeToken = getNativeTokenForChainId(chainId) - const amount = coin( - HugeDecimal.fromHumanReadable( - macroAmount, - nativeToken.decimals - ).toString(), - nativeToken.denomOrAddress - ) + const amount = HugeDecimal.fromHumanReadable( + macroAmount, + nativeToken.decimals + ).toCoin(nativeToken.denomOrAddress) let msg: UnifiedCosmosMsg switch (type) { @@ -503,7 +500,7 @@ export class ManageStakingAction extends ActionBase { decodedMessage.distribution.withdraw_delegator_reward.validator, // Default values, not needed for displaying this type of message. toValidator: '', - amount: 1, + amount: '1', withdrawAddress: '', } } else if ( @@ -516,7 +513,7 @@ export class ManageStakingAction extends ActionBase { decodedMessage.distribution.set_withdraw_address.address, validator: '', toValidator: '', - amount: 1, + amount: '1', } } } else if ('staking' in decodedMessage) { @@ -541,7 +538,7 @@ export class ManageStakingAction extends ActionBase { action.type === StakingActionType.Redelegate ? data.dst_validator : '', - amount: HugeDecimal.from(data.amount.amount).toHumanReadableNumber( + amount: HugeDecimal.from(data.amount.amount).toHumanReadableString( nativeToken.decimals ), withdrawAddress: '', diff --git a/packages/stateful/actions/core/actions/ManageSubDaoPause/Component.tsx b/packages/stateful/actions/core/actions/ManageSubDaoPause/Component.tsx index 5bece8211..83269ff9d 100644 --- a/packages/stateful/actions/core/actions/ManageSubDaoPause/Component.tsx +++ b/packages/stateful/actions/core/actions/ManageSubDaoPause/Component.tsx @@ -4,10 +4,10 @@ import { useTranslation } from 'react-i18next' import { FilterableItemPopup, + HugeDecimalInput, InputErrorMessage, InputLabel, Loader, - NumberInput, SegmentedControlsTitle, useActionOptions, } from '@dao-dao/stateless' @@ -35,7 +35,8 @@ export const ManageSubDaoPauseComponent: ActionComponent< options: { neutronSubdaos, EntityDisplay }, }) => { const { t } = useTranslation() - const { register, watch, setValue } = useFormContext() + const { register, watch, setValue, getValues } = + useFormContext() const { address: daoAddress } = useActionOptions() const address = watch((fieldNamePrefix + 'address') as 'address') @@ -112,23 +113,25 @@ export const ManageSubDaoPauseComponent: ActionComponent<
- diff --git a/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx b/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx index 07c3c390d..afd24e57d 100644 --- a/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx +++ b/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx @@ -18,10 +18,10 @@ import { CopyToClipboard, DateTimePicker, DateTimePickerNoForm, + HugeDecimalInput, IconButton, InputErrorMessage, InputLabel, - NumberInput, RadioInput, RadioInputOption, SelectInput, @@ -133,8 +133,15 @@ export const BeginVesting: ActionComponent = ({ throw new Error('Unsupported chain context') } - const { control, register, watch, setValue, setError, clearErrors } = - useFormContext() + const { + control, + register, + watch, + setValue, + getValues, + setError, + clearErrors, + } = useFormContext() const { fields: stepFields, append: appendStep, @@ -603,22 +610,23 @@ export const BeginVesting: ActionComponent = ({
- @@ -637,14 +645,16 @@ export const BeginVesting: ActionComponent = ({
- = ({ count: steps[index].delay.value, }).toLocaleLowerCase() } - validation={[validatePositive, validateRequired]} - watch={watch} + validation={[validateRequired, validatePositive]} /> {isCreating && ( diff --git a/packages/stateful/actions/core/actions/Migrate/Component.tsx b/packages/stateful/actions/core/actions/Migrate/Component.tsx index 58235ed38..a67b6eb97 100644 --- a/packages/stateful/actions/core/actions/Migrate/Component.tsx +++ b/packages/stateful/actions/core/actions/Migrate/Component.tsx @@ -4,9 +4,9 @@ import { useTranslation } from 'react-i18next' import { AddressInput, CodeMirrorInput, + HugeDecimalInput, InputErrorMessage, InputLabel, - NumberInput, StatusCard, useActionOptions, useChain, @@ -72,13 +72,16 @@ export const MigrateContractComponent: ActionComponent = ({
- diff --git a/packages/stateful/actions/core/actions/Spend/Component.tsx b/packages/stateful/actions/core/actions/Spend/Component.tsx index 1cddd1ed1..a25a0ccda 100644 --- a/packages/stateful/actions/core/actions/Spend/Component.tsx +++ b/packages/stateful/actions/core/actions/Spend/Component.tsx @@ -12,12 +12,12 @@ import { ChainLogo, ChainProvider, FormSwitchCard, + HugeDecimalInput, IbcDestinationChainPicker, InputErrorMessage, InputLabel, InputThemedText, Loader, - NumberInput, PercentButton, SelectInput, StatusCard, @@ -169,7 +169,7 @@ export const SpendComponent: ActionComponent = ({ chain: { chain_id: mainChainId }, } = useActionOptions() - const { register, watch, setValue } = useFormContext() + const { register, watch, setValue, getValues } = useFormContext() const spendChainId = watch((fieldNamePrefix + 'fromChainId') as 'fromChainId') const spendAmount = watch((fieldNamePrefix + 'amount') as 'amount') @@ -795,14 +795,16 @@ export const SpendComponent: ActionComponent = ({ {isCreating ? ( <>
- = ({ count: ibcTimeout?.value, }).toLocaleLowerCase() } - validation={[validatePositive, validateRequired]} - watch={watch} + validation={[validateRequired, validatePositive]} /> {isCreating && ( diff --git a/packages/stateful/actions/core/actions/UpdateRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/UpdateRewardDistribution/Component.tsx index af5df7561..a4e4fac13 100644 --- a/packages/stateful/actions/core/actions/UpdateRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/UpdateRewardDistribution/Component.tsx @@ -5,11 +5,11 @@ import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { FilterableItemPopup, + HugeDecimalInput, InputErrorMessage, InputLabel, InputThemedText, MarkdownRenderer, - NumberInput, SegmentedControls, SelectInput, StatusCard, @@ -35,7 +35,7 @@ export type UpdateRewardDistributionData = { id: number immediate: boolean rate: { - amount: number + amount: string duration: DurationWithUnits } openFunding?: boolean | null @@ -52,7 +52,7 @@ export const UpdateRewardDistributionComponent: ActionComponent< UpdateRewardDistributionOptions > = ({ fieldNamePrefix, errors, isCreating, options: { distributions } }) => { const { t } = useTranslation() - const { register, setValue, watch } = + const { register, setValue, getValues, watch } = useFormContext() const address = watch((fieldNamePrefix + 'address') as 'address') @@ -67,9 +67,7 @@ export const UpdateRewardDistributionComponent: ActionComponent< (distribution) => distribution.address === address && distribution.id === id ) - const minAmount = HugeDecimal.one.toHumanReadableNumber( - selectedDistribution?.token.decimals ?? 0 - ) + const decimals = selectedDistribution?.token.decimals ?? 0 const selectedDistributionDisplay = selectedDistribution && ( <> @@ -116,7 +114,7 @@ export const UpdateRewardDistributionComponent: ActionComponent< (fieldNamePrefix + 'rate.amount') as 'rate.amount', HugeDecimal.from( active_epoch.emission_rate.linear.amount - ).toHumanReadableNumber(token.decimals) + ).toHumanReadableString(token.decimals) ) setValue( (fieldNamePrefix + 'rate.duration') as 'rate.duration', @@ -190,22 +188,22 @@ export const UpdateRewardDistributionComponent: ActionComponent< {!immediate && (
-
@@ -213,20 +211,21 @@ export const UpdateRewardDistributionComponent: ActionComponent<
- , "immediate": , "rate": { - "amount": , + "amount": "", "unit": "" }, "openFunding": diff --git a/packages/stateful/actions/core/actions/UpdateRewardDistribution/index.tsx b/packages/stateful/actions/core/actions/UpdateRewardDistribution/index.tsx index 11486c9e9..822f20a9e 100644 --- a/packages/stateful/actions/core/actions/UpdateRewardDistribution/index.tsx +++ b/packages/stateful/actions/core/actions/UpdateRewardDistribution/index.tsx @@ -100,8 +100,8 @@ export class UpdateRewardDistributionAction extends ActionBase) => { const { t } = useTranslation() @@ -45,17 +45,18 @@ const ActiveThresholdInput = ({ {enabled && ( <>
- { const { t } = useTranslation() - const { register, watch, setValue } = + const { register, watch, setValue, getValues } = useFormContext() const unstakingDurationEnabled = watch( @@ -57,14 +57,16 @@ export const UpdateStakingConfigComponent: ActionComponent = ({ {unstakingDurationEnabled && ( <>
- {isCreating && ( diff --git a/packages/stateless/components/dao/create/DaoCreateConfigInputCard.stories.tsx b/packages/stateless/components/dao/create/DaoCreateConfigInputCard.stories.tsx index 7d74a074a..f62fa653a 100644 --- a/packages/stateless/components/dao/create/DaoCreateConfigInputCard.stories.tsx +++ b/packages/stateless/components/dao/create/DaoCreateConfigInputCard.stories.tsx @@ -17,7 +17,7 @@ export default { } as ComponentMeta const Template: ComponentStory = (args) => { - const { register, watch, setValue } = useForm({ + const { register, watch, setValue, getValues } = useForm({ defaultValues: makeDefaultNewDao(CHAIN_ID), mode: 'onChange', }) @@ -32,6 +32,22 @@ const Template: ComponentStory = (args) => { + fieldNameOrNames === undefined + ? getValues() + : typeof fieldNameOrNames === 'string' + ? getValues( + ('proposalModuleAdapters.0.data.' + + fieldNameOrNames) as `proposalModuleAdapters.${number}.data.${string}` + ) + : getValues( + fieldNameOrNames.map( + (fieldName) => + ('proposalModuleAdapters.0.data.' + + fieldName) as `proposalModuleAdapters.${number}.data.${string}` + ) + ) + } newDao={newDao} register={(fieldName, options) => register( diff --git a/packages/stateless/components/dao/create/pages/CreateDaoVoting.tsx b/packages/stateless/components/dao/create/pages/CreateDaoVoting.tsx index 4b1c22d25..2b8c6a611 100644 --- a/packages/stateless/components/dao/create/pages/CreateDaoVoting.tsx +++ b/packages/stateless/components/dao/create/pages/CreateDaoVoting.tsx @@ -14,6 +14,7 @@ export const CreateDaoVoting = ({ register, watch, setValue, + getValues, }, commonVotingConfig: { items: commonVotingConfigItems, @@ -94,6 +95,24 @@ export const CreateDaoVoting = ({ data={creatorData} errors={errors.creator?.data} fieldNamePrefix="creator.data." + getValues={( + fieldNameOrNames?: string | readonly string[] + ) => + fieldNameOrNames === undefined + ? getValues() + : typeof fieldNameOrNames === 'string' + ? getValues( + ('creator.data.' + + fieldNameOrNames) as `creator.data.${string}` + ) + : getValues( + fieldNameOrNames.map( + (fieldName) => + ('creator.data.' + + fieldName) as `creator.data.${string}` + ) + ) + } newDao={newDao} register={(fieldName, options) => register( @@ -155,6 +174,24 @@ export const CreateDaoVoting = ({ data={proposalModuleAdapters[index].data} errors={errors.proposalModuleAdapters?.[index]?.data} fieldNamePrefix={`proposalModuleAdapters.${index}.data.`} + getValues={( + fieldNameOrNames?: string | readonly string[] + ) => + fieldNameOrNames === undefined + ? getValues() + : typeof fieldNameOrNames === 'string' + ? getValues( + (`proposalModuleAdapters.${index}.data.` + + fieldNameOrNames) as `proposalModuleAdapters.${number}.data.${string}` + ) + : getValues( + fieldNameOrNames.map( + (fieldName) => + (`proposalModuleAdapters.${index}.data.` + + fieldName) as `proposalModuleAdapters.${number}.data.${string}` + ) + ) + } newDao={newDao} register={(fieldName, options) => register( @@ -213,6 +250,24 @@ export const CreateDaoVoting = ({ data={votingConfig} errors={errors.votingConfig} fieldNamePrefix="votingConfig." + getValues={( + fieldNameOrNames?: string | readonly string[] + ) => + fieldNameOrNames === undefined + ? getValues() + : typeof fieldNameOrNames === 'string' + ? getValues( + ('votingConfig.' + + fieldNameOrNames) as `votingConfig.${string}` + ) + : getValues( + fieldNameOrNames.map( + (fieldName) => + ('votingConfig.' + + fieldName) as `votingConfig.${string}` + ) + ) + } newDao={newDao} register={(fieldName, options) => register(('votingConfig.' + fieldName) as any, options) @@ -317,6 +372,24 @@ export const CreateDaoVoting = ({ data={creatorData} errors={errors.creator?.data} fieldNamePrefix="creator.data." + getValues={( + fieldNameOrNames?: string | readonly string[] + ) => + fieldNameOrNames === undefined + ? getValues() + : typeof fieldNameOrNames === 'string' + ? getValues( + ('creator.data.' + + fieldNameOrNames) as `creator.data.${string}` + ) + : getValues( + fieldNameOrNames.map( + (fieldName) => + ('creator.data.' + + fieldName) as `creator.data.${string}` + ) + ) + } newDao={newDao} register={(fieldName, options) => register( @@ -380,6 +453,24 @@ export const CreateDaoVoting = ({ errors.proposalModuleAdapters?.[index]?.data } fieldNamePrefix={`proposalModuleAdapters.${index}.data.`} + getValues={( + fieldNameOrNames?: string | readonly string[] + ) => + fieldNameOrNames === undefined + ? getValues() + : typeof fieldNameOrNames === 'string' + ? getValues( + (`proposalModuleAdapters.${index}.data.` + + fieldNameOrNames) as `proposalModuleAdapters.${number}.data.${string}` + ) + : getValues( + fieldNameOrNames.map( + (fieldName) => + (`proposalModuleAdapters.${index}.data.` + + fieldName) as `proposalModuleAdapters.${number}.data.${string}` + ) + ) + } newDao={newDao} register={(fieldName, options) => register( @@ -438,6 +529,24 @@ export const CreateDaoVoting = ({ data={votingConfig} errors={errors.votingConfig} fieldNamePrefix="votingConfig." + getValues={( + fieldNameOrNames?: string | readonly string[] + ) => + fieldNameOrNames === undefined + ? getValues() + : typeof fieldNameOrNames === 'string' + ? getValues( + ('votingConfig.' + + fieldNameOrNames) as `votingConfig.${string}` + ) + : getValues( + fieldNameOrNames.map( + (fieldName) => + ('votingConfig.' + + fieldName) as `votingConfig.${string}` + ) + ) + } newDao={newDao} register={(fieldName, options) => register( diff --git a/packages/stateless/components/inputs/HugeDecimalInput.tsx b/packages/stateless/components/inputs/HugeDecimalInput.tsx index aa6cb3db7..921635ac5 100644 --- a/packages/stateless/components/inputs/HugeDecimalInput.tsx +++ b/packages/stateless/components/inputs/HugeDecimalInput.tsx @@ -1,5 +1,6 @@ import { Add, Remove } from '@mui/icons-material' import clsx from 'clsx' +import { useRef } from 'react' import { FieldValues, Path } from 'react-hook-form' import { useTranslation } from 'react-i18next' @@ -61,6 +62,8 @@ export const HugeDecimalInput = < {} ) + const lastValueSet = useRef('') + return (
{ + : setValue && + (({ target }) => { const value = (target as HTMLInputElement).value + + // If a decimal point is entered, and we already set the same + // value without a decimal point, don't set again. We don't want + // to clear the decimal point in the case that this input is + // controlled and the parent component transforms the value + // manually into a number and back (which would clear the + // decimal point). + if (value === lastValueSet.current + '.') { + return + } + + lastValueSet.current = value setValue( fieldName ?? '', numericValue @@ -182,7 +198,7 @@ export const HugeDecimalInput = < : Number(value) : value ) - } + }) } type="number" value={value} diff --git a/packages/stateless/components/modals/TokenDepositModal.tsx b/packages/stateless/components/modals/TokenDepositModal.tsx index ecec59ef9..2d47879a2 100644 --- a/packages/stateless/components/modals/TokenDepositModal.tsx +++ b/packages/stateless/components/modals/TokenDepositModal.tsx @@ -12,7 +12,7 @@ import { import { shortenTokenSymbol } from '@dao-dao/utils' import { Button } from '../buttons/Button' -import { NumberInput, PercentButton } from '../inputs' +import { HugeDecimalInput, PercentButton } from '../inputs' import { TokenAmountDisplay } from '../token/TokenAmountDisplay' import { Modal } from './Modal' @@ -114,20 +114,12 @@ export const TokenDepositModal = ({
)} - - setAmount( - HugeDecimal.fromHumanReadable( - (event.target as HTMLInputElement).value, - token.decimals - ) - ) - } onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault() @@ -139,7 +131,7 @@ export const TokenDepositModal = ({ } step={min} unit={'$' + tokenSymbol} - value={amount.toHumanReadableNumber(token.decimals)} + value={amount.toHumanReadableString(token.decimals)} />
diff --git a/packages/stateless/components/proposal/ProposalVetoConfigurer.tsx b/packages/stateless/components/proposal/ProposalVetoConfigurer.tsx index 865649231..b736336d2 100644 --- a/packages/stateless/components/proposal/ProposalVetoConfigurer.tsx +++ b/packages/stateless/components/proposal/ProposalVetoConfigurer.tsx @@ -22,9 +22,9 @@ import { Button } from '../buttons' import { IconButton } from '../icon_buttons' import { FormSwitchCard, + HugeDecimalInput, InputErrorMessage, InputLabel, - NumberInput, SelectInput, } from '../inputs' @@ -52,7 +52,7 @@ export const ProposalVetoConfigurer = ({ const { t } = useTranslation() const { bech32_prefix: bech32Prefix } = useChain() - const { control, register, setValue, watch } = + const { control, register, setValue, getValues } = useFormContext() const { @@ -173,7 +173,7 @@ export const ProposalVetoConfigurer = ({ />
- setValue: UseFormSetValue + getValues: UseFormGetValues watch: >( name: TFieldName, defaultValue?: FieldPathValue diff --git a/packages/utils/validation/index.ts b/packages/utils/validation/index.ts index 7ef5d33a1..92a678fd0 100644 --- a/packages/utils/validation/index.ts +++ b/packages/utils/validation/index.ts @@ -24,10 +24,10 @@ export const validateRequired = (v: any) => { } export const validatePositive = (v: HugeDecimal.Value | undefined) => - (v && HugeDecimal.from(v).isPositive()) || 'Must be positive' + (v !== undefined && HugeDecimal.from(v).isPositive()) || 'Must be positive' export const validateNonNegative = (v: HugeDecimal.Value | undefined) => - (v && HugeDecimal.from(v).gte(0)) || 'Must be 0 or more' + (v !== undefined && HugeDecimal.from(v).gte(0)) || 'Must be 0 or more' export const validatePercent = (v: string | number | undefined) => { const p = v ? Number(v) : NaN From 4ac4e925355f66bddbafe548dd76da5cc33c3802 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Tue, 1 Oct 2024 17:41:54 -0400 Subject: [PATCH 17/26] replace even more NumberInput with HugeDecimalInput --- .../stateful/components/dao/CreateDaoForm.tsx | 16 +++--- .../QuorumVotingConfigItem.tsx | 13 +++-- .../VotingDurationVotingConfigItem.tsx | 13 +++-- .../gov/GovProposalStatusAndInfo.tsx | 48 ++++++++--------- .../GovernanceConfigurationInput.tsx | 2 + .../creators/MembershipBased/TierCard.tsx | 10 ++-- .../UnstakingDurationVotingConfigItem.tsx | 9 ++-- .../GovernanceConfigurationInput.tsx | 19 ++++--- .../stateful/creators/TokenBased/TierCard.tsx | 34 ++++++++----- .../UnstakingDurationVotingConfigItem.tsx | 8 +-- .../creators/TokenBased/getInstantiateInfo.ts | 15 +++--- .../stateful/creators/TokenBased/index.ts | 4 +- .../stateful/creators/TokenBased/types.ts | 4 +- .../UpdateProposalConfigComponent.tsx | 23 ++++++--- .../UpdateProposalConfigComponent.stories.tsx | 2 +- .../UpdateProposalConfigComponent.tsx | 51 +++++++++++++------ .../actions/UpdateProposalConfigV1/index.tsx | 6 +-- .../UpdateProposalConfigComponent.tsx | 35 ++++++++----- .../daoCreation/ThresholdVotingConfigItem.tsx | 16 +++--- .../actions/Mint/MintComponent.stories.tsx | 2 +- .../actions/Mint/MintComponent.tsx | 21 ++++---- .../actions/Mint/index.tsx | 4 +- .../actions/UpdateStakingConfig/Component.tsx | 9 ++-- .../actions/UpdateStakingConfig/README.md | 2 +- .../actions/ManageMembers/Component.tsx | 9 ++-- .../actions/UpdateStakingConfig/Component.tsx | 9 ++-- .../actions/UpdateStakingConfig/README.md | 2 +- .../actions/Mint/MintComponent.stories.tsx | 2 +- .../actions/Mint/MintComponent.tsx | 12 ++--- .../actions/Mint/index.tsx | 4 +- .../actions/UpdateStakingConfig/Component.tsx | 9 ++-- .../actions/UpdateStakingConfig/README.md | 2 +- .../actions/UpdateStakingConfig/Component.tsx | 9 ++-- .../actions/UpdateStakingConfig/README.md | 2 +- .../actions/Mint/BitSongFantokenMintAction.ts | 18 +++---- .../actions/Mint/MintAction.ts | 4 +- .../actions/Mint/MintComponent.stories.tsx | 2 +- .../actions/Mint/MintComponent.tsx | 10 ++-- ...ories.tsx => HugeDecimalInput.stories.tsx} | 17 ++++--- 39 files changed, 276 insertions(+), 201 deletions(-) rename packages/stateless/components/inputs/{NumberInput.stories.tsx => HugeDecimalInput.stories.tsx} (59%) diff --git a/packages/stateful/components/dao/CreateDaoForm.tsx b/packages/stateful/components/dao/CreateDaoForm.tsx index 505ed7176..3b8175222 100644 --- a/packages/stateful/components/dao/CreateDaoForm.tsx +++ b/packages/stateful/components/dao/CreateDaoForm.tsx @@ -787,18 +787,18 @@ export const InnerCreateDaoForm = ({ tokenBalance: daoVotingTokenBasedCreatorData.govTokenType === GovernanceTokenType.New - ? daoVotingTokenBasedCreatorData.newInfo.initialSupply + ? HugeDecimal.fromHumanReadable( + daoVotingTokenBasedCreatorData.newInfo.initialSupply, + NEW_DAO_TOKEN_DECIMALS + ) : // If using existing token but no token info loaded (should // be impossible), just display 0. !daoVotingTokenBasedCreatorData.existingToken || daoVotingTokenBasedCreatorData.existingTokenSupply === undefined - ? 0 - : // If using existing token, convert supply from query using decimals. - HugeDecimal.from( + ? HugeDecimal.zero + : HugeDecimal.from( daoVotingTokenBasedCreatorData.existingTokenSupply - ).toHumanReadableNumber( - daoVotingTokenBasedCreatorData.existingToken.decimals ), tokenSymbol: daoVotingTokenBasedCreatorData.govTokenType === @@ -825,7 +825,7 @@ export const InnerCreateDaoForm = ({ } : //! Otherwise display native token, which has a balance of 0 initially. { - tokenBalance: 0, + tokenBalance: HugeDecimal.zero, tokenSymbol: nativeToken.symbol, tokenDecimals: nativeToken.decimals, } @@ -863,7 +863,7 @@ export const InnerCreateDaoForm = ({ data: { proposalCount: 0, tokenWithBalance: { - balance: tokenBalance, + balance: tokenBalance.toHumanReadableNumber(tokenDecimals), symbol: tokenSymbol, decimals: tokenDecimals, }, diff --git a/packages/stateful/components/dao/commonVotingConfig/QuorumVotingConfigItem.tsx b/packages/stateful/components/dao/commonVotingConfig/QuorumVotingConfigItem.tsx index 5917efb18..eb558dab5 100644 --- a/packages/stateful/components/dao/commonVotingConfig/QuorumVotingConfigItem.tsx +++ b/packages/stateful/components/dao/commonVotingConfig/QuorumVotingConfigItem.tsx @@ -1,6 +1,10 @@ import { useTranslation } from 'react-i18next' -import { MegaphoneEmoji, NumberInput, SelectInput } from '@dao-dao/stateless' +import { + HugeDecimalInput, + MegaphoneEmoji, + SelectInput, +} from '@dao-dao/stateless' import { DaoCreationVotingConfigItem, DaoCreationVotingConfigItemInputProps, @@ -19,7 +23,7 @@ const QuorumInput = ({ }, register, setValue, - watch, + getValues, errors, }: DaoCreationVotingConfigItemInputProps) => { const { t } = useTranslation() @@ -27,17 +31,18 @@ const QuorumInput = ({ return (
{!majority && ( - )} diff --git a/packages/stateful/components/dao/commonVotingConfig/VotingDurationVotingConfigItem.tsx b/packages/stateful/components/dao/commonVotingConfig/VotingDurationVotingConfigItem.tsx index 60dc5bcc6..639b69519 100644 --- a/packages/stateful/components/dao/commonVotingConfig/VotingDurationVotingConfigItem.tsx +++ b/packages/stateful/components/dao/commonVotingConfig/VotingDurationVotingConfigItem.tsx @@ -1,6 +1,10 @@ import { useTranslation } from 'react-i18next' -import { HourglassEmoji, NumberInput, SelectInput } from '@dao-dao/stateless' +import { + HourglassEmoji, + HugeDecimalInput, + SelectInput, +} from '@dao-dao/stateless' import { DaoCreationVotingConfigItem, DaoCreationVotingConfigItemInputProps, @@ -19,18 +23,20 @@ export const VotingDurationInput = ({ data: { votingDuration }, register, setValue, - watch, + getValues, errors, }: DaoCreationVotingConfigItemInputProps) => { const { t } = useTranslation() return (
- = 60 || t('error.mustBeAtLeastSixtySeconds'), ]} - watch={watch} /> d.denom === depositToken.denomOrAddress )!.amount - const missingDeposit = HugeDecimal.from(minDepositAmount) - .minus(currentDepositAmount) - .toHumanReadableNumber(depositToken.decimals) + const missingDeposit = + HugeDecimal.from(minDepositAmount).minus(currentDepositAmount) const info: ProposalStatusAndInfoProps['info'] = [ { @@ -233,13 +231,7 @@ const InnerGovProposalStatusAndInfo = ({ value: { proposalId, depositor: walletAddress, - amount: coins( - HugeDecimal.fromHumanReadable( - depositValue, - depositToken.decimals - ).toString(), - depositToken.denomOrAddress - ), + amount: depositValue.toCoins(depositToken.denomOrAddress), }, } @@ -251,7 +243,9 @@ const InnerGovProposalStatusAndInfo = ({ toast.success( t('success.deposited', { - amount: depositValue, + amount: depositValue.toInternationalizedHumanReadableString({ + decimals: depositToken.decimals, + }), tokenSymbol: depositToken.symbol, }) ) @@ -294,33 +288,35 @@ const InnerGovProposalStatusAndInfo = ({ status === ProposalStatus.PROPOSAL_STATUS_DEPOSIT_PERIOD ? { header: ( - - setDepositValue( - Number( - Number( - (event.target as HTMLInputElement).value - ).toFixed(depositToken.decimals) - ) - ) - } onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault() deposit() } }} - setValue={(_, value) => setDepositValue(value)} + setValue={(_, value) => + setDepositValue( + HugeDecimal.fromHumanReadable( + value, + depositToken.decimals + ) + ) + } step={HugeDecimal.one.toHumanReadableNumber( depositToken.decimals )} unit={'$' + depositToken.symbol} - value={depositValue} + value={depositValue.toHumanReadableString( + depositToken.decimals + )} /> ), label: t('button.deposit'), diff --git a/packages/stateful/creators/MembershipBased/GovernanceConfigurationInput.tsx b/packages/stateful/creators/MembershipBased/GovernanceConfigurationInput.tsx index cf6295d9e..ba65e8ec8 100644 --- a/packages/stateful/creators/MembershipBased/GovernanceConfigurationInput.tsx +++ b/packages/stateful/creators/MembershipBased/GovernanceConfigurationInput.tsx @@ -32,6 +32,7 @@ export const GovernanceConfigurationInput = ({ register, watch, setValue, + getValues, setError, clearErrors, }, @@ -175,6 +176,7 @@ export const GovernanceConfigurationInput = ({ control={control} data={data} errors={errors} + getValues={getValues} register={register} remove={tierFields.length === 1 ? undefined : () => removeTier(idx)} setValue={setValue} diff --git a/packages/stateful/creators/MembershipBased/TierCard.tsx b/packages/stateful/creators/MembershipBased/TierCard.tsx index d652239f6..c7241fbbf 100644 --- a/packages/stateful/creators/MembershipBased/TierCard.tsx +++ b/packages/stateful/creators/MembershipBased/TierCard.tsx @@ -2,6 +2,7 @@ import { Add, Close } from '@mui/icons-material' import { Control, FormState, + UseFormGetValues, UseFormRegister, UseFormSetValue, UseFormWatch, @@ -11,10 +12,10 @@ import { useTranslation } from 'react-i18next' import { Button, + HugeDecimalInput, IconButton, InputErrorMessage, InputLabel, - NumberInput, TextInput, useChain, } from '@dao-dao/stateless' @@ -41,6 +42,7 @@ export interface TierCardProps { watch: UseFormWatch> errors: FormState>['errors'] setValue: UseFormSetValue> + getValues: UseFormGetValues> remove?: () => void } @@ -58,6 +60,7 @@ export const TierCard = ({ watch, errors, setValue, + getValues, showColorDotOnMember, } = props @@ -135,15 +138,16 @@ export const TierCard = ({ })} /> - ) => { const { t } = useTranslation() return (
-
- - (typeof maxSupply === 'number' && - maxSupply >= data.newInfo.initialSupply) || - t('error.maxSupplyMustBeAtLeastInitialSupply'), + HugeDecimal.from(maxSupply || 0).gte( + data.newInfo.initialSupply + ) || t('error.maxSupplyMustBeAtLeastInitialSupply'), ]} />

@@ -577,7 +578,7 @@ export const GovernanceConfigurationInput = ({

-
- removeTier(idx) diff --git a/packages/stateful/creators/TokenBased/TierCard.tsx b/packages/stateful/creators/TokenBased/TierCard.tsx index 50c4a9447..01672b75c 100644 --- a/packages/stateful/creators/TokenBased/TierCard.tsx +++ b/packages/stateful/creators/TokenBased/TierCard.tsx @@ -2,6 +2,7 @@ import { Add, Close } from '@mui/icons-material' import { Control, FormState, + UseFormGetValues, UseFormRegister, UseFormSetValue, UseFormWatch, @@ -9,12 +10,13 @@ import { } from 'react-hook-form' import { useTranslation } from 'react-i18next' +import { HugeDecimal } from '@dao-dao/math' import { Button, + HugeDecimalInput, IconButton, InputErrorMessage, InputLabel, - NumberInput, TextInput, TooltipInfoIcon, useChain, @@ -25,6 +27,7 @@ import { NEW_DAO_TOKEN_DECIMALS, formatPercentOf100, makeValidateAddress, + validatePercent, validatePositive, validateRequired, } from '@dao-dao/utils' @@ -44,6 +47,7 @@ export interface TierCardProps { watch: UseFormWatch> errors: FormState>['errors'] setValue: UseFormSetValue> + getValues: UseFormGetValues> remove?: () => void } @@ -61,6 +65,7 @@ export const TierCard = ({ watch, errors, setValue, + getValues, showColorDotOnMember, } = props @@ -139,16 +144,17 @@ export const TierCard = ({ />
-
@@ -215,12 +221,16 @@ export const TierCard = ({ diff --git a/packages/stateful/creators/TokenBased/UnstakingDurationVotingConfigItem.tsx b/packages/stateful/creators/TokenBased/UnstakingDurationVotingConfigItem.tsx index f2251d807..d5d41c287 100644 --- a/packages/stateful/creators/TokenBased/UnstakingDurationVotingConfigItem.tsx +++ b/packages/stateful/creators/TokenBased/UnstakingDurationVotingConfigItem.tsx @@ -5,8 +5,8 @@ import { useTranslation } from 'react-i18next' import { contractQueries } from '@dao-dao/state/query' import { ClockEmoji, + HugeDecimalInput, InputErrorMessage, - NumberInput, SelectInput, SwitchCard, useHoldingKey, @@ -41,6 +41,7 @@ export const UnstakingDurationInput = ({ }, register, setValue, + getValues, watch, errors, }: DaoCreationVotingConfigItemInputProps) => { @@ -127,17 +128,18 @@ export const UnstakingDurationInput = ({
) : (
- = ({ address, // Governance Token-based DAOs distribute tier weights evenly amongst // members. - amount: HugeDecimal.from( - (weight / members.length / 100) * initialSupply - ).toInternationalizedHumanReadableString({ - decimals: NEW_DAO_TOKEN_DECIMALS, - }), + amount: HugeDecimal.fromHumanReadable( + initialSupply, + NEW_DAO_TOKEN_DECIMALS + ) + .times(weight) + .div(members.length) + .div(100) + .toFixed(0), })) ) // To prevent rounding issues, treasury balance becomes the remaining tokens @@ -114,7 +117,7 @@ export const getInstantiateInfo: DaoCreatorGetInstantiateInfo = ({ throw new Error('tokenCreationFactoryAddress not set') } - if (!maxSupply) { + if (!maxSupply || maxSupply === '0') { throw new Error('Max supply not set') } diff --git a/packages/stateful/creators/TokenBased/index.ts b/packages/stateful/creators/TokenBased/index.ts index f4f6b2987..2cb5fed86 100644 --- a/packages/stateful/creators/TokenBased/index.ts +++ b/packages/stateful/creators/TokenBased/index.ts @@ -41,9 +41,9 @@ export const TokenBasedCreator: DaoCreator = { selectedTokenType: tokenDaoType === 'both' ? TokenType.Native : tokenDaoType, newInfo: { - initialSupply: 10000000, + initialSupply: '10000000', initialTreasuryPercent: 90, - maxSupply: 100000000, + maxSupply: '100000000', symbol: '', name: '', }, diff --git a/packages/stateful/creators/TokenBased/types.ts b/packages/stateful/creators/TokenBased/types.ts index c7fec5294..e8d5ec287 100644 --- a/packages/stateful/creators/TokenBased/types.ts +++ b/packages/stateful/creators/TokenBased/types.ts @@ -21,10 +21,10 @@ export type CreatorData = { govTokenType: GovernanceTokenType selectedTokenType: TokenType newInfo: { - initialSupply: number + initialSupply: string initialTreasuryPercent: number // For BitSong which needs an up front max. - maxSupply?: number + maxSupply?: string imageUrl?: string // For Bitsong, which needs a JSON URL containing the image. metadataUrl?: string diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdateProposalConfig/UpdateProposalConfigComponent.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdateProposalConfig/UpdateProposalConfigComponent.tsx index 617a5615b..3d1df555b 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdateProposalConfig/UpdateProposalConfigComponent.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdateProposalConfig/UpdateProposalConfigComponent.tsx @@ -6,9 +6,9 @@ import { useTranslation } from 'react-i18next' import { ClockEmoji, FormSwitchCard, + HugeDecimalInput, InputErrorMessage, KeyEmoji, - NumberInput, PeopleEmoji, ProposalVetoConfigurer, RecycleEmoji, @@ -67,7 +67,7 @@ export const UpdateProposalConfigComponent: ActionComponent< }, }) => { const { t } = useTranslation() - const { register, setValue, watch } = + const { register, setValue, getValues, watch } = useFormContext() const onlyMembersExecute = watch( @@ -99,18 +99,24 @@ export const UpdateProposalConfigComponent: ActionComponent<
{percentageQuorumSelected && (
-
@@ -139,14 +145,16 @@ export const UpdateProposalConfigComponent: ActionComponent<
- = 60 || t('error.mustBeAtLeastSixtySeconds'), ]} - watch={watch} />
diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.stories.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.stories.tsx index 39d3f5fe9..bb4b5dc45 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.stories.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.stories.tsx @@ -17,7 +17,7 @@ export default { onlyMembersExecute: true, depositRequired: true, depositInfo: { - deposit: 123, + deposit: '123', refundFailedProposals: true, }, thresholdType: '%', diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.tsx index 13e0d6c45..ffbe92c64 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.tsx @@ -1,14 +1,15 @@ import { useFormContext } from 'react-hook-form' import { useTranslation } from 'react-i18next' +import { HugeDecimal } from '@dao-dao/math' import { ChartEmoji, ClockEmoji, FormSwitchCard, + HugeDecimalInput, InputErrorMessage, KeyEmoji, MoneyEmoji, - NumberInput, PeopleEmoji, RecycleEmoji, SelectInput, @@ -31,7 +32,7 @@ export type UpdateProposalConfigData = { depositRequired: boolean depositInfo?: { - deposit: number + deposit: string refundFailedProposals: boolean } @@ -60,7 +61,7 @@ export const UpdateProposalConfigComponent: ActionComponent< options: { commonGovernanceTokenInfo }, }) => { const { t } = useTranslation() - const { register, setValue, watch } = + const { register, setValue, watch, getValues } = useFormContext() const onlyMembersExecute = watch( @@ -118,16 +119,21 @@ export const UpdateProposalConfigComponent: ActionComponent< {depositRequired && (
- @@ -166,19 +172,25 @@ export const UpdateProposalConfigComponent: ActionComponent<
{percentageThresholdSelected && (
-
@@ -222,18 +234,24 @@ export const UpdateProposalConfigComponent: ActionComponent<
{percentageQuorumSelected && (
-
@@ -264,14 +282,16 @@ export const UpdateProposalConfigComponent: ActionComponent<
- = 60 || t('error.mustBeAtLeastSixtySeconds'), ]} - watch={watch} />
diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/index.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/index.tsx index b20fcff82..f4b8be277 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/index.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/index.tsx @@ -144,11 +144,11 @@ export class DaoProposalSingleV1UpdateConfigAction extends ActionBase { const { t } = useTranslation() - const { register, setValue, watch } = + const { register, setValue, getValues, watch } = useFormContext() const onlyMembersExecute = watch( @@ -111,19 +111,24 @@ export const UpdateProposalConfigComponent: ActionComponent<
{percentageThresholdSelected && (
-
@@ -165,18 +170,23 @@ export const UpdateProposalConfigComponent: ActionComponent<
{percentageQuorumSelected && (
-
@@ -207,14 +217,16 @@ export const UpdateProposalConfigComponent: ActionComponent<
- = 60 || t('error.mustBeAtLeastSixtySeconds'), ]} - watch={watch} />
diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/daoCreation/ThresholdVotingConfigItem.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/daoCreation/ThresholdVotingConfigItem.tsx index 0e0c7ccf2..f248d7b02 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/daoCreation/ThresholdVotingConfigItem.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/daoCreation/ThresholdVotingConfigItem.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import { BallotDepositEmoji, FormSwitchCard, - NumberInput, + HugeDecimalInput, SelectInput, } from '@dao-dao/stateless' import { @@ -13,6 +13,7 @@ import { } from '@dao-dao/types' import { formatPercentOf100, + validatePercent, validatePositive, validateRequired, } from '@dao-dao/utils' @@ -26,7 +27,7 @@ const ThresholdInput = ({ }, register, setValue, - watch, + getValues, errors, }: DaoCreationVotingConfigItemInputProps) => { const { t } = useTranslation() @@ -35,17 +36,18 @@ const ThresholdInput = ({
{!majority && ( - )} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/MintComponent.stories.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/MintComponent.stories.tsx index 6c557d73f..7bca77534 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/MintComponent.stories.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/MintComponent.stories.tsx @@ -12,7 +12,7 @@ export default { component: MintComponent, decorators: [ makeReactHookFormDecorator({ - amount: 100000, + amount: '100000', to: '', }), ], diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/MintComponent.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/MintComponent.tsx index b17506f45..7e6044fb4 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/MintComponent.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/MintComponent.tsx @@ -6,9 +6,10 @@ import clsx from 'clsx' import { ComponentType } from 'react' import { useFormContext } from 'react-hook-form' +import { HugeDecimal } from '@dao-dao/math' import { + HugeDecimalInput, InputErrorMessage, - NumberInput, useActionOptions, useDetectWrap, } from '@dao-dao/stateless' @@ -25,13 +26,13 @@ import { export type MintData = { to: string - amount: number + amount: string } export type MintOptions = { govToken: GenericToken // Used to display the profile of the address receiving minted tokens. - AddressInput: ComponentType + AddressInput: ComponentType> } export const MintComponent: ActionComponent = ({ @@ -43,7 +44,7 @@ export const MintComponent: ActionComponent = ({ const { chain: { bech32_prefix: bech32Prefix }, } = useActionOptions() - const { register, watch, setValue } = useFormContext() + const { register, setValue, getValues } = useFormContext() const { containerRef, childRef, wrapped } = useDetectWrap() const Icon = wrapped ? SubdirectoryArrowRightRounded : ArrowRightAltRounded @@ -54,19 +55,19 @@ export const MintComponent: ActionComponent = ({ className="flex flex-row flex-wrap items-stretch gap-x-3 gap-y-2" ref={containerRef} > -
= ({ containerClassName="grow" disabled={!isCreating} error={errors?.to} - fieldName={fieldNamePrefix + 'to'} + fieldName={(fieldNamePrefix + 'to') as 'to'} register={register} validation={[validateRequired, makeValidateAddress(bech32Prefix)]} /> diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/index.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/index.tsx index c78796da2..0b5d6988f 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/index.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/index.tsx @@ -50,7 +50,7 @@ export class MintAction extends ActionBase { this.defaults = { to: options.address, - amount: 1, + amount: '1', } } @@ -115,7 +115,7 @@ export class MintAction extends ActionBase { to: decodedMessage.wasm.execute.msg.mint.recipient, amount: HugeDecimal.from( decodedMessage.wasm.execute.msg.mint.amount - ).toHumanReadableNumber(this.governanceToken.decimals), + ).toHumanReadableString(this.governanceToken.decimals), } } } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/UpdateStakingConfig/Component.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/UpdateStakingConfig/Component.tsx index 9e2fa6f60..22865a47e 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/UpdateStakingConfig/Component.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/UpdateStakingConfig/Component.tsx @@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next' import { FormSwitchCard, + HugeDecimalInput, InputErrorMessage, - NumberInput, SelectInput, } from '@dao-dao/stateless' import { @@ -26,7 +26,7 @@ export const UpdateStakingConfigComponent: ActionComponent = ({ isCreating, }) => { const { t } = useTranslation() - const { register, watch, setValue } = + const { register, watch, setValue, getValues } = useFormContext() const unstakingDurationEnabled = watch( @@ -57,14 +57,16 @@ export const UpdateStakingConfigComponent: ActionComponent = ({ {unstakingDurationEnabled && ( <>
- {isCreating && ( diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/UpdateStakingConfig/README.md b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/UpdateStakingConfig/README.md index 389b34430..639d6f65f 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/UpdateStakingConfig/README.md +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/UpdateStakingConfig/README.md @@ -17,7 +17,7 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). { "unstakingDurationEnabled": , "unstakingDuration": { - "value": "", + "value": , "units": "" } } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/Component.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/Component.tsx index 01a0e5a77..c85efe785 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/Component.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/Component.tsx @@ -12,11 +12,11 @@ import { useTranslation } from 'react-i18next' import { Button, Checkbox, + HugeDecimalInput, IconButton, InputErrorMessage, InputLabel, Loader, - NumberInput, useActionOptions, useDetectWrap, } from '@dao-dao/stateless' @@ -58,7 +58,7 @@ export const ManageMembersComponent: ActionComponent< const { chain: { bech32_prefix: bech32Prefix }, } = useActionOptions() - const { register, setValue, watch, control } = + const { register, setValue, watch, control, getValues } = useFormContext() const toRemove = watch((fieldNamePrefix + 'toRemove') as 'toRemove') @@ -102,17 +102,18 @@ export const ManageMembersComponent: ActionComponent< >
- { const { t } = useTranslation() - const { register, watch, setValue } = + const { register, watch, setValue, getValues } = useFormContext() const unstakingDurationEnabled = watch( @@ -57,14 +57,16 @@ export const UpdateStakingConfigComponent: ActionComponent = ({ {unstakingDurationEnabled && ( <>
- {isCreating && ( diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/actions/UpdateStakingConfig/README.md b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/actions/UpdateStakingConfig/README.md index 74171ed2c..79c2dc74c 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/actions/UpdateStakingConfig/README.md +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/actions/UpdateStakingConfig/README.md @@ -17,7 +17,7 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). { "unstakingDurationEnabled": , "unstakingDuration": { - "value": "", + "value": , "units": "" } } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.stories.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.stories.tsx index 141d86e1a..5225850a8 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.stories.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.stories.tsx @@ -11,7 +11,7 @@ export default { component: MintComponent, decorators: [ makeReactHookFormDecorator({ - amount: 100000, + amount: '100000', }), ], } as ComponentMeta diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.tsx index 402208773..a13e2f7f9 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.tsx @@ -2,12 +2,12 @@ import { useFormContext } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' -import { InputErrorMessage, NumberInput } from '@dao-dao/stateless' +import { HugeDecimalInput, InputErrorMessage } from '@dao-dao/stateless' import { ActionComponent, GenericToken } from '@dao-dao/types' import { validatePositive, validateRequired } from '@dao-dao/utils' export type MintData = { - amount: number + amount: string } export type MintOptions = { @@ -21,15 +21,16 @@ export const MintComponent: ActionComponent = ({ options: { govToken }, }) => { const { t } = useTranslation() - const { register, watch, setValue } = useFormContext() + const { register, setValue, getValues } = useFormContext() return ( <> - = ({ step={HugeDecimal.one.toHumanReadableNumber(govToken.decimals)} unit={'$' + govToken.symbol} validation={[validateRequired, validatePositive]} - watch={watch} /> {errors?.amount && ( diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/index.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/index.tsx index b8c36499b..4846f19ee 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/index.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/index.tsx @@ -42,7 +42,7 @@ export class MintAction extends ActionBase { public readonly Component = Component protected _defaults: MintData = { - amount: 1, + amount: '1', } private governanceToken?: GenericToken @@ -108,7 +108,7 @@ export class MintAction extends ActionBase { return { amount: HugeDecimal.from( decodedMessage.stargate.value.amount.amount - ).toHumanReadableNumber(this.governanceToken.decimals), + ).toHumanReadableString(this.governanceToken.decimals), } } } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.tsx index 9e2fa6f60..22865a47e 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.tsx @@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next' import { FormSwitchCard, + HugeDecimalInput, InputErrorMessage, - NumberInput, SelectInput, } from '@dao-dao/stateless' import { @@ -26,7 +26,7 @@ export const UpdateStakingConfigComponent: ActionComponent = ({ isCreating, }) => { const { t } = useTranslation() - const { register, watch, setValue } = + const { register, watch, setValue, getValues } = useFormContext() const unstakingDurationEnabled = watch( @@ -57,14 +57,16 @@ export const UpdateStakingConfigComponent: ActionComponent = ({ {unstakingDurationEnabled && ( <>
- {isCreating && ( diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/README.md b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/README.md index 74171ed2c..79c2dc74c 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/README.md +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/README.md @@ -17,7 +17,7 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). { "unstakingDurationEnabled": , "unstakingDuration": { - "value": "", + "value": , "units": "" } } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/actions/UpdateStakingConfig/Component.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/actions/UpdateStakingConfig/Component.tsx index 9e2fa6f60..22865a47e 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/actions/UpdateStakingConfig/Component.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/actions/UpdateStakingConfig/Component.tsx @@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next' import { FormSwitchCard, + HugeDecimalInput, InputErrorMessage, - NumberInput, SelectInput, } from '@dao-dao/stateless' import { @@ -26,7 +26,7 @@ export const UpdateStakingConfigComponent: ActionComponent = ({ isCreating, }) => { const { t } = useTranslation() - const { register, watch, setValue } = + const { register, watch, setValue, getValues } = useFormContext() const unstakingDurationEnabled = watch( @@ -57,14 +57,16 @@ export const UpdateStakingConfigComponent: ActionComponent = ({ {unstakingDurationEnabled && ( <>
- {isCreating && ( diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/actions/UpdateStakingConfig/README.md b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/actions/UpdateStakingConfig/README.md index 74171ed2c..79c2dc74c 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/actions/UpdateStakingConfig/README.md +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/actions/UpdateStakingConfig/README.md @@ -17,7 +17,7 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). { "unstakingDurationEnabled": , "unstakingDuration": { - "value": "", + "value": , "units": "" } } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/BitSongFantokenMintAction.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/BitSongFantokenMintAction.ts index 0d951cff7..e824c29aa 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/BitSongFantokenMintAction.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/BitSongFantokenMintAction.ts @@ -1,5 +1,3 @@ -import { coin } from '@cosmjs/amino' - import { HugeDecimal } from '@dao-dao/math' import { tokenQueries } from '@dao-dao/state/query' import { ActionBase, HerbEmoji } from '@dao-dao/stateless' @@ -68,26 +66,24 @@ export class BitSongFantokenMintAction extends ActionBase { this.defaults = { recipient: this.options.address, - amount: 1, + amount: '1', } } - encode({ recipient, amount: _amount }: MintData): UnifiedCosmosMsg { + encode({ recipient, amount }: MintData): UnifiedCosmosMsg { if (!this.governanceToken) { throw new Error('Action not ready') } - const amount = HugeDecimal.fromHumanReadable( - _amount, - this.governanceToken.decimals - ).toString() - return makeStargateMessage({ stargate: { typeUrl: MsgMint.typeUrl, value: MsgMint.fromPartial({ recipient, - coin: coin(amount, this.governanceToken.denomOrAddress), + coin: HugeDecimal.fromHumanReadable( + amount, + this.governanceToken.decimals + ).toCoin(this.governanceToken.denomOrAddress), minter: this.options.address, }), }, @@ -114,7 +110,7 @@ export class BitSongFantokenMintAction extends ActionBase { recipient: decodedMessage.stargate.value.recipient, amount: HugeDecimal.from( decodedMessage.stargate.value.coin.amount - ).toHumanReadableNumber(this.governanceToken.decimals), + ).toHumanReadableString(this.governanceToken.decimals), } } } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintAction.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintAction.ts index 81f70cfab..f15c83d4c 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintAction.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintAction.ts @@ -38,7 +38,7 @@ export class MintAction extends ActionBase { this.defaults = { recipient: options.address, - amount: 1, + amount: '1', } // Fire async init immediately since we may hide this action. @@ -161,7 +161,7 @@ export class MintAction extends ActionBase { recipient: decodedMessage.wasm.execute.msg.mint.to_address, amount: HugeDecimal.from( decodedMessage.wasm.execute.msg.mint.amount - ).toHumanReadableNumber(this.governanceToken.decimals), + ).toHumanReadableString(this.governanceToken.decimals), } } } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.stories.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.stories.tsx index 3a89a311d..a0af43982 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.stories.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.stories.tsx @@ -13,7 +13,7 @@ export default { decorators: [ makeReactHookFormDecorator({ recipient: 'address', - amount: 100000, + amount: '100000', }), ], } as ComponentMeta diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.tsx index 4bbb8ed19..53dae6f34 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.tsx @@ -9,8 +9,8 @@ import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { + HugeDecimalInput, InputErrorMessage, - NumberInput, StatusCard, useChain, useDetectWrap, @@ -28,7 +28,7 @@ import { export type MintData = { recipient: string - amount: number + amount: string } export type MintOptions = { @@ -43,7 +43,7 @@ export const MintComponent: ActionComponent = ({ options: { govToken, AddressInput }, }) => { const { t } = useTranslation() - const { register, watch, setValue } = useFormContext() + const { register, setValue, getValues } = useFormContext() const { bech32_prefix: bech32Prefix } = useChain() const { containerRef, childRef, wrapped } = useDetectWrap() @@ -61,17 +61,17 @@ export const MintComponent: ActionComponent = ({ className="flex min-w-0 flex-row flex-wrap items-stretch justify-between gap-x-3 gap-y-1" ref={containerRef} > -
+} as ComponentMeta -const Template: ComponentStory = (args) => { - const { register, watch, setValue } = useFormContext() +const Template: ComponentStory = (args) => { + const { register, setValue, getValues } = useFormContext() return ( - ) } From 9bb2b87d8fac2ad744563a6fb75c40d74fcad8a4 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Tue, 1 Oct 2024 22:57:32 -0400 Subject: [PATCH 18/26] replace even mooooore NumberInput with HugeDecimalInput --- packages/math/HugeDecimal.ts | 20 +- .../actions/AuthzGrantRevoke/Component.tsx | 4 +- .../core/actions/AuthzGrantRevoke/index.tsx | 11 +- .../core/actions/AuthzGrantRevoke/types.ts | 2 +- .../CommunityPoolDeposit/Component.tsx | 25 +- .../actions/CommunityPoolDeposit/index.tsx | 13 +- .../actions/ConfigureRebalancer/Component.tsx | 7 +- .../actions/ConfigureRebalancer/README.md | 21 +- .../actions/ConfigureRebalancer/index.tsx | 4 +- .../CreateRewardDistribution/index.tsx | 1 + .../CreateValenceAccount/Component.tsx | 4 +- .../actions/CreateValenceAccount/index.tsx | 17 +- .../core/actions/Execute/Component.tsx | 5 +- .../actions/core/actions/Execute/index.tsx | 15 +- .../actions/FundRewardDistribution/README.md | 2 +- .../GovernanceDeposit/Component.stories.tsx | 4 +- .../actions/GovernanceDeposit/Component.tsx | 17 +- .../core/actions/GovernanceDeposit/README.md | 2 +- .../core/actions/GovernanceDeposit/index.tsx | 124 ++++++---- .../GovernanceProposal/Component.stories.tsx | 8 +- .../actions/GovernanceProposal/Component.tsx | 123 ++++++---- .../core/actions/GovernanceProposal/README.md | 6 +- .../core/actions/GovernanceProposal/index.tsx | 216 ++++++++++------- .../core/actions/Instantiate/Component.tsx | 2 +- .../core/actions/Instantiate/index.tsx | 11 +- .../core/actions/Instantiate2/Component.tsx | 2 +- .../core/actions/Instantiate2/index.tsx | 9 +- .../core/actions/ManageStaking/Component.tsx | 12 +- .../core/actions/ManageStaking/README.md | 2 +- .../actions/ManageVesting/BeginVesting.tsx | 44 ++-- .../core/actions/ManageVesting/README.md | 27 ++- .../core/actions/ManageVesting/index.tsx | 16 +- .../core/actions/Spend/Component.stories.tsx | 2 +- .../actions/core/actions/Spend/Component.tsx | 41 ++-- .../actions/core/actions/Spend/README.md | 2 +- .../actions/core/actions/Spend/index.tsx | 16 +- .../stateful/ChooseExistingTokenSwap.tsx | 2 +- .../stateful/InstantiateTokenSwap.tsx | 4 +- .../InstantiateTokenSwap.stories.tsx | 4 +- .../stateless/InstantiateTokenSwap.tsx | 39 ++-- .../actions/core/actions/token_swap/types.ts | 2 +- .../ActiveThresholdVotingConfigItem.tsx | 5 +- .../ProposalDepositVotingConfigItem.tsx | 11 +- .../creators/NftBased/getInstantiateInfo.ts | 2 +- .../creators/TokenBased/getInstantiateInfo.ts | 9 +- .../stateful/creators/TokenBased/index.ts | 2 +- ...pdatePreProposeConfigComponent.stories.tsx | 2 +- .../UpdatePreProposeConfigComponent.tsx | 6 +- .../actions/UpdatePreProposeConfig/index.tsx | 8 +- ...pdatePreProposeConfigComponent.stories.tsx | 2 +- .../UpdatePreProposeConfigComponent.tsx | 6 +- .../actions/UpdatePreProposeConfig/index.tsx | 8 +- .../components/stateless/NewAttribute.tsx | 5 + .../components/actions/NativeCoinSelector.tsx | 24 +- .../components/inputs/HugeDecimalInput.tsx | 17 +- .../components/inputs/NumberInput.tsx | 219 ------------------ .../components/inputs/TokenInput.stories.tsx | 3 +- .../components/inputs/TokenInput.tsx | 34 ++- packages/stateless/components/inputs/index.ts | 1 - packages/types/components/NumberInput.ts | 64 ----- packages/types/components/TokenInput.ts | 21 +- packages/types/components/index.ts | 1 - packages/types/dao.ts | 4 +- packages/types/gov.ts | 6 +- 64 files changed, 620 insertions(+), 728 deletions(-) delete mode 100644 packages/stateless/components/inputs/NumberInput.tsx delete mode 100644 packages/types/components/NumberInput.ts diff --git a/packages/math/HugeDecimal.ts b/packages/math/HugeDecimal.ts index cbeeb1d7f..c0f913127 100644 --- a/packages/math/HugeDecimal.ts +++ b/packages/math/HugeDecimal.ts @@ -112,8 +112,20 @@ export class HugeDecimal { return this.value.toJSON() } - toNumber() { - return this.value.toNumber() + /** + * Returns the value of this HugeDecimal instance as a JavaScript primitive + * number. + * + * Pass `truncateDecimals` to truncate to a specific number of decimal places. + * Leaving it undefined will preserve all existing decimal places. + * + * @param truncateDecimals whether or not to truncate the decimal places + * @returns a number + */ + toNumber(truncateDecimals?: number) { + return truncateDecimals !== undefined + ? Number(this.toFixed(truncateDecimals)) + : this.value.toNumber() } /** @@ -171,6 +183,10 @@ export class HugeDecimal { return this.value.isZero() } + isNaN() { + return this.value.isNaN() + } + lt(n: HugeDecimal.Value) { return this.value.lt(valueToBigNumber(n)) } diff --git a/packages/stateful/actions/core/actions/AuthzGrantRevoke/Component.tsx b/packages/stateful/actions/core/actions/AuthzGrantRevoke/Component.tsx index ea7be0c70..473d4ea8f 100644 --- a/packages/stateful/actions/core/actions/AuthzGrantRevoke/Component.tsx +++ b/packages/stateful/actions/core/actions/AuthzGrantRevoke/Component.tsx @@ -255,7 +255,7 @@ export const AuthzGrantRevokeComponent: ActionComponent< className="mt-2 self-start" onClick={() => appendCoin({ - amount: 1, + amount: '1', denom: nativeToken.denomOrAddress, decimals: nativeToken.decimals, }) @@ -467,7 +467,7 @@ export const AuthzGrantRevokeComponent: ActionComponent< className="mt-2 self-start" onClick={() => appendCoin({ - amount: 1, + amount: '1', denom: nativeToken.denomOrAddress, decimals: nativeToken.decimals, }) diff --git a/packages/stateful/actions/core/actions/AuthzGrantRevoke/index.tsx b/packages/stateful/actions/core/actions/AuthzGrantRevoke/index.tsx index 3067e327d..8a30f4f8d 100644 --- a/packages/stateful/actions/core/actions/AuthzGrantRevoke/index.tsx +++ b/packages/stateful/actions/core/actions/AuthzGrantRevoke/index.tsx @@ -161,10 +161,9 @@ export class AuthzGrantRevokeAction extends ActionBase { callsRemaining: BigInt(calls), // MaxFundsLimit // CombinedLimit - amounts: funds.map(({ denom, amount, decimals }) => ({ - amount: HugeDecimal.fromHumanReadable(amount, decimals).toString(), - denom, - })), + amounts: funds.map(({ denom, amount, decimals }) => + HugeDecimal.fromHumanReadable(amount, decimals).toCoin(denom) + ), }) let authorization: Any | undefined @@ -381,7 +380,7 @@ export class AuthzGrantRevokeAction extends ActionBase { return { denom, amount: - HugeDecimal.from(amount).toHumanReadableNumber(decimals), + HugeDecimal.from(amount).toHumanReadableString(decimals), decimals, } }) ?? [], @@ -422,7 +421,7 @@ export class AuthzGrantRevokeAction extends ActionBase { return { denom, amount: - HugeDecimal.from(amount).toHumanReadableNumber(decimals), + HugeDecimal.from(amount).toHumanReadableString(decimals), decimals, } }) ?? [], diff --git a/packages/stateful/actions/core/actions/AuthzGrantRevoke/types.ts b/packages/stateful/actions/core/actions/AuthzGrantRevoke/types.ts index ba76531d8..93052a2dd 100644 --- a/packages/stateful/actions/core/actions/AuthzGrantRevoke/types.ts +++ b/packages/stateful/actions/core/actions/AuthzGrantRevoke/types.ts @@ -33,7 +33,7 @@ export type AuthzGrantRevokeData = { contract: string funds: { denom: string - amount: number + amount: string // Will multiply `amount` by 10^decimals when generating the message. decimals: number }[] diff --git a/packages/stateful/actions/core/actions/CommunityPoolDeposit/Component.tsx b/packages/stateful/actions/core/actions/CommunityPoolDeposit/Component.tsx index a2b322e88..354f881b3 100644 --- a/packages/stateful/actions/core/actions/CommunityPoolDeposit/Component.tsx +++ b/packages/stateful/actions/core/actions/CommunityPoolDeposit/Component.tsx @@ -12,7 +12,7 @@ import { ActionComponent } from '@dao-dao/types/actions' export type CommunityPoolDepositData = { chainId: string - amount: number + amount: string denom: string _error?: string } @@ -26,7 +26,7 @@ export const CommunityPoolDepositComponent: ActionComponent< > = ({ fieldNamePrefix, isCreating, errors, options: { tokens } }) => { const { t } = useTranslation() - const { register, watch, setValue } = + const { register, watch, setValue, getValues } = useFormContext() const spendChainId = watch((fieldNamePrefix + 'chainId') as 'chainId') @@ -40,9 +40,7 @@ export const CommunityPoolDepositComponent: ActionComponent< token.chainId === spendChainId && token.denomOrAddress === spendDenom ) const selectedDecimals = selectedToken?.token.decimals ?? 0 - const selectedBalance = HugeDecimal.from( - selectedToken?.balance ?? 0 - ).toHumanReadableNumber(selectedDecimals) + const selectedBalance = HugeDecimal.from(selectedToken?.balance ?? 0) // A warning if the denom was not found in the treasury or the amount is too // high. We don't want to make this an error because often people want to @@ -53,10 +51,10 @@ export const CommunityPoolDepositComponent: ActionComponent< ? undefined : !selectedToken ? t('error.unknownDenom', { denom: spendDenom }) - : spendAmount > selectedBalance + : selectedBalance.toHumanReadable(selectedDecimals).lt(spendAmount) ? t('error.insufficientFundsWarning', { - amount: selectedBalance.toLocaleString(undefined, { - maximumFractionDigits: selectedDecimals, + amount: selectedBalance.toInternationalizedHumanReadableString({ + decimals: selectedDecimals, }), tokenSymbol: symbol, }) @@ -69,6 +67,7 @@ export const CommunityPoolDepositComponent: ActionComponent< amount={{ watch, setValue, + getValues, register, fieldName: (fieldNamePrefix + 'amount') as 'amount', error: errors?.amount, @@ -91,11 +90,11 @@ export const CommunityPoolDepositComponent: ActionComponent< description: t('title.balance') + ': ' + - HugeDecimal.from(balance) - .toHumanReadableNumber(token.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: token.decimals, - }), + HugeDecimal.from( + balance + ).toInternationalizedHumanReadableString({ + decimals: token.decimals, + }), })), } } diff --git a/packages/stateful/actions/core/actions/CommunityPoolDeposit/index.tsx b/packages/stateful/actions/core/actions/CommunityPoolDeposit/index.tsx index 2d92e34cd..c9c37c0f8 100644 --- a/packages/stateful/actions/core/actions/CommunityPoolDeposit/index.tsx +++ b/packages/stateful/actions/core/actions/CommunityPoolDeposit/index.tsx @@ -1,4 +1,3 @@ -import { coins } from '@cosmjs/stargate' import { useFormContext } from 'react-hook-form' import { HugeDecimal } from '@dao-dao/math' @@ -84,7 +83,7 @@ export class CommunityPoolDepositAction extends ActionBase", - ... + "trustee": "", + "baseDenom": "", + "tokens": [ + { + "denom": "", + "percent": + }, + ... + ], + "pid": { + "kp": , + "ki": , + "kd": + }, + "maxLimit": , + "minBalance": { + "denom": "", + "amount": "" + } | undefined, + "targetOverrideStrategy": "" } ``` diff --git a/packages/stateful/actions/core/actions/ConfigureRebalancer/index.tsx b/packages/stateful/actions/core/actions/ConfigureRebalancer/index.tsx index d8a648a26..dbcfaffd2 100644 --- a/packages/stateful/actions/core/actions/ConfigureRebalancer/index.tsx +++ b/packages/stateful/actions/core/actions/ConfigureRebalancer/index.tsx @@ -418,7 +418,7 @@ export class ConfigureRebalancerAction extends ActionBase appendCoin({ - amount: 1, + amount: '1', denom: nativeToken.denomOrAddress, decimals: nativeToken.decimals, }) diff --git a/packages/stateful/actions/core/actions/CreateValenceAccount/index.tsx b/packages/stateful/actions/core/actions/CreateValenceAccount/index.tsx index eb044996b..4cf57fbb3 100644 --- a/packages/stateful/actions/core/actions/CreateValenceAccount/index.tsx +++ b/packages/stateful/actions/core/actions/CreateValenceAccount/index.tsx @@ -163,7 +163,7 @@ export class CreateValenceAccountAction extends ActionBase ({ - denom, - amount: HugeDecimal.fromHumanReadable(amount, decimals).toString(), - })) + const convertedFunds = funds.map(({ denom, amount, decimals }) => + HugeDecimal.fromHumanReadable(amount, decimals).toCoin(denom) + ) // Add service fee to funds. if (serviceFee && serviceFee.amount !== '0') { const existing = convertedFunds.find((f) => f.denom === serviceFee.denom) if (existing) { - existing.amount = ( - BigInt(existing.amount) + BigInt(serviceFee.amount) - ).toString() + existing.amount = HugeDecimal.from(existing.amount) + .plus(serviceFee.amount) + .toString() } else { convertedFunds.push({ denom: serviceFee.denom, @@ -279,7 +278,7 @@ export class CreateValenceAccountAction extends ActionBase = ({ const { context } = useActionOptions() const { chain_id: chainId, bech32_prefix: bech32Prefix } = useChain() - const { register, control, watch, setValue } = useFormContext() + const { register, control, watch, setValue, getValues } = useFormContext() const { fields: coins, append: appendCoin, @@ -192,6 +192,7 @@ export const ExecuteComponent: ActionComponent = ({ amount={{ watch, setValue, + getValues, register, fieldName: fieldNamePrefix + 'funds.0.amount', error: errors?.funds?.[0]?.amount, diff --git a/packages/stateful/actions/core/actions/Execute/index.tsx b/packages/stateful/actions/core/actions/Execute/index.tsx index a65b2914a..c7e97eccd 100644 --- a/packages/stateful/actions/core/actions/Execute/index.tsx +++ b/packages/stateful/actions/core/actions/Execute/index.tsx @@ -206,10 +206,9 @@ export class ExecuteAction extends ActionBase { contractAddress: address, msg, funds: funds - .map(({ denom, amount, decimals }) => ({ - denom, - amount: HugeDecimal.fromHumanReadable(amount, decimals).toString(), - })) + .map(({ denom, amount, decimals }) => + HugeDecimal.fromHumanReadable(amount, decimals).toCoin(denom) + ) // Neutron errors with `invalid coins` if the funds list is not // alphabetized. .sort((a, b) => a.denom.localeCompare(b.denom)), @@ -362,14 +361,14 @@ export class ExecuteAction extends ActionBase { denom: decodedMessage.wasm.execute.contract_addr, amount: HugeDecimal.from( executeMsg.send.amount - ).toHumanReadableNumber(cw20TokenDecimals), + ).toHumanReadableString(cw20TokenDecimals), decimals: cw20TokenDecimals, }, ] : fundsTokens.map(({ denom, amount, decimals }) => ({ denom, amount: - HugeDecimal.from(amount).toHumanReadableNumber(decimals), + HugeDecimal.from(amount).toHumanReadableString(decimals), decimals, })), cw20: isCw20, @@ -400,14 +399,14 @@ export class ExecuteAction extends ActionBase { ), amount: HugeDecimal.from( executeMsg.send.amount - ).toHumanReadableNumber(cw20TokenDecimals), + ).toHumanReadableString(cw20TokenDecimals), decimals: cw20TokenDecimals, }, ] : fundsTokens.map(({ denom, amount, decimals }) => ({ denom, amount: - HugeDecimal.from(amount).toHumanReadableNumber(decimals), + HugeDecimal.from(amount).toHumanReadableString(decimals), decimals, })), cw20: isCw20, diff --git a/packages/stateful/actions/core/actions/FundRewardDistribution/README.md b/packages/stateful/actions/core/actions/FundRewardDistribution/README.md index ab54cf1dc..df51ecc47 100644 --- a/packages/stateful/actions/core/actions/FundRewardDistribution/README.md +++ b/packages/stateful/actions/core/actions/FundRewardDistribution/README.md @@ -17,6 +17,6 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). { "address": "
", "id": , - "amount": + "amount": "" } ``` diff --git a/packages/stateful/actions/core/actions/GovernanceDeposit/Component.stories.tsx b/packages/stateful/actions/core/actions/GovernanceDeposit/Component.stories.tsx index dd5fa5eb4..baf84db23 100644 --- a/packages/stateful/actions/core/actions/GovernanceDeposit/Component.stories.tsx +++ b/packages/stateful/actions/core/actions/GovernanceDeposit/Component.stories.tsx @@ -78,7 +78,7 @@ Default.args = { deposit: [ { denom: 'JUNOX', - amount: 1, + amount: '1', }, ], }, @@ -86,7 +86,7 @@ Default.args = { errors: {}, options: { proposals: [makeProposal(), makeProposal(), makeProposal(), makeProposal()], - depositTokens: { loading: false, data: [] }, + depositTokens: { loading: false, errored: false, data: [] }, TokenAmountDisplay, GovProposalActionDisplay, }, diff --git a/packages/stateful/actions/core/actions/GovernanceDeposit/Component.tsx b/packages/stateful/actions/core/actions/GovernanceDeposit/Component.tsx index ccdf09454..3b6b556ac 100644 --- a/packages/stateful/actions/core/actions/GovernanceDeposit/Component.tsx +++ b/packages/stateful/actions/core/actions/GovernanceDeposit/Component.tsx @@ -15,7 +15,7 @@ import { GenericToken, GovProposalActionDisplayProps, GovProposalWithDecodedContent, - LoadingData, + LoadingDataWithError, StatefulTokenAmountDisplayProps, } from '@dao-dao/types' import { ActionComponent } from '@dao-dao/types/actions' @@ -23,7 +23,7 @@ import { validateRequired } from '@dao-dao/utils' export type GovernanceDepositOptions = { proposals: GovProposalWithDecodedContent[] - depositTokens: LoadingData + depositTokens: LoadingDataWithError TokenAmountDisplay: ComponentType GovProposalActionDisplay: ComponentType } @@ -32,7 +32,7 @@ export type GovernanceDepositData = { chainId: string proposalId: string deposit: { - amount: number + amount: string denom: string }[] } @@ -53,13 +53,14 @@ export const GovernanceDepositComponent: ActionComponent< data, }) => { const { t } = useTranslation() - const { setValue, register, watch } = useFormContext() + const { setValue, register, getValues, watch } = + useFormContext() const proposalId = watch((fieldNamePrefix + 'proposalId') as 'proposalId') const proposalSelected = proposals.find((p) => p.id.toString() === proposalId) const selectedDepositToken = - depositTokens.loading || !data.deposit.length + depositTokens.loading || depositTokens.errored || !data.deposit.length ? undefined : depositTokens.data.find( ({ denomOrAddress }) => denomOrAddress === data.deposit[0].denom @@ -99,6 +100,7 @@ export const GovernanceDepositComponent: ActionComponent< amount={{ watch, setValue, + getValues, register, fieldName: (fieldNamePrefix + 'deposit.0.amount') as 'deposit.0.amount', @@ -109,7 +111,6 @@ export const GovernanceDepositComponent: ActionComponent< step: HugeDecimal.one.toHumanReadableNumber( selectedDepositToken?.decimals ?? 0 ), - convertMicroDenom: true, }} onSelectToken={({ denomOrAddress }) => setValue( @@ -119,7 +120,9 @@ export const GovernanceDepositComponent: ActionComponent< } readOnly={!isCreating} selectedToken={selectedDepositToken} - tokens={depositTokens} + tokens={ + depositTokens.errored ? { loading: false, data: [] } : depositTokens + } />
diff --git a/packages/stateful/actions/core/actions/GovernanceDeposit/README.md b/packages/stateful/actions/core/actions/GovernanceDeposit/README.md index 013f85e82..c7130ec5e 100644 --- a/packages/stateful/actions/core/actions/GovernanceDeposit/README.md +++ b/packages/stateful/actions/core/actions/GovernanceDeposit/README.md @@ -19,7 +19,7 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). "deposit": [ { "denom": "", - "amount": + "amount": "" } ] } diff --git a/packages/stateful/actions/core/actions/GovernanceDeposit/index.tsx b/packages/stateful/actions/core/actions/GovernanceDeposit/index.tsx index 297c82a72..3a2ba58d7 100644 --- a/packages/stateful/actions/core/actions/GovernanceDeposit/index.tsx +++ b/packages/stateful/actions/core/actions/GovernanceDeposit/index.tsx @@ -2,16 +2,15 @@ import { Coin } from '@cosmjs/stargate' import { useQueryClient } from '@tanstack/react-query' import { useEffect } from 'react' import { useFormContext } from 'react-hook-form' -import { waitForAll } from 'recoil' -import { chainQueries, genericTokenSelector } from '@dao-dao/state' +import { HugeDecimal } from '@dao-dao/math' +import { chainQueries, tokenQueries } from '@dao-dao/state' import { ActionBase, BankEmoji, ChainProvider, DaoSupportedChainPickerInput, useActionOptions, - useCachedLoading, useChain, } from '@dao-dao/stateless' import { @@ -37,7 +36,7 @@ import { import { GovProposalActionDisplay } from '../../../../components' import { TokenAmountDisplay } from '../../../../components/TokenAmountDisplay' -import { useQueryLoadingDataWithError } from '../../../../hooks' +import { useQueryLoadingDataWithError, useQueryTokens } from '../../../../hooks' import { GovActionsProvider } from '../../../providers/gov' import { GovernanceDepositData, @@ -129,8 +128,26 @@ const InnerComponent: ActionComponent = ( : undefined ) + const depositTokens = useQueryTokens( + context.params.minDeposit.map(({ denom }) => ({ + chainId, + type: TokenType.Native, + denomOrAddress: denom, + })) + ) + + const minDeposit = context.params.minDeposit[0] + const minDepositToken = + depositTokens.loading || depositTokens.errored + ? undefined + : depositTokens.data.find((t) => t.denomOrAddress === minDeposit.denom) + // On proposal change, update deposit to remaining needed. useEffect(() => { + if (!minDepositToken) { + return + } + const proposalSelected = proposalId && !proposalOptions.loading && @@ -140,24 +157,31 @@ const InnerComponent: ActionComponent = ( return } - const minDeposit = context.params.minDeposit[0] - const missingDeposit = - BigInt(minDeposit.amount) - - BigInt( - proposalSelected.proposal.totalDeposit.find( - ({ denom }) => minDeposit.denom === denom - )?.amount ?? 0 - ) + const missingDeposit = HugeDecimal.from(minDeposit.amount).minus( + proposalSelected.proposal.totalDeposit.find( + ({ denom }) => denom === minDeposit.denom + )?.amount ?? 0 + ) - if (missingDeposit > 0) { + if (missingDeposit.isPositive()) { setValue((fieldNamePrefix + 'deposit') as 'deposit', [ { denom: minDeposit.denom, - amount: Number(missingDeposit), + amount: missingDeposit.toHumanReadableString( + minDepositToken.decimals + ), }, ]) } - }, [proposalId, proposalOptions, context.params, setValue, fieldNamePrefix]) + }, [ + proposalId, + proposalOptions, + context.params, + setValue, + fieldNamePrefix, + minDepositToken, + minDeposit, + ]) // Select first proposal once loaded if nothing selected. useEffect(() => { @@ -175,19 +199,6 @@ const InnerComponent: ActionComponent = ( } }, [isCreating, proposalOptions, proposalId, setValue, fieldNamePrefix]) - const depositTokens = useCachedLoading( - waitForAll( - context.params.minDeposit.map(({ denom }) => - genericTokenSelector({ - type: TokenType.Native, - denomOrAddress: denom, - chainId, - }) - ) - ), - [] - ) - return ( { } } - encode({ + async encode({ chainId, proposalId, deposit, - }: GovernanceDepositData): UnifiedCosmosMsg[] { + }: GovernanceDepositData): Promise { const depositor = getChainAddressForActionOptions(this.options, chainId) if (!depositor) { throw new Error('Depositor address not found for chain.') } + const amount = await Promise.all( + deposit.map(async ({ denom, amount }) => { + const { decimals } = await this.options.queryClient.fetchQuery( + tokenQueries.info(this.options.queryClient, { + chainId, + type: TokenType.Native, + denomOrAddress: denom, + }) + ) + + return HugeDecimal.fromHumanReadable(amount, decimals).toCoin(denom) + }) + ) + return maybeMakePolytoneExecuteMessages( this.options.chain.chain_id, chainId, makeStargateMessage({ stargate: { typeUrl: MsgDeposit.typeUrl, - value: { + value: MsgDeposit.fromPartial({ proposalId: BigInt(proposalId || '0'), depositor, - amount: deposit.map(({ denom, amount }) => ({ - denom, - amount: BigInt(amount).toString(), - })), - } as MsgDeposit, + amount, + }), }, }) ) @@ -270,21 +292,35 @@ export class GovernanceDepositAction extends ActionBase { }) } - decode([ + async decode([ { decodedMessage, account: { chainId }, }, - ]: ProcessedMessage[]): GovernanceDepositData { + ]: ProcessedMessage[]): Promise { + const deposit = await Promise.all( + (decodedMessage.stargate.value.amount as Coin[]).map( + async ({ denom, amount }) => { + const { decimals } = await this.options.queryClient.fetchQuery( + tokenQueries.info(this.options.queryClient, { + chainId, + type: TokenType.Native, + denomOrAddress: denom, + }) + ) + + return { + denom, + amount: HugeDecimal.fromHumanReadable(amount, decimals).toString(), + } + } + ) + ) + return { chainId, proposalId: decodedMessage.stargate.value.proposalId.toString(), - deposit: (decodedMessage.stargate.value.amount as Coin[]).map( - ({ denom, amount }) => ({ - denom, - amount: Number(amount), - }) - ), + deposit, } } } diff --git a/packages/stateful/actions/core/actions/GovernanceProposal/Component.stories.tsx b/packages/stateful/actions/core/actions/GovernanceProposal/Component.stories.tsx index e2f86c195..2bd1852f5 100644 --- a/packages/stateful/actions/core/actions/GovernanceProposal/Component.stories.tsx +++ b/packages/stateful/actions/core/actions/GovernanceProposal/Component.stories.tsx @@ -38,16 +38,18 @@ Default.args = { metadata: '', deposit: [ { - amount: 100, + amount: '100', denom: 'ujunox', + decimals: 6, }, ], legacy: { typeUrl: SoftwareUpgradeProposal.typeUrl, spends: [ { - amount: 1, + amount: '1', denom: 'ujunox', + decimals: 6, }, ], spendRecipient: 'junoRecipient', @@ -73,7 +75,7 @@ Default.args = { isCreating: true, errors: {}, options: { - minDeposits: { loading: false, data: [] }, + minDeposits: { loading: false, errored: false, data: [] }, communityPoolBalances: { loading: false, data: [] }, encodeContext: { type: ActionContextType.Wallet, diff --git a/packages/stateful/actions/core/actions/GovernanceProposal/Component.tsx b/packages/stateful/actions/core/actions/GovernanceProposal/Component.tsx index 1172031e5..876a7c3a4 100644 --- a/packages/stateful/actions/core/actions/GovernanceProposal/Component.tsx +++ b/packages/stateful/actions/core/actions/GovernanceProposal/Component.tsx @@ -36,6 +36,7 @@ import { GovProposalActionDisplayProps, GovernanceProposalActionData, LoadingData, + LoadingDataWithError, StatefulTokenAmountDisplayProps, } from '@dao-dao/types' import { ActionComponent, ActionContextType } from '@dao-dao/types/actions' @@ -53,7 +54,7 @@ import { } from '@dao-dao/utils' export type GovernanceProposalOptions = { - minDeposits: LoadingData< + minDeposits: LoadingDataWithError< (GenericTokenBalance & { min: string })[] @@ -83,8 +84,15 @@ export const GovernanceProposalComponent: ActionComponent< } = props const { t } = useTranslation() - const { register, setValue, watch, control, setError, clearErrors } = - useFormContext() + const { + register, + setValue, + getValues, + watch, + control, + setError, + clearErrors, + } = useFormContext() const { context } = useActionOptions() // Type-check, this action should not be used in a non-gov action context. @@ -110,14 +118,20 @@ export const GovernanceProposalComponent: ActionComponent< nativeToken, } = useChainContext() - const selectedMinDepositToken = minDeposits.loading - ? undefined - : minDeposits.data.find( - ({ token }) => token.denomOrAddress === data.deposit[0].denom - ) - const depositMin = HugeDecimal.from(selectedMinDepositToken?.min ?? 0) - .times(context.params.minInitialDepositRatio) - .toHumanReadableNumber(selectedMinDepositToken?.token.decimals ?? 0) + const selectedMinDepositToken = + minDeposits.loading || minDeposits.errored + ? undefined + : minDeposits.data.find( + ({ token }) => token.denomOrAddress === data.deposit[0].denom + ) + const minDepositTokenDecimals = selectedMinDepositToken?.token.decimals ?? 0 + const depositVotingMin = HugeDecimal.from(selectedMinDepositToken?.min ?? 0) + const depositSubmitMin = depositVotingMin.times( + context.params.minInitialDepositRatio + ) + const depositTokenBalance = HugeDecimal.from( + selectedMinDepositToken?.balance ?? 0 + ) const { fields: spendFields, @@ -170,6 +184,10 @@ export const GovernanceProposalComponent: ActionComponent< const parsedUpgradePlan = JSON5.parse(upgradePlan) const parsedCustom = JSON5.parse(custom) + const spendAmount = spends.map(({ amount, denom, decimals }) => + HugeDecimal.fromHumanReadable(amount, decimals).toCoin(denom) + ) + const content = typeUrl === GOVERNANCE_PROPOSAL_TYPE_CUSTOM ? Cosmos_govv1beta1Content_FromAmino({ @@ -188,10 +206,7 @@ export const GovernanceProposalComponent: ActionComponent< title, description, // CommunityPoolSpendProposal - amount: spends.map(({ amount, denom }) => ({ - denom, - amount: BigInt(amount).toString(), - })), + amount: spendAmount, recipient: spendRecipient, // ParameterChangeProposal changes: JSON5.parse(parameterChanges), @@ -365,18 +380,14 @@ export const GovernanceProposalComponent: ActionComponent<

{t('info.govDepositDescription', { - amount: HugeDecimal.from(selectedMinDepositToken?.min ?? 0) - .toHumanReadableNumber( - selectedMinDepositToken?.token.decimals ?? 0 - ) - .toLocaleString(undefined, { - maximumFractionDigits: - selectedMinDepositToken?.token.decimals ?? 0, + amount: + depositVotingMin.toInternationalizedHumanReadableString({ + decimals: minDepositTokenDecimals, + }), + minAmount: + depositSubmitMin.toInternationalizedHumanReadableString({ + decimals: minDepositTokenDecimals, }), - minAmount: depositMin.toLocaleString(undefined, { - maximumFractionDigits: - selectedMinDepositToken?.token.decimals ?? 0, - }), symbol: selectedMinDepositToken?.token.symbol ?? t('info.tokens'), })} @@ -388,43 +399,53 @@ export const GovernanceProposalComponent: ActionComponent< amount={{ watch, setValue, + getValues, register, fieldName: (fieldNamePrefix + 'deposit.0.amount') as 'deposit.0.amount', error: errors?.deposit?.[0]?.amount, - min: depositMin, + min: depositSubmitMin.toHumanReadableNumber( + minDepositTokenDecimals + ), step: HugeDecimal.one.toHumanReadableNumber( - selectedMinDepositToken?.token.decimals ?? 0 + minDepositTokenDecimals ), - convertMicroDenom: true, // Validate that balance is sufficient to pay the deposit. validations: [ (value) => isNaN(value) || !selectedMinDepositToken || - Number(selectedMinDepositToken.balance) >= value || + depositTokenBalance.gte(value) || t('error.insufficientBalance', { - amount: HugeDecimal.from( - selectedMinDepositToken.balance - ).toHumanReadableNumber( - selectedMinDepositToken.token.decimals - ), + amount: + depositTokenBalance.toInternationalizedHumanReadableString( + { + decimals: minDepositTokenDecimals, + } + ), tokenSymbol: selectedMinDepositToken.token.symbol, }), ], }} - onSelectToken={({ denomOrAddress }) => + onSelectToken={({ denomOrAddress, decimals }) => { setValue( (fieldNamePrefix + 'deposit.0.denom') as 'deposit.0.denom', denomOrAddress ) - } + setValue( + (fieldNamePrefix + + 'deposit.0.decimals') as 'deposit.0.decimals', + decimals + ) + }} readOnly={!isCreating} selectedToken={selectedMinDepositToken?.token} tokens={ minDeposits.loading ? { loading: true } + : minDeposits.errored + ? { loading: false, data: [] } : { loading: false, data: minDeposits.data.map(({ token }) => token), @@ -558,30 +579,34 @@ export const GovernanceProposalComponent: ActionComponent< amount={{ watch, setValue, + getValues, register, fieldName: (fieldNamePrefix + `legacy.spends.${index}.amount`) as `legacy.spends.${number}.amount`, error: errors?.legacy?.spends?.[index]?.amount, - min: HugeDecimal.from( - 1 - ).toHumanReadableNumber( + min: HugeDecimal.one.toHumanReadableNumber( selectedToken.decimals ), - step: HugeDecimal.from( - 1 - ).toHumanReadableNumber( + step: HugeDecimal.one.toHumanReadableNumber( selectedToken.decimals ), - convertMicroDenom: true, }} - onSelectToken={({ denomOrAddress }) => + onSelectToken={({ + denomOrAddress, + decimals, + }) => { setValue( (fieldNamePrefix + `legacy.spends.${index}.denom`) as `legacy.spends.${number}.denom`, denomOrAddress ) - } + setValue( + (fieldNamePrefix + + `legacy.spends.${index}.decimals`) as `legacy.spends.${number}.decimals`, + decimals + ) + }} selectedToken={selectedToken} tokens={{ loading: false, @@ -604,7 +629,7 @@ export const GovernanceProposalComponent: ActionComponent< className="self-start" onClick={() => appendSpend({ - amount: 1, + amount: '1', denom: nativeToken?.denomOrAddress || '', }) } @@ -704,9 +729,9 @@ export const GovernanceProposalComponent: ActionComponent< GovProposalActionDisplay={GovProposalActionDisplay} TokenAmountDisplay={TokenAmountDisplay} content={govProposalActionDataToDecodedContent(data)} - deposit={data.deposit.map(({ denom, amount }) => ({ + deposit={data.deposit.map(({ denom, amount, decimals }) => ({ denom, - amount: isNaN(amount) ? '0' : BigInt(amount).toString(), + amount: HugeDecimal.fromHumanReadable(amount, decimals).toString(), }))} /> )} diff --git a/packages/stateful/actions/core/actions/GovernanceProposal/README.md b/packages/stateful/actions/core/actions/GovernanceProposal/README.md index 1656d584a..2b6087376 100644 --- a/packages/stateful/actions/core/actions/GovernanceProposal/README.md +++ b/packages/stateful/actions/core/actions/GovernanceProposal/README.md @@ -21,13 +21,15 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). "deposit": [ { "denom": "", - "amount": + "amount": "", + "decimals": } ], "spends": [ { "denom": "", - "amount": + "amount": "", + "decimals": }, ... ], diff --git a/packages/stateful/actions/core/actions/GovernanceProposal/index.tsx b/packages/stateful/actions/core/actions/GovernanceProposal/index.tsx index 4512de17d..098371de0 100644 --- a/packages/stateful/actions/core/actions/GovernanceProposal/index.tsx +++ b/packages/stateful/actions/core/actions/GovernanceProposal/index.tsx @@ -3,11 +3,12 @@ import { MutableRefObject, useEffect, useRef } from 'react' import { useFormContext } from 'react-hook-form' import { waitForAll } from 'recoil' +import { HugeDecimal } from '@dao-dao/math' import { chainQueries, communityPoolBalancesSelector, genericTokenBalanceSelector, - genericTokenSelector, + tokenQueries, } from '@dao-dao/state' import { ActionBase, @@ -16,6 +17,7 @@ import { RaisedHandEmoji, useActionOptions, useCachedLoading, + useCachedLoadingWithError, } from '@dao-dao/stateless' import { AccountType, @@ -138,8 +140,42 @@ const InnerComponent = ({ ? context.params.expeditedMinDeposit : context.params.minDeposit + // Get tokens and balances for all deposit tokens. + const minDeposits = useCachedLoadingWithError( + address + ? waitForAll( + minDepositParams.map(({ denom }) => + genericTokenBalanceSelector({ + address, + type: TokenType.Native, + denomOrAddress: denom, + chainId, + }) + ) + ) + : undefined, + (data) => + data.map(({ token, balance }, index) => ({ + token, + balance, + // Min deposit required. + min: minDepositParams[index].amount, + })) + ) + + const minDepositToken = + minDeposits.loading || minDeposits.errored || !minDepositParams.length + ? undefined + : minDeposits.data.find( + ({ token }) => token.denomOrAddress === minDepositParams[0].denom + ) + // On chain or min deposit change, reset deposit, except first load. useEffect(() => { + if (!minDepositToken) { + return + } + if (!firstDepositSet.current) { firstDepositSet.current = true return @@ -148,7 +184,10 @@ const InnerComponent = ({ setValue((props.fieldNamePrefix + 'deposit') as 'deposit', [ { denom: minDepositParams[0].denom, - amount: Number(minDepositParams[0].amount), + amount: HugeDecimal.from( + minDepositParams[0].amount + ).toHumanReadableString(minDepositToken.token.decimals), + decimals: minDepositToken.token.decimals, }, ]) }, [ @@ -157,6 +196,7 @@ const InnerComponent = ({ props.fieldNamePrefix, minDepositParams, firstDepositSet, + minDepositToken, ]) // Update version in data. @@ -169,56 +209,11 @@ const InnerComponent = ({ ) }, [setValue, props.fieldNamePrefix, context.params.supportsV1]) - // Get token info for all deposit tokens. - const minDepositTokens = useCachedLoading( - waitForAll( - minDepositParams.map(({ denom }) => - genericTokenSelector({ - type: TokenType.Native, - denomOrAddress: denom, - chainId, - }) - ) - ), - [] - ) - - // Get address balances for all deposit tokens when wallet connected. - const minDepositBalances = useCachedLoading( - address - ? waitForAll( - minDepositParams.map(({ denom }) => - genericTokenBalanceSelector({ - address, - type: TokenType.Native, - denomOrAddress: denom, - chainId, - }) - ) - ) - : undefined, - [] - ) - return ( ({ - token, - // Wallet's balance or 0 if not loaded. - balance: - (!minDepositBalances.loading && - minDepositBalances.data[index]?.balance) || - '0', - // Min deposit required. - min: minDepositParams[index].amount, - })), - }, + minDeposits, communityPoolBalances, encodeContext, TokenAmountDisplay, @@ -317,6 +312,14 @@ export class GovernanceProposalAction extends ActionBase + HugeDecimal.fromHumanReadable(amount, decimals).toCoin(denom) + ) + ) + const msg = supportsV1 ? makeStargateMessage({ stargate: { typeUrl: MsgSubmitProposalV1.typeUrl, - value: { + value: MsgSubmitProposalV1.fromPartial({ messages: useV1LegacyContent ? [ MsgExecLegacyContent.toProtoMsg({ @@ -399,10 +412,7 @@ export class GovernanceProposalAction extends ActionBase cwMsgToProtobuf(chainId, msg, govModuleAddress) ), - initialDeposit: deposit.map(({ amount, denom }) => ({ - amount: BigInt(amount).toString(), - denom, - })), + initialDeposit, proposer, title, summary: description, @@ -410,20 +420,17 @@ export class GovernanceProposalAction extends ActionBase ({ - amount: BigInt(amount).toString(), - denom, - })), + initialDeposit, proposer, - } as MsgSubmitProposalV1Beta1, + }), }, }) @@ -464,12 +471,12 @@ export class GovernanceProposalAction extends ActionBase { + ]: ProcessedMessage[]): Promise> { if (decodedMessage.stargate.typeUrl === MsgSubmitProposalV1Beta1.typeUrl) { const proposal = decodedMessage.stargate.value as MsgSubmitProposalV1Beta1 const typeUrl = proposal.content?.typeUrl @@ -485,27 +492,59 @@ export class GovernanceProposalAction extends ActionBase { + const { decimals } = await this.options.queryClient.fetchQuery( + tokenQueries.info(this.options.queryClient, { + chainId, + type: TokenType.Native, + denomOrAddress: denom, + }) + ) + + return { + denom, + amount: HugeDecimal.from(amount).toHumanReadableString(decimals), + decimals, + } + }) + ) + + const spends = + proposal.content.typeUrl === CommunityPoolSpendProposal.typeUrl + ? await Promise.all( + (proposal.content.amount as Coin[]).map( + async ({ denom, amount }) => { + const { decimals } = + await this.options.queryClient.fetchQuery( + tokenQueries.info(this.options.queryClient, { + chainId, + type: TokenType.Native, + denomOrAddress: denom, + }) + ) + + return { + denom, + amount: + HugeDecimal.from(amount).toHumanReadableString(decimals), + decimals, + } + } + ) + ) + : [] + return { chainId, version: GovProposalVersion.V1_BETA_1, title: proposal.content.title, description: proposal.content.description, metadata: '', - deposit: proposal.initialDeposit.map(({ amount, ...coin }) => ({ - ...coin, - amount: Number(amount), - })), + deposit, legacy: { typeUrl, - spends: - proposal.content.typeUrl === CommunityPoolSpendProposal.typeUrl - ? (proposal.content.amount as Coin[]).map( - ({ amount, denom }) => ({ - amount: Number(amount), - denom, - }) - ) - : [], + spends, spendRecipient: proposal.content.typeUrl === CommunityPoolSpendProposal.typeUrl ? proposal.content.recipient @@ -531,16 +570,31 @@ export class GovernanceProposalAction extends ActionBase { + const { decimals } = await this.options.queryClient.fetchQuery( + tokenQueries.info(this.options.queryClient, { + chainId, + type: TokenType.Native, + denomOrAddress: denom, + }) + ) + + return { + denom, + amount: HugeDecimal.from(amount).toHumanReadableString(decimals), + decimals, + } + }) + ) + return { chainId, version: GovProposalVersion.V1, title: proposal.title, description: proposal.summary, metadata: proposal.metadata, - deposit: proposal.initialDeposit.map(({ amount, ...coin }) => ({ - ...coin, - amount: Number(amount), - })), + deposit, msgs: decodedMessages, expedited: proposal.expedited || false, } diff --git a/packages/stateful/actions/core/actions/Instantiate/Component.tsx b/packages/stateful/actions/core/actions/Instantiate/Component.tsx index 8ca1b16f6..c6c80562f 100644 --- a/packages/stateful/actions/core/actions/Instantiate/Component.tsx +++ b/packages/stateful/actions/core/actions/Instantiate/Component.tsx @@ -41,7 +41,7 @@ export type InstantiateData = { message: string funds: { denom: string - amount: number + amount: string // Will multiply `amount` by 10^decimals when generating the message. decimals: number }[] diff --git a/packages/stateful/actions/core/actions/Instantiate/index.tsx b/packages/stateful/actions/core/actions/Instantiate/index.tsx index b378653a2..455d57570 100644 --- a/packages/stateful/actions/core/actions/Instantiate/index.tsx +++ b/packages/stateful/actions/core/actions/Instantiate/index.tsx @@ -300,10 +300,9 @@ export class InstantiateAction extends ActionBase { const msg = JSON5.parse(message) const convertedFunds = funds - .map(({ denom, amount, decimals }) => ({ - denom, - amount: HugeDecimal.fromHumanReadable(amount, decimals).toString(), - })) + .map(({ denom, amount, decimals }) => + HugeDecimal.fromHumanReadable(amount, decimals).toCoin(denom) + ) // Neutron errors with `invalid coins` if the funds list is not // alphabetized. .sort((a, b) => a.denom.localeCompare(b.denom)) @@ -428,7 +427,7 @@ export class InstantiateAction extends ActionBase { message: JSON.stringify(decodedMessage.wasm.instantiate.msg, null, 2), funds: fundsTokens.map(({ denom, amount, decimals }) => ({ denom, - amount: HugeDecimal.from(amount).toHumanReadableNumber(decimals), + amount: HugeDecimal.from(amount).toHumanReadableString(decimals), decimals, })), _polytone: polytone @@ -456,7 +455,7 @@ export class InstantiateAction extends ActionBase { ), funds: fundsTokens.map(({ denom, amount, decimals }) => ({ denom, - amount: HugeDecimal.from(amount).toHumanReadableNumber(decimals), + amount: HugeDecimal.from(amount).toHumanReadableString(decimals), decimals, })), _polytone: polytone diff --git a/packages/stateful/actions/core/actions/Instantiate2/Component.tsx b/packages/stateful/actions/core/actions/Instantiate2/Component.tsx index 10ef0b65e..7573235e0 100644 --- a/packages/stateful/actions/core/actions/Instantiate2/Component.tsx +++ b/packages/stateful/actions/core/actions/Instantiate2/Component.tsx @@ -43,7 +43,7 @@ export type Instantiate2Data = { salt: string funds: { denom: string - amount: number + amount: string // Will multiply `amount` by 10^decimals when generating the message. decimals: number }[] diff --git a/packages/stateful/actions/core/actions/Instantiate2/index.tsx b/packages/stateful/actions/core/actions/Instantiate2/index.tsx index b0a843538..7cce8390c 100644 --- a/packages/stateful/actions/core/actions/Instantiate2/index.tsx +++ b/packages/stateful/actions/core/actions/Instantiate2/index.tsx @@ -203,10 +203,9 @@ export class Instantiate2Action extends ActionBase { const msg = JSON5.parse(message) const convertedFunds = funds - .map(({ denom, amount, decimals }) => ({ - denom, - amount: HugeDecimal.fromHumanReadable(amount, decimals).toString(), - })) + .map(({ denom, amount, decimals }) => + HugeDecimal.fromHumanReadable(amount, decimals).toCoin(denom) + ) // Neutron errors with `invalid coins` if the funds list is not // alphabetized. .sort((a, b) => a.denom.localeCompare(b.denom)) @@ -314,7 +313,7 @@ export class Instantiate2Action extends ActionBase { salt: data.salt, funds: fundsTokens.map(({ denom, amount, decimals }) => ({ denom, - amount: HugeDecimal.from(amount).toHumanReadableNumber(decimals), + amount: HugeDecimal.from(amount).toHumanReadableString(decimals), decimals, })), } diff --git a/packages/stateful/actions/core/actions/ManageStaking/Component.tsx b/packages/stateful/actions/core/actions/ManageStaking/Component.tsx index e16fe7311..67c872d56 100644 --- a/packages/stateful/actions/core/actions/ManageStaking/Component.tsx +++ b/packages/stateful/actions/core/actions/ManageStaking/Component.tsx @@ -252,13 +252,13 @@ export const ManageStakingComponent: ActionComponent< // high. We don't want to make this an error because often people want to // spend funds that a previous action makes available, so just show a warning. const delegateWarning = - isCreating && maxAmount.lt(amount) && type === StakingActionType.Delegate + isCreating && + maxAmount.toHumanReadable(nativeToken.decimals).lt(amount) && + type === StakingActionType.Delegate ? t('error.insufficientFundsWarning', { - amount: HugeDecimal.from(maxAmount) - .toHumanReadableNumber(nativeToken.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: nativeToken.decimals, - }), + amount: maxAmount.toInternationalizedHumanReadableString({ + decimals: nativeToken.decimals, + }), tokenSymbol: nativeToken.symbol, }) : undefined diff --git a/packages/stateful/actions/core/actions/ManageStaking/README.md b/packages/stateful/actions/core/actions/ManageStaking/README.md index 94b72e928..5ef7d32e0 100644 --- a/packages/stateful/actions/core/actions/ManageStaking/README.md +++ b/packages/stateful/actions/core/actions/ManageStaking/README.md @@ -21,7 +21,7 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). "toValidator": "", // Only used when setting withdraw address. "withdrawAddress": "", - "amount": + "amount": "" } ``` diff --git a/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx b/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx index afd24e57d..5538130e1 100644 --- a/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx +++ b/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx @@ -65,7 +65,7 @@ import { export type BeginVestingData = { chainId: string - amount: number + amount: string type: TokenType denomOrAddress: string recipient: string @@ -215,7 +215,9 @@ export const BeginVesting: ActionComponent = ({ ...acc, { timestamp: lastMs + delayMs, - amount: lastAmount.plus((percent / 100) * watchAmount), + amount: lastAmount.plus( + HugeDecimal.from(watchAmount).times(percent).div(100) + ), }, ] }, [] as VestingStep[]) @@ -244,11 +246,7 @@ export const BeginVesting: ActionComponent = ({ ({ token: { denomOrAddress } }) => denomOrAddress === watchDenomOrAddress ) const selectedDecimals = selectedToken?.token.decimals ?? 0 - const selectedMicroBalance = selectedToken?.balance ?? 0 - const selectedBalance = - HugeDecimal.from(selectedMicroBalance).toHumanReadableNumber( - selectedDecimals - ) + const selectedBalance = HugeDecimal.from(selectedToken?.balance ?? 0) const selectedSymbol = selectedToken?.token?.symbol ?? t('info.tokens') const configureVestingPaymentAction = useInitializedActionForKey( @@ -295,16 +293,17 @@ export const BeginVesting: ActionComponent = ({ // A warning if the amount is too high. We don't want to make this an error // because often people want to spend funds that a previous action makes // available, so just show a warning. - const insufficientFundsWarning = - watchAmount > selectedBalance - ? t('error.insufficientFundsWarning', { - amount: selectedBalance.toLocaleString(undefined, { - maximumFractionDigits: selectedDecimals, - }), - tokenSymbol: - selectedToken?.token.symbol ?? t('info.token').toLocaleUpperCase(), - }) - : undefined + const insufficientFundsWarning = selectedBalance + .toHumanReadable(selectedDecimals) + .lt(watchAmount) + ? t('error.insufficientFundsWarning', { + amount: selectedBalance.toInternationalizedHumanReadableString({ + decimals: selectedDecimals, + }), + tokenSymbol: + selectedToken?.token.symbol ?? t('info.token').toLocaleUpperCase(), + }) + : undefined return ( @@ -374,6 +373,7 @@ export const BeginVesting: ActionComponent = ({ amount={{ watch, setValue, + getValues, register, fieldName: (fieldNamePrefix + 'amount') as 'amount', error: errors?.amount, @@ -402,11 +402,11 @@ export const BeginVesting: ActionComponent = ({ description: t('title.balance') + ': ' + - HugeDecimal.from(balance) - .toHumanReadableNumber(token.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: token.decimals, - }), + HugeDecimal.from( + balance + ).toInternationalizedHumanReadableString({ + decimals: token.decimals, + }), })), }} /> diff --git a/packages/stateful/actions/core/actions/ManageVesting/README.md b/packages/stateful/actions/core/actions/ManageVesting/README.md index b59a12864..bdd5b064d 100644 --- a/packages/stateful/actions/core/actions/ManageVesting/README.md +++ b/packages/stateful/actions/core/actions/ManageVesting/README.md @@ -17,22 +17,39 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). { "mode": "", "begin": { - "amount": , + "chainId": "", + "amount": "", + "type": "", "denomOrAddress": "", "recipient": "", "title": "", // Optional. "description": "<DESCRIPTION>", "startDate": "<START DATE>", - "duration": { - "value": <DURATION VALUE>, - "units": "<seconds | minutes | hours | days | weeks | months | years>" - } + "ownerMode": "<none | me | other | many>", + "otherOwner": "<ADDRESS>", + "manyOwners": [ + { "address": "<ADDRESS>" }, + ... + ], + "manyOwnersCw1WhitelistContract": "<MANY OWNER CW1-WHITELIST ADDRESS>", + "steps": [ + { + "percent": <PERCENT>, + "delay": { + "value": <DURATION VALUE>, + "units": "<seconds | minutes | hours | days | weeks | months | years>" + } + }, + ... + ] }, "cancel": { + "chainId": "<CHAIN ID>", "address": "<VESTING PAYMENT ADDRESS>" }, "registerSlash": { + "chainId": "<CHAIN ID>", "address": "<VESTING PAYMENT ADDRESS>", "valiator": "<VALIDATOR ADDRESS>", "time": "<TIME OF SLASH>", diff --git a/packages/stateful/actions/core/actions/ManageVesting/index.tsx b/packages/stateful/actions/core/actions/ManageVesting/index.tsx index bed9d32d2..238cb8064 100644 --- a/packages/stateful/actions/core/actions/ManageVesting/index.tsx +++ b/packages/stateful/actions/core/actions/ManageVesting/index.tsx @@ -500,7 +500,7 @@ export class ManageVestingAction extends ActionBase<ManageVestingData> { mode: this.widgetData ? 'begin' : 'cancel', begin: { chainId: this.options.chain.chain_id, - amount: 1, + amount: '1', type: TokenType.Native, denomOrAddress: getNativeTokenForChainId(this.options.chain.chain_id) .denomOrAddress, @@ -952,7 +952,7 @@ export class ManageVestingAction extends ActionBase<ManageVestingData> { ).toISOString() : '', title: instantiateMsg.title, - amount: HugeDecimal.from(instantiateMsg.total).toHumanReadableNumber( + amount: HugeDecimal.from(instantiateMsg.total).toHumanReadableString( token.decimals ), ownerMode, @@ -1007,13 +1007,11 @@ export class ManageVestingAction extends ActionBase<ManageVestingData> { return [ ...acc, { - percent: Number( - ( - ((Number(amount) - Number(pastAmount)) / - Number(instantiateMsg!.total)) * - 100 - ).toFixed(2) - ), + percent: HugeDecimal.from(amount) + .minus(pastAmount) + .div(instantiateMsg!.total) + .times(100) + .toNumber(2), delay: convertSecondsToDurationWithUnits( seconds - pastTimestamp ), diff --git a/packages/stateful/actions/core/actions/Spend/Component.stories.tsx b/packages/stateful/actions/core/actions/Spend/Component.stories.tsx index 96ef861b7..817461a13 100644 --- a/packages/stateful/actions/core/actions/Spend/Component.stories.tsx +++ b/packages/stateful/actions/core/actions/Spend/Component.stories.tsx @@ -21,7 +21,7 @@ export default { toChainId: CHAIN_ID, from: '', to: '', - amount: 1, + amount: '1', cw20: false, denom: getNativeTokenForChainId(CHAIN_ID).denomOrAddress, }), diff --git a/packages/stateful/actions/core/actions/Spend/Component.tsx b/packages/stateful/actions/core/actions/Spend/Component.tsx index a25a0ccda..641fefc57 100644 --- a/packages/stateful/actions/core/actions/Spend/Component.tsx +++ b/packages/stateful/actions/core/actions/Spend/Component.tsx @@ -1,5 +1,4 @@ import { ArrowRightAltRounded } from '@mui/icons-material' -import clsx from 'clsx' import { ComponentType, RefAttributes, useEffect, useState } from 'react' import { useFormContext } from 'react-hook-form' import { useTranslation } from 'react-i18next' @@ -74,7 +73,7 @@ export type SpendData = { */ from: string to: string - amount: number + amount: string denom: string /** * Whether or not `denom` is a CW20 token address. CW20 tokens cannot be sent @@ -331,6 +330,7 @@ export const SpendComponent: ActionComponent<SpendOptions> = ({ amount={{ watch, setValue, + getValues, register, fieldName: (fieldNamePrefix + 'amount') as 'amount', error: errors?.amount, @@ -343,7 +343,7 @@ export const SpendComponent: ActionComponent<SpendOptions> = ({ : undefined, unitClassName: '!text-text-primary', }} - containerClassName={clsx('grow !max-w-full')} + containerClassName="grow !max-w-full" onCustomTokenChange={(custom) => { setValue((fieldNamePrefix + 'denom') as 'denom', custom) // If denom entered is a valid contract address, it's most @@ -480,7 +480,7 @@ export const SpendComponent: ActionComponent<SpendOptions> = ({ onClick={() => setValue( (fieldNamePrefix + 'amount') as 'amount', - balance.toHumanReadableNumber(decimals) + balance.toHumanReadableString(decimals) ) } showFullAmount @@ -502,7 +502,7 @@ export const SpendComponent: ActionComponent<SpendOptions> = ({ setAmount={(amount) => setValue( (fieldNamePrefix + 'amount') as 'amount', - amount.toHumanReadableNumber(decimals) + amount.toHumanReadableString(decimals) ) } /> @@ -566,7 +566,7 @@ export const SpendComponent: ActionComponent<SpendOptions> = ({ ) : ( <div className="flex flex-row gap-3 items-center"> <TokenAmountDisplay - amount={spendAmount} + amount={HugeDecimal.fromHumanReadable(spendAmount, decimals)} decimals={decimals} iconClassName="!h-6 !w-6" iconUrl={ @@ -881,26 +881,27 @@ export const SpendComponent: ActionComponent<SpendOptions> = ({ type NobleTariffProps = { token: GenericToken - amount: number + amount: string params: NobleTariffParams } const NobleTariff = ({ token: { symbol, decimals }, - amount, + amount: _amount, params: { transferFeeBps, transferFeeMax }, }: NobleTariffProps) => { const { t } = useTranslation() + const amount = HugeDecimal.fromHumanReadable(_amount, decimals) + const feeDecimal = Number(transferFeeBps) / 1e4 - const maxFee = - HugeDecimal.from(transferFeeMax).toHumanReadableNumber(decimals) + const maxFee = HugeDecimal.from(transferFeeMax) const fee = - amount && !isNaN(amount) - ? Math.min(Number((amount * feeDecimal).toFixed(decimals)), maxFee) - : 0 + _amount && !amount.isNaN() + ? HugeDecimal.min(amount.times(feeDecimal), maxFee) + : HugeDecimal.zero - if (fee === 0) { + if (fee.isZero()) { return null } @@ -909,14 +910,14 @@ const NobleTariff = ({ {t('info.nobleTariffApplied', { feePercent: formatPercentOf100(feeDecimal * 100), tokenSymbol: symbol, - maxFee: maxFee.toLocaleString(undefined, { - maximumFractionDigits: decimals, + maxFee: maxFee.toInternationalizedHumanReadableString({ + decimals, }), - fee: fee.toLocaleString(undefined, { - maximumFractionDigits: decimals, + fee: fee.toInternationalizedHumanReadableString({ + decimals, }), - output: (amount - fee).toLocaleString(undefined, { - maximumFractionDigits: decimals, + output: amount.minus(fee).toInternationalizedHumanReadableString({ + decimals, }), })} </p> diff --git a/packages/stateful/actions/core/actions/Spend/README.md b/packages/stateful/actions/core/actions/Spend/README.md index 3678c9263..1e2e40c48 100644 --- a/packages/stateful/actions/core/actions/Spend/README.md +++ b/packages/stateful/actions/core/actions/Spend/README.md @@ -19,7 +19,7 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). "toChainId": "<CHAIN ID>", "from": "<FROM ADDRESS>", "to": "<RECIPIENT ADDRESS>", - "amount": <AMOUNT>, + "amount": "<AMOUNT>", "denom": "<DENOM>", "cw20": "<true | false>", "ibcTimeout": { diff --git a/packages/stateful/actions/core/actions/Spend/index.tsx b/packages/stateful/actions/core/actions/Spend/index.tsx index 59c211d1c..8b9b6eb15 100644 --- a/packages/stateful/actions/core/actions/Spend/index.tsx +++ b/packages/stateful/actions/core/actions/Spend/index.tsx @@ -556,7 +556,7 @@ export class SpendAction extends ActionBase<SpendData> { toChainId: this.options.chain.chain_id, from: this.options.address, to: '', - amount: 1, + amount: '1', denom: nativeToken?.denomOrAddress || '', cw20: nativeToken?.type === TokenType.Cw20, ibcTimeout: { @@ -738,7 +738,7 @@ export class SpendAction extends ActionBase<SpendData> { } } else if (!cw20) { msg = { - bank: makeBankMessage(amount.toFixed(0), to, denom), + bank: makeBankMessage(amount.toString(), to, denom), } } else { msg = makeWasmMessage({ @@ -749,7 +749,7 @@ export class SpendAction extends ActionBase<SpendData> { msg: { transfer: { recipient: to, - amount: amount.toFixed(0), + amount: amount.toString(), }, }, }, @@ -1001,7 +1001,7 @@ export class SpendAction extends ActionBase<SpendData> { to, amount: HugeDecimal.from( decodedMessage.stargate.value.token.amount - ).toHumanReadableNumber(token.decimals), + ).toHumanReadableString(token.decimals), denom: token.denomOrAddress, // Should always be false. cw20: token.type === TokenType.Cw20, @@ -1024,7 +1024,7 @@ export class SpendAction extends ActionBase<SpendData> { to: decodedMessage.bank.send.to_address, amount: HugeDecimal.from( decodedMessage.bank.send.amount[0].amount - ).toHumanReadableNumber(token.decimals), + ).toHumanReadableString(token.decimals), denom: token.denomOrAddress, cw20: false, } @@ -1036,7 +1036,7 @@ export class SpendAction extends ActionBase<SpendData> { to: decodedMessage.wasm.execute.msg.transfer.recipient, amount: HugeDecimal.from( decodedMessage.wasm.execute.msg.transfer.amount - ).toHumanReadableNumber(token.decimals), + ).toHumanReadableString(token.decimals), denom: decodedMessage.wasm.execute.contract_addr, cw20: true, } @@ -1048,8 +1048,8 @@ export class SpendAction extends ActionBase<SpendData> { transformImportData(data: any): SpendData { return { ...data, - // Ensure amount is a number. - amount: Number(data.amount), + // Ensure amount is a string. + amount: HugeDecimal.from(data.amount).toString(), } } } diff --git a/packages/stateful/actions/core/actions/token_swap/stateful/ChooseExistingTokenSwap.tsx b/packages/stateful/actions/core/actions/token_swap/stateful/ChooseExistingTokenSwap.tsx index e4e9254df..ea47a8617 100644 --- a/packages/stateful/actions/core/actions/token_swap/stateful/ChooseExistingTokenSwap.tsx +++ b/packages/stateful/actions/core/actions/token_swap/stateful/ChooseExistingTokenSwap.tsx @@ -152,7 +152,7 @@ export const ChooseExistingTokenSwap: ActionComponent< 'cw20' in selfParty.promise ? selfParty.promise.cw20.amount : selfParty.promise.native.amount - ).toHumanReadableNumber(selfPartyTokenInfo.decimals), + ).toHumanReadableString(selfPartyTokenInfo.decimals), decimals: selfPartyTokenInfo.decimals, }) diff --git a/packages/stateful/actions/core/actions/token_swap/stateful/InstantiateTokenSwap.tsx b/packages/stateful/actions/core/actions/token_swap/stateful/InstantiateTokenSwap.tsx index 0ac84a77a..79238457f 100644 --- a/packages/stateful/actions/core/actions/token_swap/stateful/InstantiateTokenSwap.tsx +++ b/packages/stateful/actions/core/actions/token_swap/stateful/InstantiateTokenSwap.tsx @@ -207,7 +207,7 @@ const InnerInstantiateTokenSwap: ActionComponent< denomOrAddress: selfPartyDefaultCw20 ? selfPartyDefaultCw20.token.denomOrAddress : nativeToken.denomOrAddress, - amount: 0, + amount: '0', decimals: selfPartyDefaultCw20 ? selfPartyDefaultCw20.token.decimals : nativeToken.decimals, @@ -218,7 +218,7 @@ const InnerInstantiateTokenSwap: ActionComponent< address: '', type: 'native', denomOrAddress: nativeToken.denomOrAddress, - amount: 0, + amount: '0', decimals: nativeToken.decimals, }, }) diff --git a/packages/stateful/actions/core/actions/token_swap/stateless/InstantiateTokenSwap.stories.tsx b/packages/stateful/actions/core/actions/token_swap/stateless/InstantiateTokenSwap.stories.tsx index 6909969c7..fce8cf172 100644 --- a/packages/stateful/actions/core/actions/token_swap/stateless/InstantiateTokenSwap.stories.tsx +++ b/packages/stateful/actions/core/actions/token_swap/stateless/InstantiateTokenSwap.stories.tsx @@ -24,13 +24,13 @@ export default { selfParty: { type: TokenType.Cw20, denomOrAddress: 'cw20_1', - amount: 0, + amount: '0', decimals: 6, }, counterparty: { ...getNativeTokenForChainId(CHAIN_ID), address: '', - amount: 0, + amount: '0', }, }), makeDaoProvidersDecorator(makeDaoInfo()), diff --git a/packages/stateful/actions/core/actions/token_swap/stateless/InstantiateTokenSwap.tsx b/packages/stateful/actions/core/actions/token_swap/stateless/InstantiateTokenSwap.tsx index 526063e84..47b650b4b 100644 --- a/packages/stateful/actions/core/actions/token_swap/stateless/InstantiateTokenSwap.tsx +++ b/packages/stateful/actions/core/actions/token_swap/stateless/InstantiateTokenSwap.tsx @@ -37,7 +37,7 @@ export const InstantiateTokenSwap: ActionComponent< const { chain: { bech32_prefix: bech32Prefix }, } = useActionOptions() - const { register, watch, setValue, trigger } = useFormContext() + const { register, watch, setValue, getValues, trigger } = useFormContext() const selfParty = watch(fieldNamePrefix + 'selfParty') const counterparty = watch(fieldNamePrefix + 'counterparty') @@ -46,10 +46,7 @@ export const InstantiateTokenSwap: ActionComponent< ({ token }) => selfParty.denomOrAddress === token.denomOrAddress ) const selfDecimals = selfToken?.token.decimals ?? 0 - const selfMin = HugeDecimal.one.toHumanReadableNumber(selfDecimals) - const selfMax = HugeDecimal.from( - selfToken?.balance ?? 0 - ).toHumanReadableNumber(selfDecimals) + const selfMax = HugeDecimal.from(selfToken?.balance ?? 0) const selfSymbol = selfToken?.token.symbol ?? t('info.tokens') const counterpartyToken = counterpartyTokenBalances.loading @@ -58,11 +55,7 @@ export const InstantiateTokenSwap: ActionComponent< ({ token }) => counterparty.denomOrAddress === token.denomOrAddress ) const counterpartyDecimals = counterpartyToken?.token.decimals ?? 0 - const counterpartyMin = - HugeDecimal.one.toHumanReadableNumber(counterpartyDecimals) - const counterpartyMax = HugeDecimal.from( - counterpartyToken?.balance ?? 0 - ).toHumanReadableNumber(counterpartyDecimals) + const counterpartyMax = HugeDecimal.from(counterpartyToken?.balance ?? 0) const counterpartySymbol = counterpartyToken?.token.symbol ?? t('info.tokens') const counterpartyAddressValid = @@ -109,11 +102,12 @@ export const InstantiateTokenSwap: ActionComponent< amount={{ watch, setValue, + getValues, register, fieldName: fieldNamePrefix + 'counterparty.amount', error: errors?.counterparty?.amount, - min: counterpartyMin, - step: counterpartyMin, + min: HugeDecimal.one.toHumanReadableNumber(counterpartyDecimals), + step: HugeDecimal.one.toHumanReadableNumber(counterpartyDecimals), }} disabled={!counterpartyAddressValid} onSelectToken={({ type, denomOrAddress, decimals }) => { @@ -143,11 +137,13 @@ export const InstantiateTokenSwap: ActionComponent< /> {/* Warn if counterparty does not have the requested amount. */} - {counterparty.amount > counterpartyMax && ( + {counterpartyMax + .toHumanReadable(counterpartyDecimals) + .lt(counterparty.amount) && ( <p className="caption-text text-text-interactive-warning-body"> {t('error.counterpartyBalanceInsufficient', { - amount: counterpartyMax.toLocaleString(undefined, { - maximumFractionDigits: counterpartyDecimals, + amount: counterpartyMax.toInternationalizedHumanReadableString({ + decimals: counterpartyDecimals, }), tokenSymbol: counterpartySymbol, })} @@ -171,18 +167,19 @@ export const InstantiateTokenSwap: ActionComponent< amount={{ watch, setValue, + getValues, register, fieldName: fieldNamePrefix + 'selfParty.amount', error: errors?.selfParty?.amount, - min: selfMin, - step: selfMin, - max: selfMax, + min: HugeDecimal.one.toHumanReadableNumber(selfDecimals), + step: HugeDecimal.one.toHumanReadableNumber(selfDecimals), + max: selfMax.toHumanReadableString(selfDecimals), validations: [ (value) => - value <= selfMax || + selfMax.toHumanReadable(selfDecimals).gte(value) || t('error.treasuryInsufficient', { - amount: selfMax.toLocaleString(undefined, { - maximumFractionDigits: selfDecimals, + amount: selfMax.toInternationalizedHumanReadableString({ + decimals: selfDecimals, }), tokenSymbol: selfSymbol, }), diff --git a/packages/stateful/actions/core/actions/token_swap/types.ts b/packages/stateful/actions/core/actions/token_swap/types.ts index 2e4610bb0..483d9c446 100644 --- a/packages/stateful/actions/core/actions/token_swap/types.ts +++ b/packages/stateful/actions/core/actions/token_swap/types.ts @@ -12,7 +12,7 @@ export interface Counterparty { address: string type: TokenType denomOrAddress: string - amount: number + amount: string decimals: number } diff --git a/packages/stateful/components/dao/commonVotingConfig/ActiveThresholdVotingConfigItem.tsx b/packages/stateful/components/dao/commonVotingConfig/ActiveThresholdVotingConfigItem.tsx index 370486c06..283cca0c5 100644 --- a/packages/stateful/components/dao/commonVotingConfig/ActiveThresholdVotingConfigItem.tsx +++ b/packages/stateful/components/dao/commonVotingConfig/ActiveThresholdVotingConfigItem.tsx @@ -23,7 +23,7 @@ const ActiveThresholdInput = ({ activeThreshold: { enabled, type } = { enabled: false, type: 'percent', - value: 10, + value: '10', }, }, register, @@ -51,7 +51,6 @@ const ActiveThresholdInput = ({ fieldName="activeThreshold.value" getValues={getValues} min={1} - numericValue register={register} setValue={setValue} sizing="sm" @@ -82,7 +81,7 @@ const ActiveThresholdReview = ({ activeThreshold: { enabled, type, value } = { enabled: false, type: 'percent', - value: 10, + value: '10', }, }, }: DaoCreationVotingConfigItemReviewProps<DaoCreationVotingConfigWithActiveThreshold>) => { diff --git a/packages/stateful/components/dao/commonVotingConfig/ProposalDepositVotingConfigItem.tsx b/packages/stateful/components/dao/commonVotingConfig/ProposalDepositVotingConfigItem.tsx index 57a8e757f..4213d67f9 100644 --- a/packages/stateful/components/dao/commonVotingConfig/ProposalDepositVotingConfigItem.tsx +++ b/packages/stateful/components/dao/commonVotingConfig/ProposalDepositVotingConfigItem.tsx @@ -48,6 +48,7 @@ const ProposalDepositInput = ({ }, register, setValue, + getValues, watch, errors, }: DaoCreationVotingConfigItemInputProps<DaoCreationVotingConfigWithProposalDeposit>) => { @@ -155,6 +156,7 @@ const ProposalDepositInput = ({ ? // Only works for cw20. 'voting_module_token' : governanceTokenLoadable.contents.denomOrAddress, + decimals: governanceTokenLoadable.contents.decimals, description: t('title.governanceToken'), }, ] @@ -166,6 +168,7 @@ const ProposalDepositInput = ({ chainId, type: TokenType.Cw20, denomOrAddress: 'other_cw20', + decimals: tokenLoaded?.decimals ?? 0, symbol: (type === TokenType.Cw20 && tokenLoaded?.symbol) || t('form.cw20Token'), imageUrl: (type === TokenType.Cw20 && tokenLoaded?.imageUrl) || undefined, @@ -205,6 +208,7 @@ const ProposalDepositInput = ({ watch: watch as UseFormWatch<DaoCreationVotingConfigWithProposalDeposit>, setValue, + getValues, register, fieldName: 'proposalDeposit.amount', error: errors?.proposalDeposit?.amount, @@ -304,8 +308,11 @@ const ProposalDepositReview = ({ ) : ( <> {t('format.token', { - amount: amount.toLocaleString(undefined, { - maximumFractionDigits: decimals, + amount: HugeDecimal.fromHumanReadable( + amount, + decimals + ).toInternationalizedHumanReadableString({ + decimals, }), symbol, })} diff --git a/packages/stateful/creators/NftBased/getInstantiateInfo.ts b/packages/stateful/creators/NftBased/getInstantiateInfo.ts index 20ac2de0c..fce56a636 100644 --- a/packages/stateful/creators/NftBased/getInstantiateInfo.ts +++ b/packages/stateful/creators/NftBased/getInstantiateInfo.ts @@ -22,7 +22,7 @@ export const getInstantiateInfo: DaoCreatorGetInstantiateInfo<CreatorData> = ({ ? !activeThreshold.type || activeThreshold.type === 'percent' ? { percentage: { - percent: (activeThreshold.value / 100).toString(), + percent: (Number(activeThreshold.value) / 100).toString(), }, } : { diff --git a/packages/stateful/creators/TokenBased/getInstantiateInfo.ts b/packages/stateful/creators/TokenBased/getInstantiateInfo.ts index 19d15a04e..539512ebb 100644 --- a/packages/stateful/creators/TokenBased/getInstantiateInfo.ts +++ b/packages/stateful/creators/TokenBased/getInstantiateInfo.ts @@ -52,12 +52,17 @@ export const getInstantiateInfo: DaoCreatorGetInstantiateInfo<CreatorData> = ({ ? !activeThreshold.type || activeThreshold.type === 'percent' ? { percentage: { - percent: (activeThreshold.value / 100).toString(), + percent: (Number(activeThreshold.value) / 100).toString(), }, } : { absolute_count: { - count: BigInt(activeThreshold.value).toString(), + count: HugeDecimal.fromHumanReadable( + activeThreshold.value, + govTokenType === GovernanceTokenType.New + ? NEW_DAO_TOKEN_DECIMALS + : existingToken?.decimals ?? 0 + ).toString(), }, } : null, diff --git a/packages/stateful/creators/TokenBased/index.ts b/packages/stateful/creators/TokenBased/index.ts index 2cb5fed86..8fad7e999 100644 --- a/packages/stateful/creators/TokenBased/index.ts +++ b/packages/stateful/creators/TokenBased/index.ts @@ -55,7 +55,7 @@ export const TokenBasedCreator: DaoCreator<CreatorData> = { activeThreshold: { enabled: false, type: 'percent', - value: 10, + value: '10', }, }), governanceConfig: { diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.stories.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.stories.tsx index 5870b4765..aee539e4d 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.stories.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.stories.tsx @@ -19,7 +19,7 @@ export default { makeReactHookFormDecorator<UpdatePreProposeConfigData>({ depositRequired: true, depositInfo: { - amount: Math.pow(10, 6), + amount: Math.pow(10, 6).toString(), type: 'native', denomOrAddress: '', token: undefined, diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.tsx index 911ef9400..762ca871c 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.tsx @@ -34,7 +34,7 @@ const DepositRefundPolicyValues = Object.values(DepositRefundPolicy) export interface UpdatePreProposeConfigData { depositRequired: boolean depositInfo: { - amount: number + amount: string // Token input fields. type: 'native' | 'cw20' | 'voting_module_token' denomOrAddress: string @@ -64,7 +64,7 @@ export const UpdatePreProposeConfigComponent: ActionComponent< const { chain: { chain_id: chainId, bech32_prefix: bech32Prefix }, } = useActionOptions() - const { register, setValue, watch } = + const { register, setValue, getValues, watch } = useFormContext<UpdatePreProposeConfigData>() const depositRequired = watch( @@ -98,6 +98,7 @@ export const UpdatePreProposeConfigComponent: ActionComponent< chainId, type: TokenType.Cw20, denomOrAddress: 'other_cw20', + decimals: depositInfo.token?.decimals ?? 0, symbol: (depositInfo.type === TokenType.Cw20 && depositInfo.token?.symbol) || t('form.cw20Token'), @@ -160,6 +161,7 @@ export const UpdatePreProposeConfigComponent: ActionComponent< amount={{ watch, setValue, + getValues, register, fieldName: (fieldNamePrefix + 'depositInfo.amount') as 'depositInfo.amount', diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/index.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/index.tsx index dd0c6e329..2b20b0ab8 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/index.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/index.tsx @@ -187,7 +187,7 @@ export class DaoProposalMultipleUpdatePreProposeConfigAction extends ActionBase< ? { amount: HugeDecimal.from( config.deposit_info.amount - ).toHumanReadableNumber(token?.decimals ?? 0), + ).toHumanReadableString(token?.decimals ?? 0), type: isVotingModuleToken ? 'voting_module_token' : 'native' in config.deposit_info.denom @@ -202,7 +202,7 @@ export class DaoProposalMultipleUpdatePreProposeConfigAction extends ActionBase< refundPolicy: config.deposit_info.refund_policy, } : { - amount: 1, + amount: '1', type: 'native', denomOrAddress: getNativeTokenForChainId( this.proposalModule.dao.chainId @@ -387,7 +387,7 @@ export class DaoProposalMultipleUpdatePreProposeConfigAction extends ActionBase< return { depositRequired: false, depositInfo: { - amount: 1, + amount: '1', type: 'native', denomOrAddress: getNativeTokenForChainId(chainId).denomOrAddress, refundPolicy: DepositRefundPolicy.OnlyPassed, @@ -404,7 +404,7 @@ export class DaoProposalMultipleUpdatePreProposeConfigAction extends ActionBase< : 'cw20' const depositInfo: UpdatePreProposeConfigData['depositInfo'] = { - amount: HugeDecimal.from(configDepositInfo.amount).toHumanReadableNumber( + amount: HugeDecimal.from(configDepositInfo.amount).toHumanReadableString( token.decimals ), type, diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.stories.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.stories.tsx index 4b773b752..d47e12754 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.stories.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.stories.tsx @@ -19,7 +19,7 @@ export default { makeReactHookFormDecorator<UpdatePreProposeConfigData>({ depositRequired: true, depositInfo: { - amount: Math.pow(10, 6), + amount: Math.pow(10, 6).toString(), type: 'native', denomOrAddress: '', token: undefined, diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.tsx index 7e1ca798c..0691ffd48 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/UpdatePreProposeConfigComponent.tsx @@ -34,7 +34,7 @@ const DepositRefundPolicyValues = Object.values(DepositRefundPolicy) export interface UpdatePreProposeConfigData { depositRequired: boolean depositInfo: { - amount: number + amount: string // Token input fields. type: 'native' | 'cw20' | 'voting_module_token' denomOrAddress: string @@ -65,7 +65,7 @@ export const UpdatePreProposeConfigComponent: ActionComponent< chain: { chain_id: chainId, bech32_prefix: bech32Prefix }, } = useActionOptions() - const { register, setValue, watch } = + const { register, setValue, getValues, watch } = useFormContext<UpdatePreProposeConfigData>() const depositRequired = watch( @@ -99,6 +99,7 @@ export const UpdatePreProposeConfigComponent: ActionComponent< chainId, type: TokenType.Cw20, denomOrAddress: 'other_cw20', + decimals: depositInfo.token?.decimals ?? 0, symbol: (depositInfo.type === TokenType.Cw20 && depositInfo.token?.symbol) || t('form.cw20Token'), @@ -161,6 +162,7 @@ export const UpdatePreProposeConfigComponent: ActionComponent< amount={{ watch, setValue, + getValues, register, fieldName: (fieldNamePrefix + 'depositInfo.amount') as 'depositInfo.amount', diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/index.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/index.tsx index 2d433fad8..f1db3b788 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/index.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/index.tsx @@ -196,7 +196,7 @@ export class DaoProposalSingleUpdatePreProposeConfigAction extends ActionBase<Up ? { amount: HugeDecimal.from( config.deposit_info.amount - ).toHumanReadableNumber(token?.decimals ?? 0), + ).toHumanReadableString(token?.decimals ?? 0), type: isVotingModuleToken ? 'voting_module_token' : 'native' in config.deposit_info.denom @@ -211,7 +211,7 @@ export class DaoProposalSingleUpdatePreProposeConfigAction extends ActionBase<Up refundPolicy: config.deposit_info.refund_policy, } : { - amount: 1, + amount: '1', type: 'native', denomOrAddress: getNativeTokenForChainId( this.proposalModule.dao.chainId @@ -396,7 +396,7 @@ export class DaoProposalSingleUpdatePreProposeConfigAction extends ActionBase<Up return { depositRequired: false, depositInfo: { - amount: 1, + amount: '1', type: 'native', denomOrAddress: getNativeTokenForChainId(chainId).denomOrAddress, refundPolicy: DepositRefundPolicy.OnlyPassed, @@ -413,7 +413,7 @@ export class DaoProposalSingleUpdatePreProposeConfigAction extends ActionBase<Up : 'cw20' const depositInfo: UpdatePreProposeConfigData['depositInfo'] = { - amount: HugeDecimal.from(configDepositInfo.amount).toHumanReadableNumber( + amount: HugeDecimal.from(configDepositInfo.amount).toHumanReadableString( token.decimals ), type, 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 6ea73617d..7e92cb2e2 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/NewAttribute.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/NewAttribute.tsx @@ -35,6 +35,7 @@ export const NewAttribute = ({ register, formState: { errors }, setValue, + getValues, watch, } = useFormContext<NewSurveyFormData>() @@ -124,11 +125,15 @@ export const NewAttribute = ({ amount={{ watch, setValue, + getValues, register, fieldName: `attributes.${attributeIndex}.tokens.${tokenIndex}.amount`, error: errors?.attributes?.[attributeIndex]?.tokens?.[tokenIndex] ?.amount, + min: HugeDecimal.one.toHumanReadableNumber( + selectedToken?.decimals ?? 0 + ), step: HugeDecimal.one.toHumanReadableNumber( selectedToken?.decimals ?? 0 ), diff --git a/packages/stateless/components/actions/NativeCoinSelector.tsx b/packages/stateless/components/actions/NativeCoinSelector.tsx index d9d34e9c9..7bbbe556f 100644 --- a/packages/stateless/components/actions/NativeCoinSelector.tsx +++ b/packages/stateless/components/actions/NativeCoinSelector.tsx @@ -83,7 +83,8 @@ export const NativeCoinSelector = ({ const amountField = (fieldNamePrefix + 'amount') as 'amount' const decimalsField = (fieldNamePrefix + 'decimals') as 'decimals' - const { register, setValue, watch } = useFormContext<NativeCoinForm>() + const { register, setValue, getValues, watch } = + useFormContext<NativeCoinForm>() const watchDenom = watch(denomField) const watchAmount = watch(amountField) const watchDecimals = watch(decimalsField) || 0 @@ -99,9 +100,7 @@ export const NativeCoinSelector = ({ token.denomOrAddress === watchDenom && (!chainId || token.chainId === chainId) ) - const balance = HugeDecimal.from( - selectedToken?.balance ?? 0 - ).toHumanReadableNumber(selectedToken?.token.decimals ?? 0) + const balance = HugeDecimal.from(selectedToken?.balance ?? 0) const decimals = customToken ? watchDecimals @@ -120,10 +119,10 @@ export const NativeCoinSelector = ({ ? undefined : !selectedToken ? t('error.unknownDenom', { denom: watchDenom }) - : watchAmount > balance + : balance.toHumanReadable(decimals).lt(watchAmount) ? t('error.insufficientFundsWarning', { - amount: balance.toLocaleString(undefined, { - maximumFractionDigits: decimals, + amount: balance.toInternationalizedHumanReadableString({ + decimals, }), tokenSymbol: symbol, }) @@ -139,6 +138,7 @@ export const NativeCoinSelector = ({ amount={{ watch, setValue, + getValues, register, fieldName: amountField, error: errors?.amount || errors?._error, @@ -163,11 +163,11 @@ export const NativeCoinSelector = ({ description: t('title.balance') + ': ' + - HugeDecimal.from(balance) - .toHumanReadableNumber(token.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: token.decimals, - }), + HugeDecimal.from( + balance + ).toInternationalizedHumanReadableString({ + decimals: token.decimals, + }), })), } } diff --git a/packages/stateless/components/inputs/HugeDecimalInput.tsx b/packages/stateless/components/inputs/HugeDecimalInput.tsx index 921635ac5..5eb6ddcaf 100644 --- a/packages/stateless/components/inputs/HugeDecimalInput.tsx +++ b/packages/stateless/components/inputs/HugeDecimalInput.tsx @@ -11,19 +11,18 @@ import { toAccessibleImageUrl } from '@dao-dao/utils' import { IconButton } from '../icon_buttons' /** - * This is an input designed for numeric values, similar to NumberInput, but it - * takes advantage of the HugeDecimal class to handle large numbers more - * gracefully. It expects the underlying value to be a human-readable string. - * You can optionally convert the underlying value to a number with the - * `numericValue` prop, which should only be used when not needing to store - * potentially large numbers. + * This is input is designed for numeric values and takes advantage of the + * HugeDecimal class to handle large decimal numbers gracefully. It expects the + * underlying value to be a human-readable string. You can optionally store the + * underlying value as a number by setting the `numericValue` prop—this should + * only be used when not needing to store potentially large numbers. * * There is no need to provide `value` when providing `fieldName` and `register` * via react-hook-form. * * To show plus/minus buttons, make sure to provide `setValue` and either * `fieldName`+`getValues` or `value`. When using a react-hook-form form, - * `setValue`, and `getValues` can be retrieved easily from `useForm` or + * `setValue` and `getValues` can be retrieved easily from `useForm` or * `useFormContext`. When not using a react-hook-form form, the `setValue` * function can easily be mocked, and its first argument (`fieldName`) can be * ignored. @@ -120,7 +119,7 @@ export const HugeDecimalInput = < ) } size={ - // The larger button size for this NumberInput corresponds to the + // The larger button size for this input corresponds to the // default icon button size. plusMinusButtonSize === 'lg' ? 'default' : plusMinusButtonSize } @@ -153,7 +152,7 @@ export const HugeDecimalInput = < ) } size={ - // The larger button size for this NumberInput corresponds to the + // The larger button size for this input corresponds to the // default icon button size. plusMinusButtonSize === 'lg' ? 'default' : plusMinusButtonSize } diff --git a/packages/stateless/components/inputs/NumberInput.tsx b/packages/stateless/components/inputs/NumberInput.tsx deleted file mode 100644 index f2128bef4..000000000 --- a/packages/stateless/components/inputs/NumberInput.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import { Add, Remove } from '@mui/icons-material' -import clsx from 'clsx' -import { FieldValues, Path } from 'react-hook-form' -import { useTranslation } from 'react-i18next' - -import { HugeDecimal } from '@dao-dao/math' -import { NumberInputProps } from '@dao-dao/types' -import { toAccessibleImageUrl } from '@dao-dao/utils' - -import { IconButton } from '../icon_buttons' - -// To show plus/minus buttons, make sure to provide (`value` or -// `watch`+`fieldName`) in addition to `setValue`. When using a react-hook-form -// form, `setValue`, `watch`, and `fieldName` already exist. When not using a -// react-hook-form form, the `setValue` function can easily be mocked, and the -// first fieldName argument (which will be an empty string) can be ignored. - -export const NumberInput = < - FV extends FieldValues, - FieldName extends Path<FV> ->({ - fieldName, - register, - error, - validation, - hidePlusMinus, - value: _value, - watch, - setValue, - disabled, - sizing, - className, - containerClassName, - required, - transformDecimals, - ghost, - unit, - unitIconUrl, - textClassName, - unitClassName, - unitIconClassName, - unitContainerClassName, - plusMinusButtonSize = 'sm', - ...props -}: NumberInputProps<FV, FieldName>) => { - const { t } = useTranslation() - const validate = validation?.reduce( - (a, v) => ({ ...a, [v.toString()]: v }), - {} - ) - - const watchedField = watch && fieldName ? watch(fieldName) : _value - const untransformedValue = - !watchedField && watchedField !== 0 ? watchedField : Number(watchedField) - const value = - untransformedValue && transformDecimals - ? HugeDecimal.from(untransformedValue).toHumanReadableNumber( - transformDecimals - ) - : untransformedValue - - return ( - <div - className={clsx( - 'flex flex-row items-center gap-2 bg-transparent transition', - // Padding and outline - !ghost && 'rounded-md py-3 px-4 ring-1 ring-inset focus-within:ring-2', - // Outline color - error - ? 'ring-border-interactive-error' - : 'ring-border-primary focus-within:ring-border-interactive-focus', - // Sizing - { - 'w-28': sizing === 'sm', - 'w-40': sizing === 'md', - 'w-56': sizing === 'lg', - 'w-28 md:w-32 lg:w-40': sizing === 'auto', - 'w-full': sizing === 'fill', - }, - containerClassName - )} - > - {/* Do not require `fieldName` to be set in case a form is not being used. As long as `setValue` and `value` are present, these buttons will work. `value` is present if `watch`+`fieldName` are defined, or `value` is set directly. */} - {!hidePlusMinus && !disabled && setValue && value !== undefined && ( - <div - className={clsx( - 'flex flex-row items-center gap-1', - // Add small gap between buttons when larger buttons are used. - plusMinusButtonSize === 'lg' && 'gap-1' - )} - > - {/* Minus button */} - <IconButton - Icon={Remove} - disabled={disabled} - iconClassName="text-icon-secondary" - onClick={() => - setValue( - fieldName ?? '', - Math.min( - Math.max( - // Subtract 1 whole number. - Number(((value || 0) - 1).toFixed(0)), - typeof props.min === 'number' ? props.min : -Infinity - ), - typeof props.max === 'number' ? props.max : Infinity - ), - { - shouldValidate: true, - } - ) - } - size={ - // The larger button size for this NumberInput corresponds to the - // default icon button size. - plusMinusButtonSize === 'lg' ? 'default' : plusMinusButtonSize - } - variant="ghost" - /> - - <IconButton - Icon={Add} - disabled={disabled} - iconClassName="text-icon-secondary" - onClick={() => - setValue( - fieldName ?? '', - Math.min( - Math.max( - // Add 1 whole number. - Number(((value || 0) + 1).toFixed(0)), - typeof props.min === 'number' ? props.min : -Infinity - ), - typeof props.max === 'number' ? props.max : Infinity - ), - { - shouldValidate: true, - } - ) - } - size={ - // The larger button size for this NumberInput corresponds to the - // default icon button size. - plusMinusButtonSize === 'lg' ? 'default' : plusMinusButtonSize - } - variant="ghost" - /> - </div> - )} - - <input - className={clsx( - 'ring-none secondary-text w-full grow appearance-none border-none bg-transparent text-right text-text-body outline-none', - className, - textClassName - )} - disabled={disabled} - type="number" - value={value} - {...props} - {...(register && - fieldName && - register(fieldName, { - required: required && t('info.required'), - validate, - setValueAs: (value) => { - // If not a number AND not a string or an empty string, set NaN. - // Empty strings get converted to 0 with the Number constructor, - // which we don't want, because then the input can't be cleared. - const newValue = - typeof value !== 'number' && - (typeof value !== 'string' || value.trim() === '') - ? NaN - : // On first load, setValueAs seems to be called with the first value, which is probably default loaded from a save. We don't want to transform this first value. - transformDecimals && value !== untransformedValue - ? HugeDecimal.fromHumanReadable( - value, - transformDecimals - ).toNumber() - : Number(value) - - return newValue - }, - }))} - /> - - {(unit || unitIconUrl) && ( - <div - className={clsx( - 'flex flex-row items-center gap-1.5 max-w-[10rem] shrink-0 min-w-0', - unitContainerClassName - )} - > - {unitIconUrl && ( - <div - className={clsx( - 'h-5 w-5 shrink-0 bg-cover bg-center rounded-full ml-1', - unitIconClassName - )} - style={{ - backgroundImage: `url(${toAccessibleImageUrl(unitIconUrl)})`, - }} - /> - )} - - <p - className={clsx( - 'secondary-text max-w-[10rem] shrink-0 truncate text-right text-text-tertiary', - textClassName, - unitClassName - )} - > - {unit} - </p> - </div> - )} - </div> - ) -} diff --git a/packages/stateless/components/inputs/TokenInput.stories.tsx b/packages/stateless/components/inputs/TokenInput.stories.tsx index a967226f6..4db88d379 100644 --- a/packages/stateless/components/inputs/TokenInput.stories.tsx +++ b/packages/stateless/components/inputs/TokenInput.stories.tsx @@ -18,7 +18,7 @@ export default { } as ComponentMeta<typeof TokenInput> const Template: ComponentStory<typeof TokenInput> = (args) => { - const { register, watch, setValue } = useFormContext() + const { register, watch, setValue, getValues } = useFormContext() return ( <div className="max-w-sm"> @@ -27,6 +27,7 @@ const Template: ComponentStory<typeof TokenInput> = (args) => { amount={{ watch, setValue, + getValues, register, fieldName: 'amount', min: 0.00001, diff --git a/packages/stateless/components/inputs/TokenInput.tsx b/packages/stateless/components/inputs/TokenInput.tsx index b4bbf6b2c..3e9f075aa 100644 --- a/packages/stateless/components/inputs/TokenInput.tsx +++ b/packages/stateless/components/inputs/TokenInput.tsx @@ -26,7 +26,7 @@ import { ChainLogo } from '../chain/ChainLogo' import { IconButton } from '../icon_buttons' import { FilterableItem, FilterableItemPopup } from '../popup' import { Tooltip } from '../tooltip' -import { NumberInput } from './NumberInput' +import { HugeDecimalInput } from './HugeDecimalInput' import { TextInput } from './TextInput' /** @@ -61,13 +61,10 @@ export const TokenInput = < ? undefined : tokens.data.find((token) => tokensEqual(token, _selectedToken)) - const amount = amountField - ? amountField.convertMicroDenom - ? HugeDecimal.from( - amountField.watch(amountField.fieldName) - ).toHumanReadableNumber(selectedToken?.decimals ?? 0) - : Number(amountField.watch(amountField.fieldName)) - : 0 + const amount = HugeDecimal.fromHumanReadable( + amountField?.watch(amountField.fieldName) || '0', + selectedToken?.decimals ?? 0 + ) // All tokens from same chain. const allTokensOnSameChain = @@ -109,9 +106,8 @@ export const TokenInput = < <p className="min-w-[4rem] grow truncate text-left"> {readOnly && amountField && - amount.toLocaleString(undefined, { - // Show as many decimals as possible (max is 20). - maximumFractionDigits: 20, + amount.toInternationalizedHumanReadableString({ + decimals: selectedToken.decimals, }) + (amountField.unit ? amountField.unit : '') + ' $'} @@ -122,7 +118,10 @@ export const TokenInput = < tokenFallback ?? ( <p className="text-text-secondary"> {readOnly - ? t('info.token', { count: amount }) + ? t('info.token', { + // Plural if amount is not 1. + count: amount.eq(1) ? 1 : 2, + }) : disabled ? t('info.noTokenSelected') : t('button.selectToken')} @@ -248,20 +247,17 @@ export const TokenInput = < ) : ( <> {amountField && ( - <NumberInput + <HugeDecimalInput {...amountField} containerClassName="min-w-[12rem] grow basis-[12rem]" disabled={disabled || (!selectedToken && !customSelected)} setValue={(fieldName, value, options) => amountField.setValue(fieldName, value as any, options) } - transformDecimals={ - amountField.convertMicroDenom - ? selectedToken?.decimals - : undefined - } validation={[ - amountField.min ? validatePositive : validateNonNegative, + HugeDecimal.from(amountField.min || 0).isZero() + ? validateNonNegative + : validatePositive, ...(required ? [validateRequired] : []), ...(amountField.validations ?? []), ]} diff --git a/packages/stateless/components/inputs/index.ts b/packages/stateless/components/inputs/index.ts index 6438261dd..52e0cd107 100644 --- a/packages/stateless/components/inputs/index.ts +++ b/packages/stateless/components/inputs/index.ts @@ -14,7 +14,6 @@ export * from './ImageUploadInput' export * from './InputErrorMessage' export * from './InputLabel' export * from './InputThemedText' -export * from './NumberInput' export * from './PercentButton' export * from './RadioInput' export * from './RangeInput' diff --git a/packages/types/components/NumberInput.ts b/packages/types/components/NumberInput.ts deleted file mode 100644 index 4c87266a9..000000000 --- a/packages/types/components/NumberInput.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { ComponentPropsWithoutRef } from 'react' -import { - FieldPathValue, - FieldValues, - Path, - UseFormRegister, - Validate, -} from 'react-hook-form' - -export interface NumberInputProps< - FV extends FieldValues, - FieldName extends Path<FV> -> extends Omit< - ComponentPropsWithoutRef<'input'>, - 'type' | 'required' | 'value' - > { - // The field name for the form. - fieldName?: FieldName - // Register function returned by `useForm`/`useFormContext`. - register?: UseFormRegister<FV> - // Validations to apply when registering this input. - validation?: Validate<FieldPathValue<FV, FieldName>>[] - // Applies to the input when registering with a form. - required?: boolean - // Transform the value displayed in the input by these decimals. - transformDecimals?: number - // If error present, outline input in red. - error?: any - // Hide plus/minus buttons - hidePlusMinus?: boolean - // Value passed to the input. - value?: number - // Used to get the value when the plus/minus buttons are clicked. Accepts the - // react-hook-form `watch` function, or any custom function. - watch?: (fieldName: any) => number | undefined - // Used to set the value when the plus/minus buttons are clicked. Accepts the - // react-hook-form `watch` function, or any custom function. - setValue?: ( - fieldName: any, - value: number, - options?: { shouldValidate: boolean } - ) => void - // Applies to the outer-most container, which contains the plus/minus buttons, - // the input, and the unit. - containerClassName?: string - // Size of the container. - sizing?: 'sm' | 'md' | 'lg' | 'auto' | 'fill' | 'none' - // Remove padding, rounded corners, and outline. - ghost?: boolean - // A unit to display to the right of the number. - unit?: string - // A unit icon URL that displays to the left of the unit. - unitIconUrl?: string - // Applies to both the input text and the unit. - textClassName?: string - // Applies to the unit only. - unitClassName?: string - // Applies to the unit icon only. - unitIconClassName?: string - // Applies to the unit container only. - unitContainerClassName?: string - // Size of the plus/minus buttons. Defaults to 'sm'. - plusMinusButtonSize?: 'sm' | 'lg' -} diff --git a/packages/types/components/TokenInput.ts b/packages/types/components/TokenInput.ts index 3ab93306c..f38178f55 100644 --- a/packages/types/components/TokenInput.ts +++ b/packages/types/components/TokenInput.ts @@ -2,6 +2,7 @@ import { ReactNode, RefCallback } from 'react' import { FieldValues, Path, + UseFormGetValues, UseFormRegister, UseFormSetValue, UseFormWatch, @@ -10,14 +11,11 @@ import { import { LoadingData } from '../misc' import { GenericToken, TokenType } from '../token' -import { NumberInputProps } from './NumberInput' +import { HugeDecimalInputProps } from './HugeDecimalInput' -export type TokenInputOption = Omit<GenericToken, 'type' | 'decimals'> & { +export type TokenInputOption = Omit<GenericToken, 'type'> & { type: TokenType | string description?: string - // Only necessary if `convertMicroDenom` is true so the input can - // intelligently convert the value. 0 will be used if not provided. - decimals?: number } export type TokenInputProps< @@ -29,27 +27,20 @@ export type TokenInputProps< * The fields that control the amount input. */ amount?: Omit< - NumberInputProps<FV, FieldName>, + HugeDecimalInputProps<FV, FieldName>, | 'containerClassName' | 'disabled' - | 'transformDecimals' | 'register' - | 'watch' + | 'getValues' | 'setValue' | 'fieldName' > & { register: UseFormRegister<FV> watch: UseFormWatch<FV> setValue: UseFormSetValue<FV> + getValues: UseFormGetValues<FV> fieldName: FieldName validations?: Validate<number>[] - /* - * If true, will convert the amount to micro-denom using the token's - * decimals value for the form. Thus, the input will display the macro-denom - * amount, but the form will receive the micro-denom amount. Default is - * false. - */ - convertMicroDenom?: boolean } /** * The available tokens and selection handlers for the token. Various diff --git a/packages/types/components/index.ts b/packages/types/components/index.ts index efeaa8d94..be1a7a9fb 100644 --- a/packages/types/components/index.ts +++ b/packages/types/components/index.ts @@ -37,7 +37,6 @@ export * from './Modal' export * from './NavWallet' export * from './NavWalletConnected' export * from './Notifications' -export * from './NumberInput' export * from './PageHeader' export * from './PayEntityDisplay' export * from './Popup' diff --git a/packages/types/dao.ts b/packages/types/dao.ts index c5e29231b..e70dce2cf 100644 --- a/packages/types/dao.ts +++ b/packages/types/dao.ts @@ -414,7 +414,7 @@ export type DaoCreationVotingConfigWithAllowRevoting = { export type DaoCreationVotingConfigWithProposalDeposit = { proposalDeposit: { enabled: boolean - amount: number + amount: string // Token input fields. type: 'native' | 'cw20' | 'voting_module_token' denomOrAddress: string @@ -444,7 +444,7 @@ export type DaoCreationVotingConfigWithActiveThreshold = { activeThreshold: { enabled: boolean type: 'percent' | 'absolute' - value: number + value: string } } diff --git a/packages/types/gov.ts b/packages/types/gov.ts index c5b5c9d6e..e3f3f0bef 100644 --- a/packages/types/gov.ts +++ b/packages/types/gov.ts @@ -155,15 +155,17 @@ export type GovernanceProposalActionData = { description: string metadata: string deposit: { - amount: number + amount: string denom: string + decimals: number }[] legacy: { typeUrl: string // CommunityPoolSpendProposal spends: { - amount: number + amount: string denom: string + decimals: number }[] spendRecipient: string // ParameterChangeProposal From 4e5c6c7351c38a519b601ab78c26b32bfecf2ba9 Mon Sep 17 00:00:00 2001 From: Noah Saso <noahsaso@gmail.com> Date: Tue, 1 Oct 2024 23:05:35 -0400 Subject: [PATCH 19/26] renamed HugeDecimalInput to NumericInput --- .../actions/AuthzGrantRevoke/Component.tsx | 4 +- .../actions/ConfigureRebalancer/Component.tsx | 10 +-- .../CreateRewardDistribution/Component.tsx | 8 +- .../FundRewardDistribution/Component.tsx | 4 +- .../core/actions/Instantiate/Component.tsx | 4 +- .../core/actions/Instantiate2/Component.tsx | 4 +- .../core/actions/ManageStaking/Component.tsx | 4 +- .../actions/ManageSubDaoPause/Component.tsx | 4 +- .../actions/ManageVesting/BeginVesting.tsx | 6 +- .../core/actions/Migrate/Component.tsx | 4 +- .../actions/core/actions/Spend/Component.tsx | 4 +- .../UpdateRewardDistribution/Component.tsx | 6 +- .../ActiveThresholdVotingConfigItem.tsx | 4 +- .../QuorumVotingConfigItem.tsx | 8 +- .../VotingDurationVotingConfigItem.tsx | 8 +- .../gov/GovProposalStatusAndInfo.tsx | 4 +- .../creators/MembershipBased/TierCard.tsx | 4 +- .../UnstakingDurationVotingConfigItem.tsx | 4 +- .../GovernanceConfigurationInput.tsx | 8 +- .../stateful/creators/TokenBased/TierCard.tsx | 4 +- .../UnstakingDurationVotingConfigItem.tsx | 4 +- .../UpdateProposalConfigComponent.tsx | 6 +- .../UpdateProposalConfigComponent.tsx | 10 +-- .../UpdateProposalConfigComponent.tsx | 8 +- .../daoCreation/ThresholdVotingConfigItem.tsx | 4 +- .../actions/Mint/MintComponent.tsx | 4 +- .../actions/UpdateStakingConfig/Component.tsx | 4 +- .../actions/ManageMembers/Component.tsx | 4 +- .../actions/UpdateStakingConfig/Component.tsx | 4 +- .../actions/Mint/MintComponent.tsx | 4 +- .../actions/UpdateStakingConfig/Component.tsx | 4 +- .../actions/UpdateStakingConfig/Component.tsx | 4 +- .../actions/Mint/MintComponent.tsx | 4 +- .../actions/UpdateStakingConfig/Component.tsx | 4 +- ...t.stories.tsx => NumericInput.stories.tsx} | 13 ++- ...{HugeDecimalInput.tsx => NumericInput.tsx} | 17 ++-- .../components/inputs/TokenInput.tsx | 4 +- packages/stateless/components/inputs/index.ts | 2 +- .../components/modals/TokenDepositModal.tsx | 4 +- .../proposal/ProposalVetoConfigurer.tsx | 4 +- .../components/token/StakingModal.tsx | 4 +- .../{HugeDecimalInput.ts => NumericInput.ts} | 81 +++++++++++++------ packages/types/components/TokenInput.ts | 4 +- packages/types/components/index.ts | 2 +- 44 files changed, 168 insertions(+), 141 deletions(-) rename packages/stateless/components/inputs/{HugeDecimalInput.stories.tsx => NumericInput.stories.tsx} (68%) rename packages/stateless/components/inputs/{HugeDecimalInput.tsx => NumericInput.tsx} (94%) rename packages/types/components/{HugeDecimalInput.ts => NumericInput.ts} (55%) diff --git a/packages/stateful/actions/core/actions/AuthzGrantRevoke/Component.tsx b/packages/stateful/actions/core/actions/AuthzGrantRevoke/Component.tsx index 473d4ea8f..e6ed46bf6 100644 --- a/packages/stateful/actions/core/actions/AuthzGrantRevoke/Component.tsx +++ b/packages/stateful/actions/core/actions/AuthzGrantRevoke/Component.tsx @@ -8,10 +8,10 @@ import { Button, CodeMirrorInput, FormSwitchCard, - HugeDecimalInput, InputErrorMessage, InputLabel, NativeCoinSelector, + NumericInput, RadioInput, SegmentedControlsTitle, SelectInput, @@ -413,7 +413,7 @@ export const AuthzGrantRevokeComponent: ActionComponent< tooltip={t('form.callsDescription')} /> - <HugeDecimalInput + <NumericInput containerClassName="grow" disabled={!isCreating} error={errors?.calls} diff --git a/packages/stateful/actions/core/actions/ConfigureRebalancer/Component.tsx b/packages/stateful/actions/core/actions/ConfigureRebalancer/Component.tsx index 15800eed5..cc0ab6f49 100644 --- a/packages/stateful/actions/core/actions/ConfigureRebalancer/Component.tsx +++ b/packages/stateful/actions/core/actions/ConfigureRebalancer/Component.tsx @@ -7,12 +7,12 @@ import { HugeDecimal } from '@dao-dao/math' import { Button, ErrorPage, - HugeDecimalInput, IconButton, InputErrorMessage, InputLabel, Loader, MarkdownRenderer, + NumericInput, RebalancerProjector, RebalancerProjectorAsset, SegmentedControls, @@ -456,7 +456,7 @@ export const ConfigureRebalancerComponent: ActionComponent< <div className="space-y-2"> {/* eslint-disable-next-line i18next/no-literal-string */} <InputLabel name="P" /> - <HugeDecimalInput + <NumericInput error={errors?.pid?.kp} fieldName={(fieldNamePrefix + 'pid.kp') as 'pid.kp'} hidePlusMinus @@ -474,7 +474,7 @@ export const ConfigureRebalancerComponent: ActionComponent< <div className="space-y-2"> {/* eslint-disable-next-line i18next/no-literal-string */} <InputLabel name="I" /> - <HugeDecimalInput + <NumericInput error={errors?.pid?.ki} fieldName={(fieldNamePrefix + 'pid.ki') as 'pid.ki'} hidePlusMinus @@ -492,7 +492,7 @@ export const ConfigureRebalancerComponent: ActionComponent< <div className="space-y-2"> {/* eslint-disable-next-line i18next/no-literal-string */} <InputLabel name="D" /> - <HugeDecimalInput + <NumericInput error={errors?.pid?.kd} fieldName={(fieldNamePrefix + 'pid.kd') as 'pid.kd'} hidePlusMinus @@ -588,7 +588,7 @@ export const ConfigureRebalancerComponent: ActionComponent< {maxLimitEnabled && ( <div className="flex flex-row gap-2 self-start"> - <HugeDecimalInput + <NumericInput containerClassName="grow min-w-[min(8rem,50%)]" error={errors?.maxLimit} fieldName={(fieldNamePrefix + 'maxLimit') as 'maxLimit'} diff --git a/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx index 0dab2ce27..4ead23be0 100644 --- a/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx @@ -3,10 +3,10 @@ import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { - HugeDecimalInput, InputErrorMessage, InputLabel, MarkdownRenderer, + NumericInput, PercentButton, SegmentedControls, SelectInput, @@ -194,7 +194,7 @@ export const CreateRewardDistributionComponent: ActionComponent< {!immediate && ( <div className="bg-background-tertiary flex flex-wrap flex-row gap-x-4 gap-y-2 px-4 py-3 rounded-md max-w-prose"> - <HugeDecimalInput + <NumericInput containerClassName="grow" disabled={!isCreating} error={errors?.rate?.amount} @@ -217,7 +217,7 @@ export const CreateRewardDistributionComponent: ActionComponent< <div className="flex grow flex-row gap-2"> <div className="flex flex-col gap-1 grow"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.rate?.duration?.value} fieldName={ @@ -265,7 +265,7 @@ export const CreateRewardDistributionComponent: ActionComponent< {t('info.initialRewardsFundsDescription')} </p> - <HugeDecimalInput + <NumericInput containerClassName={!isCreating ? 'self-start' : undefined} disabled={!isCreating} fieldName={(fieldNamePrefix + 'initialFunds') as 'initialFunds'} diff --git a/packages/stateful/actions/core/actions/FundRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/FundRewardDistribution/Component.tsx index b13a0704b..3e4de3862 100644 --- a/packages/stateful/actions/core/actions/FundRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/FundRewardDistribution/Component.tsx @@ -6,10 +6,10 @@ import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { FilterableItemPopup, - HugeDecimalInput, InputErrorMessage, InputLabel, InputThemedText, + NumericInput, PercentButton, StatusCard, TokenAmountDisplay, @@ -160,7 +160,7 @@ export const FundRewardDistributionComponent: ActionComponent< <div className="flex flex-col gap-2 max-w-prose"> <InputLabel name={t('form.funds')} primary /> - <HugeDecimalInput + <NumericInput containerClassName={clsx(!isCreating && 'self-start')} disabled={!isCreating} fieldName={(fieldNamePrefix + 'amount') as 'amount'} diff --git a/packages/stateful/actions/core/actions/Instantiate/Component.tsx b/packages/stateful/actions/core/actions/Instantiate/Component.tsx index c6c80562f..6c2acca88 100644 --- a/packages/stateful/actions/core/actions/Instantiate/Component.tsx +++ b/packages/stateful/actions/core/actions/Instantiate/Component.tsx @@ -9,10 +9,10 @@ import { Button, CodeMirrorInput, CopyToClipboard, - HugeDecimalInput, InputErrorMessage, InputLabel, NativeCoinSelector, + NumericInput, TextInput, useActionOptions, useChain, @@ -132,7 +132,7 @@ export const InstantiateComponent: ActionComponent<InstantiateOptions> = ({ <div className="flex flex-row items-center gap-2"> <div className="flex flex-col items-stretch gap-1"> <InputLabel name={t('form.codeId')} /> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.codeId} fieldName={fieldNamePrefix + 'codeId'} diff --git a/packages/stateful/actions/core/actions/Instantiate2/Component.tsx b/packages/stateful/actions/core/actions/Instantiate2/Component.tsx index 7573235e0..01f0ace18 100644 --- a/packages/stateful/actions/core/actions/Instantiate2/Component.tsx +++ b/packages/stateful/actions/core/actions/Instantiate2/Component.tsx @@ -10,10 +10,10 @@ import { CodeMirrorInput, CopyToClipboard, ErrorPage, - HugeDecimalInput, InputErrorMessage, InputLabel, NativeCoinSelector, + NumericInput, TextInput, useActionOptions, useChain, @@ -118,7 +118,7 @@ export const Instantiate2Component: ActionComponent<Instantiate2Options> = ({ <div className="flex flex-row items-center gap-2"> <div className="flex flex-col items-stretch gap-1"> <InputLabel name={t('form.codeId')} /> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.codeId} fieldName={fieldNamePrefix + 'codeId'} diff --git a/packages/stateful/actions/core/actions/ManageStaking/Component.tsx b/packages/stateful/actions/core/actions/ManageStaking/Component.tsx index 67c872d56..9f331c526 100644 --- a/packages/stateful/actions/core/actions/ManageStaking/Component.tsx +++ b/packages/stateful/actions/core/actions/ManageStaking/Component.tsx @@ -6,9 +6,9 @@ import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { - HugeDecimalInput, InputErrorMessage, InputLabel, + NumericInput, SelectInput, TokenAmountDisplay, ValidatorPicker, @@ -340,7 +340,7 @@ export const ManageStakingComponent: ActionComponent< {/* If not withdrawing reward or updating withdraw address, show amount input. */} {type !== StakingActionType.WithdrawDelegatorReward && type !== StakingActionType.SetWithdrawAddress && ( - <HugeDecimalInput + <NumericInput containerClassName="grow" disabled={!isCreating} error={errors?.amount} diff --git a/packages/stateful/actions/core/actions/ManageSubDaoPause/Component.tsx b/packages/stateful/actions/core/actions/ManageSubDaoPause/Component.tsx index 83269ff9d..d2e038d0c 100644 --- a/packages/stateful/actions/core/actions/ManageSubDaoPause/Component.tsx +++ b/packages/stateful/actions/core/actions/ManageSubDaoPause/Component.tsx @@ -4,10 +4,10 @@ import { useTranslation } from 'react-i18next' import { FilterableItemPopup, - HugeDecimalInput, InputErrorMessage, InputLabel, Loader, + NumericInput, SegmentedControlsTitle, useActionOptions, } from '@dao-dao/stateless' @@ -113,7 +113,7 @@ export const ManageSubDaoPauseComponent: ActionComponent< <div className="flex flex-col gap-1"> <InputLabel name={t('form.blocksToPauseFor')} /> - <HugeDecimalInput + <NumericInput error={errors?.pauseBlocks} fieldName={(fieldNamePrefix + 'pauseBlocks') as 'pauseBlocks'} getValues={getValues} diff --git a/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx b/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx index 5538130e1..0bb5cb531 100644 --- a/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx +++ b/packages/stateful/actions/core/actions/ManageVesting/BeginVesting.tsx @@ -18,10 +18,10 @@ import { CopyToClipboard, DateTimePicker, DateTimePickerNoForm, - HugeDecimalInput, IconButton, InputErrorMessage, InputLabel, + NumericInput, RadioInput, RadioInputOption, SelectInput, @@ -610,7 +610,7 @@ export const BeginVesting: ActionComponent<BeginVestingOptions> = ({ <div className="flex shrink-0 flex-col gap-1"> <InputLabel name={t('form.unlockPercent')} /> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.steps?.[index]?.percent} fieldName={ @@ -645,7 +645,7 @@ export const BeginVesting: ActionComponent<BeginVestingOptions> = ({ </div> <div className="flex flex-row gap-1"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.steps?.[index]?.delay?.value} fieldName={ diff --git a/packages/stateful/actions/core/actions/Migrate/Component.tsx b/packages/stateful/actions/core/actions/Migrate/Component.tsx index a67b6eb97..125187e0c 100644 --- a/packages/stateful/actions/core/actions/Migrate/Component.tsx +++ b/packages/stateful/actions/core/actions/Migrate/Component.tsx @@ -4,9 +4,9 @@ import { useTranslation } from 'react-i18next' import { AddressInput, CodeMirrorInput, - HugeDecimalInput, InputErrorMessage, InputLabel, + NumericInput, StatusCard, useActionOptions, useChain, @@ -72,7 +72,7 @@ export const MigrateContractComponent: ActionComponent<MigrateOptions> = ({ </div> <div className="flex flex-col gap-1"> <InputLabel name={t('form.codeId')} /> - <HugeDecimalInput + <NumericInput containerClassName="xs:h-full" disabled={!isCreating} error={errors?.codeId} diff --git a/packages/stateful/actions/core/actions/Spend/Component.tsx b/packages/stateful/actions/core/actions/Spend/Component.tsx index 641fefc57..7afb8f3ae 100644 --- a/packages/stateful/actions/core/actions/Spend/Component.tsx +++ b/packages/stateful/actions/core/actions/Spend/Component.tsx @@ -11,12 +11,12 @@ import { ChainLogo, ChainProvider, FormSwitchCard, - HugeDecimalInput, IbcDestinationChainPicker, InputErrorMessage, InputLabel, InputThemedText, Loader, + NumericInput, PercentButton, SelectInput, StatusCard, @@ -795,7 +795,7 @@ export const SpendComponent: ActionComponent<SpendOptions> = ({ {isCreating ? ( <> <div className="flex flex-row gap-1"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.ibcTimeout?.value} fieldName={ diff --git a/packages/stateful/actions/core/actions/UpdateRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/UpdateRewardDistribution/Component.tsx index a4e4fac13..e846dca59 100644 --- a/packages/stateful/actions/core/actions/UpdateRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/UpdateRewardDistribution/Component.tsx @@ -5,11 +5,11 @@ import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { FilterableItemPopup, - HugeDecimalInput, InputErrorMessage, InputLabel, InputThemedText, MarkdownRenderer, + NumericInput, SegmentedControls, SelectInput, StatusCard, @@ -188,7 +188,7 @@ export const UpdateRewardDistributionComponent: ActionComponent< {!immediate && ( <div className="flex flex-wrap flex-row gap-x-4 gap-y-2 px-4 py-3 bg-background-tertiary rounded-md max-w-prose"> - <HugeDecimalInput + <NumericInput containerClassName="grow" disabled={!isCreating} error={errors?.rate?.amount} @@ -211,7 +211,7 @@ export const UpdateRewardDistributionComponent: ActionComponent< <div className="flex grow flex-row gap-2"> <div className="flex flex-col gap-1 grow"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.rate?.duration?.value} fieldName={ diff --git a/packages/stateful/components/dao/commonVotingConfig/ActiveThresholdVotingConfigItem.tsx b/packages/stateful/components/dao/commonVotingConfig/ActiveThresholdVotingConfigItem.tsx index 283cca0c5..b652441da 100644 --- a/packages/stateful/components/dao/commonVotingConfig/ActiveThresholdVotingConfigItem.tsx +++ b/packages/stateful/components/dao/commonVotingConfig/ActiveThresholdVotingConfigItem.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import { FilmSlateEmoji, FormSwitchCard, - HugeDecimalInput, + NumericInput, SelectInput, } from '@dao-dao/stateless' import { @@ -45,7 +45,7 @@ const ActiveThresholdInput = ({ {enabled && ( <> <div className="flex flex-row gap-2"> - <HugeDecimalInput + <NumericInput containerClassName="grow min-w-[8rem]" error={errors?.activeThreshold?.value} fieldName="activeThreshold.value" diff --git a/packages/stateful/components/dao/commonVotingConfig/QuorumVotingConfigItem.tsx b/packages/stateful/components/dao/commonVotingConfig/QuorumVotingConfigItem.tsx index eb558dab5..961b7dc65 100644 --- a/packages/stateful/components/dao/commonVotingConfig/QuorumVotingConfigItem.tsx +++ b/packages/stateful/components/dao/commonVotingConfig/QuorumVotingConfigItem.tsx @@ -1,10 +1,6 @@ import { useTranslation } from 'react-i18next' -import { - HugeDecimalInput, - MegaphoneEmoji, - SelectInput, -} from '@dao-dao/stateless' +import { MegaphoneEmoji, NumericInput, SelectInput } from '@dao-dao/stateless' import { DaoCreationVotingConfigItem, DaoCreationVotingConfigItemInputProps, @@ -31,7 +27,7 @@ const QuorumInput = ({ return ( <div className="flex flex-row gap-2"> {!majority && ( - <HugeDecimalInput + <NumericInput containerClassName="grow min-w-[8rem]" error={errors?.quorum?.value} fieldName="quorum.value" diff --git a/packages/stateful/components/dao/commonVotingConfig/VotingDurationVotingConfigItem.tsx b/packages/stateful/components/dao/commonVotingConfig/VotingDurationVotingConfigItem.tsx index 639b69519..5905bbccf 100644 --- a/packages/stateful/components/dao/commonVotingConfig/VotingDurationVotingConfigItem.tsx +++ b/packages/stateful/components/dao/commonVotingConfig/VotingDurationVotingConfigItem.tsx @@ -1,10 +1,6 @@ import { useTranslation } from 'react-i18next' -import { - HourglassEmoji, - HugeDecimalInput, - SelectInput, -} from '@dao-dao/stateless' +import { HourglassEmoji, NumericInput, SelectInput } from '@dao-dao/stateless' import { DaoCreationVotingConfigItem, DaoCreationVotingConfigItemInputProps, @@ -30,7 +26,7 @@ export const VotingDurationInput = ({ return ( <div className="flex flex-row gap-2"> - <HugeDecimalInput + <NumericInput containerClassName="grow" error={errors?.votingDuration?.value} fieldName="votingDuration.value" diff --git a/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx b/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx index 2e8c68d63..0260a6d81 100644 --- a/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx +++ b/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx @@ -15,8 +15,8 @@ import { HugeDecimal } from '@dao-dao/math' import { genericTokenSelector } from '@dao-dao/state/recoil' import { GOV_PROPOSAL_STATUS_I18N_KEY_MAP, - HugeDecimalInput, Logo, + NumericInput, ProposalStatusAndInfoProps, ProposalStatusAndInfo as StatelessProposalStatusAndInfo, Tooltip, @@ -288,7 +288,7 @@ const InnerGovProposalStatusAndInfo = ({ status === ProposalStatus.PROPOSAL_STATUS_DEPOSIT_PERIOD ? { header: ( - <HugeDecimalInput + <NumericInput containerClassName="-mb-1" max={missingDeposit.toHumanReadableString( depositToken.decimals diff --git a/packages/stateful/creators/MembershipBased/TierCard.tsx b/packages/stateful/creators/MembershipBased/TierCard.tsx index c7241fbbf..b58b34b6d 100644 --- a/packages/stateful/creators/MembershipBased/TierCard.tsx +++ b/packages/stateful/creators/MembershipBased/TierCard.tsx @@ -12,10 +12,10 @@ import { useTranslation } from 'react-i18next' import { Button, - HugeDecimalInput, IconButton, InputErrorMessage, InputLabel, + NumericInput, TextInput, useChain, } from '@dao-dao/stateless' @@ -138,7 +138,7 @@ export const TierCard = ({ })} /> - <HugeDecimalInput + <NumericInput error={errors.creator?.data?.tiers?.[tierIndex]?.weight} fieldName={`creator.data.tiers.${tierIndex}.weight`} getValues={getValues} diff --git a/packages/stateful/creators/NftBased/UnstakingDurationVotingConfigItem.tsx b/packages/stateful/creators/NftBased/UnstakingDurationVotingConfigItem.tsx index fdf215388..db27cf3ad 100644 --- a/packages/stateful/creators/NftBased/UnstakingDurationVotingConfigItem.tsx +++ b/packages/stateful/creators/NftBased/UnstakingDurationVotingConfigItem.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next' -import { ClockEmoji, HugeDecimalInput, SelectInput } from '@dao-dao/stateless' +import { ClockEmoji, NumericInput, SelectInput } from '@dao-dao/stateless' import { DaoCreationVotingConfigItem, DaoCreationVotingConfigItemInputProps, @@ -26,7 +26,7 @@ export const UnstakingDurationInput = ({ return ( <div className="flex flex-row gap-2"> - <HugeDecimalInput + <NumericInput containerClassName="grow" error={errors?.unstakingDuration?.value} fieldName="unstakingDuration.value" diff --git a/packages/stateful/creators/TokenBased/GovernanceConfigurationInput.tsx b/packages/stateful/creators/TokenBased/GovernanceConfigurationInput.tsx index 8d3d9148b..ae580fdab 100644 --- a/packages/stateful/creators/TokenBased/GovernanceConfigurationInput.tsx +++ b/packages/stateful/creators/TokenBased/GovernanceConfigurationInput.tsx @@ -16,11 +16,11 @@ import { } from '@dao-dao/state' import { Button, - HugeDecimalInput, ImageSelector, InputErrorMessage, InputLabel, Loader, + NumericInput, SegmentedControls, TextInput, VotingPowerDistribution, @@ -531,7 +531,7 @@ export const GovernanceConfigurationInput = ({ <div className="flex grow flex-col"> <div className="flex grow flex-row items-center gap-2"> - <HugeDecimalInput + <NumericInput className="symbol-small-body-text font-mono leading-5 text-text-secondary" containerClassName="grow" error={errors.creator?.data?.newInfo?.maxSupply} @@ -578,7 +578,7 @@ export const GovernanceConfigurationInput = ({ <div className="flex grow flex-col"> <div className="flex grow flex-row items-center gap-2"> - <HugeDecimalInput + <NumericInput className="symbol-small-body-text font-mono leading-5 text-text-secondary" containerClassName="grow" error={errors.creator?.data?.newInfo?.initialSupply} @@ -612,7 +612,7 @@ export const GovernanceConfigurationInput = ({ <div className="flex grow flex-col"> <div className="flex grow flex-row items-center gap-2"> - <HugeDecimalInput + <NumericInput className="symbol-small-body-text font-mono leading-5 text-text-secondary" containerClassName="grow" error={ diff --git a/packages/stateful/creators/TokenBased/TierCard.tsx b/packages/stateful/creators/TokenBased/TierCard.tsx index 01672b75c..f78d84559 100644 --- a/packages/stateful/creators/TokenBased/TierCard.tsx +++ b/packages/stateful/creators/TokenBased/TierCard.tsx @@ -13,10 +13,10 @@ import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { Button, - HugeDecimalInput, IconButton, InputErrorMessage, InputLabel, + NumericInput, TextInput, TooltipInfoIcon, useChain, @@ -144,7 +144,7 @@ export const TierCard = ({ /> <div className="flex flex-row items-center gap-2"> - <HugeDecimalInput + <NumericInput containerClassName="grow" error={errors.creator?.data?.tiers?.[tierIndex]?.weight} fieldName={`creator.data.tiers.${tierIndex}.weight`} diff --git a/packages/stateful/creators/TokenBased/UnstakingDurationVotingConfigItem.tsx b/packages/stateful/creators/TokenBased/UnstakingDurationVotingConfigItem.tsx index d5d41c287..fd447775e 100644 --- a/packages/stateful/creators/TokenBased/UnstakingDurationVotingConfigItem.tsx +++ b/packages/stateful/creators/TokenBased/UnstakingDurationVotingConfigItem.tsx @@ -5,8 +5,8 @@ import { useTranslation } from 'react-i18next' import { contractQueries } from '@dao-dao/state/query' import { ClockEmoji, - HugeDecimalInput, InputErrorMessage, + NumericInput, SelectInput, SwitchCard, useHoldingKey, @@ -128,7 +128,7 @@ export const UnstakingDurationInput = ({ </div> ) : ( <div className="flex flex-row gap-2"> - <HugeDecimalInput + <NumericInput containerClassName="grow" error={errors?.unstakingDuration?.value} fieldName="unstakingDuration.value" diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdateProposalConfig/UpdateProposalConfigComponent.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdateProposalConfig/UpdateProposalConfigComponent.tsx index 3d1df555b..19e4932d4 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdateProposalConfig/UpdateProposalConfigComponent.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdateProposalConfig/UpdateProposalConfigComponent.tsx @@ -6,9 +6,9 @@ import { useTranslation } from 'react-i18next' import { ClockEmoji, FormSwitchCard, - HugeDecimalInput, InputErrorMessage, KeyEmoji, + NumericInput, PeopleEmoji, ProposalVetoConfigurer, RecycleEmoji, @@ -99,7 +99,7 @@ export const UpdateProposalConfigComponent: ActionComponent< <div className="flex grow flex-row flex-wrap gap-2"> {percentageQuorumSelected && ( <div className="flex flex-col gap-1"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.quorumPercentage} fieldName={ @@ -145,7 +145,7 @@ export const UpdateProposalConfigComponent: ActionComponent< </div> <div className="flex grow flex-row flex-wrap gap-2"> <div className="flex flex-col gap-1"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.votingDuration?.value} fieldName={ diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.tsx index ffbe92c64..2f0e2989d 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.tsx @@ -6,10 +6,10 @@ import { ChartEmoji, ClockEmoji, FormSwitchCard, - HugeDecimalInput, InputErrorMessage, KeyEmoji, MoneyEmoji, + NumericInput, PeopleEmoji, RecycleEmoji, SelectInput, @@ -119,7 +119,7 @@ export const UpdateProposalConfigComponent: ActionComponent< {depositRequired && ( <div className="flex flex-col gap-1"> <div className="flex flex-col gap-1"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.depositInfo?.deposit} fieldName={ @@ -172,7 +172,7 @@ export const UpdateProposalConfigComponent: ActionComponent< <div className="flex grow flex-row flex-wrap gap-2"> {percentageThresholdSelected && ( <div className="flex flex-col gap-1"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.thresholdPercentage} fieldName={ @@ -234,7 +234,7 @@ export const UpdateProposalConfigComponent: ActionComponent< <div className="flex grow flex-row flex-wrap gap-2"> {percentageQuorumSelected && ( <div className="flex flex-col gap-1"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.quorumPercentage} fieldName={ @@ -282,7 +282,7 @@ export const UpdateProposalConfigComponent: ActionComponent< </div> <div className="flex grow flex-row flex-wrap gap-2"> <div className="flex flex-col gap-1"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.proposalDuration} fieldName={ diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV2/UpdateProposalConfigComponent.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV2/UpdateProposalConfigComponent.tsx index 2c5da4c90..ab6934781 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV2/UpdateProposalConfigComponent.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV2/UpdateProposalConfigComponent.tsx @@ -7,9 +7,9 @@ import { ChartEmoji, ClockEmoji, FormSwitchCard, - HugeDecimalInput, InputErrorMessage, KeyEmoji, + NumericInput, PeopleEmoji, ProposalVetoConfigurer, RecycleEmoji, @@ -111,7 +111,7 @@ export const UpdateProposalConfigComponent: ActionComponent< <div className="flex grow flex-row flex-wrap gap-2"> {percentageThresholdSelected && ( <div className="flex flex-col gap-1"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.thresholdPercentage} fieldName={ @@ -170,7 +170,7 @@ export const UpdateProposalConfigComponent: ActionComponent< <div className="flex grow flex-row flex-wrap gap-2"> {percentageQuorumSelected && ( <div className="flex flex-col gap-1"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.quorumPercentage} fieldName={ @@ -217,7 +217,7 @@ export const UpdateProposalConfigComponent: ActionComponent< </div> <div className="flex grow flex-row flex-wrap gap-2"> <div className="flex flex-col gap-1"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.proposalDuration} fieldName={ diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/daoCreation/ThresholdVotingConfigItem.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/daoCreation/ThresholdVotingConfigItem.tsx index f248d7b02..231a7dbc1 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/daoCreation/ThresholdVotingConfigItem.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/daoCreation/ThresholdVotingConfigItem.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import { BallotDepositEmoji, FormSwitchCard, - HugeDecimalInput, + NumericInput, SelectInput, } from '@dao-dao/stateless' import { @@ -36,7 +36,7 @@ const ThresholdInput = ({ <div className="flex flex-col gap-4"> <div className="flex flex-row gap-2"> {!majority && ( - <HugeDecimalInput + <NumericInput containerClassName="grow min-w-[8rem]" error={errors?.threshold?.value} fieldName="threshold.value" diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/MintComponent.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/MintComponent.tsx index 7e6044fb4..29e9aa498 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/MintComponent.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/Mint/MintComponent.tsx @@ -8,8 +8,8 @@ import { useFormContext } from 'react-hook-form' import { HugeDecimal } from '@dao-dao/math' import { - HugeDecimalInput, InputErrorMessage, + NumericInput, useActionOptions, useDetectWrap, } from '@dao-dao/stateless' @@ -55,7 +55,7 @@ export const MintComponent: ActionComponent<MintOptions> = ({ className="flex flex-row flex-wrap items-stretch gap-x-3 gap-y-2" ref={containerRef} > - <HugeDecimalInput + <NumericInput containerClassName="w-full sm:w-auto" disabled={!isCreating} error={errors?.amount} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/UpdateStakingConfig/Component.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/UpdateStakingConfig/Component.tsx index 22865a47e..b6fdbdd12 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/UpdateStakingConfig/Component.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/actions/UpdateStakingConfig/Component.tsx @@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next' import { FormSwitchCard, - HugeDecimalInput, InputErrorMessage, + NumericInput, SelectInput, } from '@dao-dao/stateless' import { @@ -57,7 +57,7 @@ export const UpdateStakingConfigComponent: ActionComponent = ({ {unstakingDurationEnabled && ( <> <div className="flex flex-row gap-2"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.unstakingDuration?.value} fieldName={ diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/Component.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/Component.tsx index c85efe785..266117eea 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/Component.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/Component.tsx @@ -12,11 +12,11 @@ import { useTranslation } from 'react-i18next' import { Button, Checkbox, - HugeDecimalInput, IconButton, InputErrorMessage, InputLabel, Loader, + NumericInput, useActionOptions, useDetectWrap, } from '@dao-dao/stateless' @@ -102,7 +102,7 @@ export const ManageMembersComponent: ActionComponent< > <div className="flex flex-col gap-1"> <InputLabel name={t('form.votingWeightPlaceholder')} /> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.toAdd?.[index]?.weight} fieldName={weightFieldName} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/actions/UpdateStakingConfig/Component.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/actions/UpdateStakingConfig/Component.tsx index 22865a47e..b6fdbdd12 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/actions/UpdateStakingConfig/Component.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/actions/UpdateStakingConfig/Component.tsx @@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next' import { FormSwitchCard, - HugeDecimalInput, InputErrorMessage, + NumericInput, SelectInput, } from '@dao-dao/stateless' import { @@ -57,7 +57,7 @@ export const UpdateStakingConfigComponent: ActionComponent = ({ {unstakingDurationEnabled && ( <> <div className="flex flex-row gap-2"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.unstakingDuration?.value} fieldName={ diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.tsx index a13e2f7f9..31504066d 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.tsx @@ -2,7 +2,7 @@ import { useFormContext } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' -import { HugeDecimalInput, InputErrorMessage } from '@dao-dao/stateless' +import { InputErrorMessage, NumericInput } from '@dao-dao/stateless' import { ActionComponent, GenericToken } from '@dao-dao/types' import { validatePositive, validateRequired } from '@dao-dao/utils' @@ -25,7 +25,7 @@ export const MintComponent: ActionComponent<MintOptions> = ({ return ( <> - <HugeDecimalInput + <NumericInput containerClassName="w-full" disabled={!isCreating} error={errors?.amount} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.tsx index 22865a47e..b6fdbdd12 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.tsx @@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next' import { FormSwitchCard, - HugeDecimalInput, InputErrorMessage, + NumericInput, SelectInput, } from '@dao-dao/stateless' import { @@ -57,7 +57,7 @@ export const UpdateStakingConfigComponent: ActionComponent = ({ {unstakingDurationEnabled && ( <> <div className="flex flex-row gap-2"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.unstakingDuration?.value} fieldName={ diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/actions/UpdateStakingConfig/Component.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/actions/UpdateStakingConfig/Component.tsx index 22865a47e..b6fdbdd12 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/actions/UpdateStakingConfig/Component.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/actions/UpdateStakingConfig/Component.tsx @@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next' import { FormSwitchCard, - HugeDecimalInput, InputErrorMessage, + NumericInput, SelectInput, } from '@dao-dao/stateless' import { @@ -57,7 +57,7 @@ export const UpdateStakingConfigComponent: ActionComponent = ({ {unstakingDurationEnabled && ( <> <div className="flex flex-row gap-2"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.unstakingDuration?.value} fieldName={ diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.tsx index 53dae6f34..565305541 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.tsx @@ -9,8 +9,8 @@ import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { - HugeDecimalInput, InputErrorMessage, + NumericInput, StatusCard, useChain, useDetectWrap, @@ -61,7 +61,7 @@ export const MintComponent: ActionComponent<MintOptions> = ({ className="flex min-w-0 flex-row flex-wrap items-stretch justify-between gap-x-3 gap-y-1" ref={containerRef} > - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.amount} fieldName={(fieldNamePrefix + 'amount') as 'amount'} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/UpdateStakingConfig/Component.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/UpdateStakingConfig/Component.tsx index 22865a47e..b6fdbdd12 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/UpdateStakingConfig/Component.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/UpdateStakingConfig/Component.tsx @@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next' import { FormSwitchCard, - HugeDecimalInput, InputErrorMessage, + NumericInput, SelectInput, } from '@dao-dao/stateless' import { @@ -57,7 +57,7 @@ export const UpdateStakingConfigComponent: ActionComponent = ({ {unstakingDurationEnabled && ( <> <div className="flex flex-row gap-2"> - <HugeDecimalInput + <NumericInput disabled={!isCreating} error={errors?.unstakingDuration?.value} fieldName={ diff --git a/packages/stateless/components/inputs/HugeDecimalInput.stories.tsx b/packages/stateless/components/inputs/NumericInput.stories.tsx similarity index 68% rename from packages/stateless/components/inputs/HugeDecimalInput.stories.tsx rename to packages/stateless/components/inputs/NumericInput.stories.tsx index a20668479..75d638c6a 100644 --- a/packages/stateless/components/inputs/HugeDecimalInput.stories.tsx +++ b/packages/stateless/components/inputs/NumericInput.stories.tsx @@ -3,20 +3,19 @@ import { useFormContext } from 'react-hook-form' import { ReactHookFormDecorator } from '@dao-dao/storybook/decorators' -import { HugeDecimalInput } from './HugeDecimalInput' +import { NumericInput } from './NumericInput' export default { - title: - 'DAO DAO / packages / stateless / components / inputs / HugeDecimalInput', - component: HugeDecimalInput, + title: 'DAO DAO / packages / stateless / components / inputs / NumericInput', + component: NumericInput, decorators: [ReactHookFormDecorator], -} as ComponentMeta<typeof HugeDecimalInput> +} as ComponentMeta<typeof NumericInput> -const Template: ComponentStory<typeof HugeDecimalInput> = (args) => { +const Template: ComponentStory<typeof NumericInput> = (args) => { const { register, setValue, getValues } = useFormContext() return ( - <HugeDecimalInput + <NumericInput {...args} getValues={getValues} register={register} diff --git a/packages/stateless/components/inputs/HugeDecimalInput.tsx b/packages/stateless/components/inputs/NumericInput.tsx similarity index 94% rename from packages/stateless/components/inputs/HugeDecimalInput.tsx rename to packages/stateless/components/inputs/NumericInput.tsx index 5eb6ddcaf..0f1e2ddcd 100644 --- a/packages/stateless/components/inputs/HugeDecimalInput.tsx +++ b/packages/stateless/components/inputs/NumericInput.tsx @@ -5,17 +5,18 @@ import { FieldValues, Path } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' -import { HugeDecimalInputProps } from '@dao-dao/types' +import { NumericInputProps } from '@dao-dao/types' import { toAccessibleImageUrl } from '@dao-dao/utils' import { IconButton } from '../icon_buttons' /** - * This is input is designed for numeric values and takes advantage of the - * HugeDecimal class to handle large decimal numbers gracefully. It expects the - * underlying value to be a human-readable string. You can optionally store the - * underlying value as a number by setting the `numericValue` prop—this should - * only be used when not needing to store potentially large numbers. + * This input is designed for numeric values and takes advantage of the + * HugeDecimal class to handle large decimal numbers gracefully. + * + * By default, it uses human-readable strings for value storage. You can instead + * use a number by setting the `numericValue` prop, but this should only be used + * when not needing to store potentially large numbers. * * There is no need to provide `value` when providing `fieldName` and `register` * via react-hook-form. @@ -27,7 +28,7 @@ import { IconButton } from '../icon_buttons' * function can easily be mocked, and its first argument (`fieldName`) can be * ignored. */ -export const HugeDecimalInput = < +export const NumericInput = < FV extends FieldValues, FieldName extends Path<FV> >({ @@ -54,7 +55,7 @@ export const HugeDecimalInput = < unitContainerClassName, plusMinusButtonSize = 'sm', ...props -}: HugeDecimalInputProps<FV, FieldName>) => { +}: NumericInputProps<FV, FieldName>) => { const { t } = useTranslation() const validate = validation?.reduce( (a, v) => ({ ...a, [v.toString()]: v }), diff --git a/packages/stateless/components/inputs/TokenInput.tsx b/packages/stateless/components/inputs/TokenInput.tsx index 3e9f075aa..dfeb17bb0 100644 --- a/packages/stateless/components/inputs/TokenInput.tsx +++ b/packages/stateless/components/inputs/TokenInput.tsx @@ -26,7 +26,7 @@ import { ChainLogo } from '../chain/ChainLogo' import { IconButton } from '../icon_buttons' import { FilterableItem, FilterableItemPopup } from '../popup' import { Tooltip } from '../tooltip' -import { HugeDecimalInput } from './HugeDecimalInput' +import { NumericInput } from './NumericInput' import { TextInput } from './TextInput' /** @@ -247,7 +247,7 @@ export const TokenInput = < ) : ( <> {amountField && ( - <HugeDecimalInput + <NumericInput {...amountField} containerClassName="min-w-[12rem] grow basis-[12rem]" disabled={disabled || (!selectedToken && !customSelected)} diff --git a/packages/stateless/components/inputs/index.ts b/packages/stateless/components/inputs/index.ts index 52e0cd107..c7519f1b3 100644 --- a/packages/stateless/components/inputs/index.ts +++ b/packages/stateless/components/inputs/index.ts @@ -7,13 +7,13 @@ export * from './DaoSupportedChainPickerInput' export * from './Dropdown' export * from './FileDropInput' export * from './FileUploadInput' -export * from './HugeDecimalInput' export * from './ImageDropInput' export * from './ImageSelector' export * from './ImageUploadInput' export * from './InputErrorMessage' export * from './InputLabel' export * from './InputThemedText' +export * from './NumericInput' export * from './PercentButton' export * from './RadioInput' export * from './RangeInput' diff --git a/packages/stateless/components/modals/TokenDepositModal.tsx b/packages/stateless/components/modals/TokenDepositModal.tsx index 2d47879a2..c490d0bea 100644 --- a/packages/stateless/components/modals/TokenDepositModal.tsx +++ b/packages/stateless/components/modals/TokenDepositModal.tsx @@ -12,7 +12,7 @@ import { import { shortenTokenSymbol } from '@dao-dao/utils' import { Button } from '../buttons/Button' -import { HugeDecimalInput, PercentButton } from '../inputs' +import { NumericInput, PercentButton } from '../inputs' import { TokenAmountDisplay } from '../token/TokenAmountDisplay' import { Modal } from './Modal' @@ -114,7 +114,7 @@ export const TokenDepositModal = ({ </div> )} - <HugeDecimalInput + <NumericInput // Auto focus does not work on mobile Safari by design // (https://bugs.webkit.org/show_bug.cgi?id=195884#c4). autoFocus={modalProps.visible} diff --git a/packages/stateless/components/proposal/ProposalVetoConfigurer.tsx b/packages/stateless/components/proposal/ProposalVetoConfigurer.tsx index b736336d2..8c30df394 100644 --- a/packages/stateless/components/proposal/ProposalVetoConfigurer.tsx +++ b/packages/stateless/components/proposal/ProposalVetoConfigurer.tsx @@ -22,9 +22,9 @@ import { Button } from '../buttons' import { IconButton } from '../icon_buttons' import { FormSwitchCard, - HugeDecimalInput, InputErrorMessage, InputLabel, + NumericInput, SelectInput, } from '../inputs' @@ -173,7 +173,7 @@ export const ProposalVetoConfigurer = ({ /> <div className="flex flex-row gap-2"> - <HugeDecimalInput + <NumericInput containerClassName="grow" disabled={disabled} error={errors?.timelockDuration?.value} diff --git a/packages/stateless/components/token/StakingModal.tsx b/packages/stateless/components/token/StakingModal.tsx index eea566b3c..b59fc5eb2 100644 --- a/packages/stateless/components/token/StakingModal.tsx +++ b/packages/stateless/components/token/StakingModal.tsx @@ -12,8 +12,8 @@ import { convertDurationToHumanReadableString } from '@dao-dao/utils' import { Button } from '../buttons/Button' import { - HugeDecimalInput, InputLabel, + NumericInput, PercentButton, SegmentedControls, TokenInput, @@ -267,7 +267,7 @@ const StakeUnstakeModesBody = ({ return ( <> <h2 className="primary-text mb-6">{t('title.chooseTokenAmount')}</h2> - <HugeDecimalInput + <NumericInput containerClassName="py-7 w-full h-20 pl-6 pr-8 bg-background-secondary rounded-md gap-4" ghost max={ diff --git a/packages/types/components/HugeDecimalInput.ts b/packages/types/components/NumericInput.ts similarity index 55% rename from packages/types/components/HugeDecimalInput.ts rename to packages/types/components/NumericInput.ts index a213b3c11..a9ef14205 100644 --- a/packages/types/components/HugeDecimalInput.ts +++ b/packages/types/components/NumericInput.ts @@ -7,35 +7,50 @@ import { Validate, } from 'react-hook-form' -export type HugeDecimalInputProps< +export type NumericInputProps< FV extends FieldValues, FieldName extends Path<FV> > = Omit<ComponentPropsWithoutRef<'input'>, 'type' | 'required' | 'onInput'> & { - // The field name for the form. + /** + * The field name for the form. + */ fieldName?: FieldName - // Register function returned by `useForm`/`useFormContext`. + /** + * Register function returned by `useForm`/`useFormContext`. + */ register?: UseFormRegister<FV> - // Validations to apply when registering this input. + /** + * Validations to apply when registering this input. + */ validation?: Validate<FieldPathValue<FV, FieldName>>[] - // Applies to the input when registering with a form. + /** + * Applies to the input when registering with a form. + */ required?: boolean - // If error present, outline input in red. + /** + * If error present, outline input in red. + */ error?: any - // Hide plus/minus buttons + /** + * Hide plus/minus buttons + */ hidePlusMinus?: boolean /** * Whether or not to store the value as a number in the react-hook-form form. * This should only be used when not needing to store potentially large - * numbers. This is discouraged as it loses the benefits of using - * `HugeDecimal` internally. + * numbers. * * Defaults to false. */ numericValue?: boolean - // Value passed to the input. + /** + * Value passed to the input. + */ value?: string - // Used to get the value when the plus/minus buttons are clicked. Accepts the - // react-hook-form `getValues` function, or any custom function. + /** + * Used to get the value when the plus/minus buttons are clicked. Accepts the + * react-hook-form `getValues` function, or any custom function. + */ getValues?: (fieldName: any) => string | undefined /** * Used to set the value when the input changes (if `register` is not pased @@ -48,25 +63,45 @@ export type HugeDecimalInputProps< value: string | number, options?: { shouldValidate: boolean } ) => void - // Applies to the outer-most container, which contains the plus/minus buttons, - // the input, and the unit. + /** + * Applies to the outer-most container, which contains the plus/minus buttons, + * the input, and the unit. + */ containerClassName?: string - // Size of the container. + /** + * Size of the container. + */ sizing?: 'sm' | 'md' | 'lg' | 'auto' | 'fill' | 'none' - // Remove padding, rounded corners, and outline. + /** + * Remove padding, rounded corners, and outline. + */ ghost?: boolean - // A unit to display to the right of the number. + /** + * A unit to display to the right of the number. + */ unit?: string - // A unit icon URL that displays to the left of the unit. + /** + * A unit icon URL that displays to the left of the unit. + */ unitIconUrl?: string - // Applies to both the input text and the unit. + /** + * Applies to both the input text and the unit. + */ textClassName?: string - // Applies to the unit only. + /** + * Applies to the unit only. + */ unitClassName?: string - // Applies to the unit icon only. + /** + * Applies to the unit icon only. + */ unitIconClassName?: string - // Applies to the unit container only. + /** + * Applies to the unit container only. + */ unitContainerClassName?: string - // Size of the plus/minus buttons. Defaults to 'sm'. + /** + * Size of the plus/minus buttons. Defaults to 'sm'. + */ plusMinusButtonSize?: 'sm' | 'lg' } diff --git a/packages/types/components/TokenInput.ts b/packages/types/components/TokenInput.ts index f38178f55..b3b389f49 100644 --- a/packages/types/components/TokenInput.ts +++ b/packages/types/components/TokenInput.ts @@ -11,7 +11,7 @@ import { import { LoadingData } from '../misc' import { GenericToken, TokenType } from '../token' -import { HugeDecimalInputProps } from './HugeDecimalInput' +import { NumericInputProps } from './NumericInput' export type TokenInputOption = Omit<GenericToken, 'type'> & { type: TokenType | string @@ -27,7 +27,7 @@ export type TokenInputProps< * The fields that control the amount input. */ amount?: Omit< - HugeDecimalInputProps<FV, FieldName>, + NumericInputProps<FV, FieldName>, | 'containerClassName' | 'disabled' | 'register' diff --git a/packages/types/components/index.ts b/packages/types/components/index.ts index be1a7a9fb..905efd0c9 100644 --- a/packages/types/components/index.ts +++ b/packages/types/components/index.ts @@ -25,7 +25,6 @@ export * from './DappNavigation' export * from './DisconnectWallet' export * from './EntityDisplay' export * from './GovProposalLine' -export * from './HugeDecimalInput' export * from './IconButtonifier' export * from './IconButtonLink' export * from './ImportMultisigModal' @@ -37,6 +36,7 @@ export * from './Modal' export * from './NavWallet' export * from './NavWalletConnected' export * from './Notifications' +export * from './NumericInput' export * from './PageHeader' export * from './PayEntityDisplay' export * from './Popup' From 54d2688765fd8c83b062b1610baa797db18f891b Mon Sep 17 00:00:00 2001 From: Noah Saso <noahsaso@gmail.com> Date: Wed, 2 Oct 2024 00:21:45 -0400 Subject: [PATCH 20/26] fix IBC amount out comparison --- packages/stateful/actions/core/actions/Spend/Component.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/stateful/actions/core/actions/Spend/Component.tsx b/packages/stateful/actions/core/actions/Spend/Component.tsx index 7afb8f3ae..0e00f0184 100644 --- a/packages/stateful/actions/core/actions/Spend/Component.tsx +++ b/packages/stateful/actions/core/actions/Spend/Component.tsx @@ -860,7 +860,9 @@ export const SpendComponent: ActionComponent<SpendOptions> = ({ !ibcAmountOut.loading && !ibcAmountOut.errored && ibcAmountOut.data && - !ibcAmountOut.data.eq(spendAmount) && ( + !ibcAmountOut.data + .toHumanReadable(selectedToken.token.decimals) + .eq(spendAmount) && ( <div className="flex flex-col gap-2 mt-1"> <InputLabel name={t('form.amountReceived')} /> From 3b7279f7daffd9309696d8628d5f56a5bc7d7bac Mon Sep 17 00:00:00 2001 From: Noah Saso <noahsaso@gmail.com> Date: Wed, 2 Oct 2024 00:49:16 -0400 Subject: [PATCH 21/26] tweaked formatting --- packages/stateful/actions/core/actions/Spend/Component.tsx | 1 + packages/stateless/components/token/TokenAmountDisplay.tsx | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/stateful/actions/core/actions/Spend/Component.tsx b/packages/stateful/actions/core/actions/Spend/Component.tsx index 0e00f0184..8bfba43a7 100644 --- a/packages/stateful/actions/core/actions/Spend/Component.tsx +++ b/packages/stateful/actions/core/actions/Spend/Component.tsx @@ -858,6 +858,7 @@ export const SpendComponent: ActionComponent<SpendOptions> = ({ {selectedToken && !ibcAmountOut.loading && + !ibcAmountOut.updating && !ibcAmountOut.errored && ibcAmountOut.data && !ibcAmountOut.data diff --git a/packages/stateless/components/token/TokenAmountDisplay.tsx b/packages/stateless/components/token/TokenAmountDisplay.tsx index c2e05082b..035b16cd4 100644 --- a/packages/stateless/components/token/TokenAmountDisplay.tsx +++ b/packages/stateless/components/token/TokenAmountDisplay.tsx @@ -95,8 +95,7 @@ export const TokenAmountDisplay = ({ : HugeDecimal.zero // If amount too small, set to min and add `< ` to prefix. - const amountBelowMin = - !!minAmount && amount.isPositive() && amount.lt(minAmount) + const amountBelowMin = !!minAmount && amount.lt(minAmount) if (amountBelowMin) { amount = HugeDecimal.fromHumanReadable(minAmount, decimals) prefix = `< ${prefix || ''}` From c7ab8409e3935ea1fd08fba6bad8d82c1d67cf6a Mon Sep 17 00:00:00 2001 From: Noah Saso <noahsaso@gmail.com> Date: Wed, 2 Oct 2024 12:29:33 -0400 Subject: [PATCH 22/26] misc improvements --- packages/i18n/locales/en/translation.json | 3 ++ packages/state/recoil/selectors/treasury.ts | 10 +++--- .../CreateRewardDistribution/Component.tsx | 8 ++--- .../CreateValenceAccount/Component.tsx | 4 ++- .../actions/EnableMultipleChoice/index.tsx | 4 +-- .../FundRewardDistribution/Component.tsx | 2 +- .../UpdateRewardDistribution/Component.tsx | 4 +-- .../components/dao/DaoTxTreasuryHistory.tsx | 32 +++++++++-------- .../gov/GovProposalStatusAndInfo.tsx | 10 +++--- .../components/vesting/VestingPaymentCard.tsx | 12 ++----- .../UpdateProposalConfigComponent.tsx | 4 +-- .../components/MembersTab.tsx | 4 +-- .../components/ProfileCardMemberInfo.tsx | 21 ++++++------ .../components/StakingModal.tsx | 34 ++++++++++++------- .../components/ProfileCardMemberInfo.tsx | 11 +++--- .../components/StakingModal.tsx | 14 +++++--- .../components/MembersTab.tsx | 4 +-- .../components/ProfileCardMemberInfo.tsx | 21 ++++++------ .../components/StakingModal.tsx | 34 ++++++++++++------- .../components/ProfileCardMemberInfo.tsx | 11 +++--- .../components/StakingModal.tsx | 10 ++++-- .../components/MembersTab.tsx | 4 +-- .../components/ProfileCardMemberInfo.tsx | 21 ++++++------ .../components/StakingModal.tsx | 34 ++++++++++++------- .../components/ProfileCardMemberInfo.tsx | 4 +-- .../components/StakingModal.tsx | 18 ++++++---- .../ProfileCardMemberInfoTokens.stories.tsx | 9 ++--- .../ProfileCardMemberInfoTokens.tsx | 16 ++++----- .../stateless/pages/ViewSurvey/Complete.tsx | 19 ++++++----- .../stateless/pages/ViewSurvey/Rate.tsx | 19 ++++++----- .../components/dao/tabs/MembersTab.tsx | 9 ++++- .../components/token/TokenAmountDisplay.tsx | 2 +- .../vesting/VestingPaymentCard.stories.tsx | 6 ++-- .../components/vesting/VestingPaymentCard.tsx | 23 +++++++------ .../stateless/hooks/useTokenSortOptions.ts | 21 ++++++------ packages/types/components/DaoMemberCard.ts | 4 ++- packages/utils/dao.ts | 8 ++--- packages/utils/token.ts | 19 ++++++----- 38 files changed, 275 insertions(+), 218 deletions(-) diff --git a/packages/i18n/locales/en/translation.json b/packages/i18n/locales/en/translation.json index c037d87e1..81cdbd236 100644 --- a/packages/i18n/locales/en/translation.json +++ b/packages/i18n/locales/en/translation.json @@ -1618,6 +1618,7 @@ "addedToProfile": "Added to profile successfully.", "addedToken": "Added token to Keplr.", "claimedRewards": "Claimed rewards.", + "claimedTokens": "Claimed {{amount}} ${{tokenSymbol}}.", "compensationCycleCompleted_noProposal": "Compensation cycle completed.", "compensationCycleCompleted_withProposal": "Compensation cycle completed. Redirecting to proposal...", "compensationCycleCreated": "Compensation cycle created.", @@ -1649,10 +1650,12 @@ "restaked": "Restaked successfully.", "saved": "Saved successfully.", "staked": "Staked successfully.", + "stakedTokens": "Staked {{amount}} ${{tokenSymbol}}.", "tokenSwapContractInstantiated": "Token swap created successfully.", "transactionExecuted": "Transaction executed successfully.", "unregistered": "Unregistered successfully.", "unstaked": "Unstaked successfully.", + "unstakedTokens": "Unstaked {{amount}} ${{tokenSymbol}}.", "voteCast": "Vote successfully cast.", "withdrewPayment": "Withdrew payment." }, diff --git a/packages/state/recoil/selectors/treasury.ts b/packages/state/recoil/selectors/treasury.ts index 292444c83..bbd30e868 100644 --- a/packages/state/recoil/selectors/treasury.ts +++ b/packages/state/recoil/selectors/treasury.ts @@ -103,8 +103,8 @@ export interface TransformedTreasuryTransaction { timestamp: Date | undefined sender: string recipient: string - amount: number - denomLabel: string + amount: HugeDecimal + token: GenericToken outgoing: boolean } @@ -152,10 +152,8 @@ export const transformedTreasuryTransactionsSelector = selectorFamily< timestamp, sender, recipient, - amount: HugeDecimal.from(coin.amount).toHumanReadableNumber( - token.decimals - ), - denomLabel: token.symbol, + amount: HugeDecimal.from(coin), + token, outgoing: sender === params.address, } }) diff --git a/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx index 4ead23be0..e8cc888c4 100644 --- a/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/CreateRewardDistribution/Component.tsx @@ -200,10 +200,10 @@ export const CreateRewardDistributionComponent: ActionComponent< error={errors?.rate?.amount} fieldName={(fieldNamePrefix + 'rate.amount') as 'rate.amount'} getValues={getValues} - min={HugeDecimal.one.toHumanReadableString(decimals)} + min={HugeDecimal.one.toHumanReadableNumber(decimals)} register={register} setValue={setValue} - step={HugeDecimal.one.toHumanReadableString(decimals)} + step={HugeDecimal.one.toHumanReadableNumber(decimals)} unit={ selectedToken ? '$' + selectedToken?.token.symbol @@ -270,10 +270,10 @@ export const CreateRewardDistributionComponent: ActionComponent< disabled={!isCreating} fieldName={(fieldNamePrefix + 'initialFunds') as 'initialFunds'} getValues={getValues} - min={HugeDecimal.zero.toHumanReadableString(decimals)} + min={0} register={register} setValue={setValue} - step={HugeDecimal.one.toHumanReadableString(decimals)} + step={HugeDecimal.one.toHumanReadableNumber(decimals)} unit={ selectedToken ? '$' + selectedToken?.token.symbol : t('info.tokens') } diff --git a/packages/stateful/actions/core/actions/CreateValenceAccount/Component.tsx b/packages/stateful/actions/core/actions/CreateValenceAccount/Component.tsx index 4559ac72f..6409975c5 100644 --- a/packages/stateful/actions/core/actions/CreateValenceAccount/Component.tsx +++ b/packages/stateful/actions/core/actions/CreateValenceAccount/Component.tsx @@ -173,7 +173,9 @@ export const CreateValenceAccountComponent: ActionComponent< ? t('format.token', { amount: HugeDecimal.from( serviceFee.data.balance - ).toHumanReadableNumber(serviceFee.data.token.decimals), + ).toInternationalizedHumanReadableString({ + decimals: serviceFee.data.token.decimals, + }), symbol: serviceFee.data.token.symbol, }) : '', diff --git a/packages/stateful/actions/core/actions/EnableMultipleChoice/index.tsx b/packages/stateful/actions/core/actions/EnableMultipleChoice/index.tsx index 68f14afb4..6639c392e 100644 --- a/packages/stateful/actions/core/actions/EnableMultipleChoice/index.tsx +++ b/packages/stateful/actions/core/actions/EnableMultipleChoice/index.tsx @@ -166,8 +166,8 @@ export class EnableMultipleChoiceAction extends ActionBase<{}> { amount: depositInfoWithToken.depositInfo ? HugeDecimal.from( depositInfoWithToken.depositInfo.amount - ).toHumanReadableNumber(depositInfoWithToken.token.decimals) - : 10, + ).toHumanReadableString(depositInfoWithToken.token.decimals) + : '10', type: depositInfoWithToken.depositInfo && 'cw20' in depositInfoWithToken.depositInfo.denom diff --git a/packages/stateful/actions/core/actions/FundRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/FundRewardDistribution/Component.tsx index 3e4de3862..69feb779e 100644 --- a/packages/stateful/actions/core/actions/FundRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/FundRewardDistribution/Component.tsx @@ -165,7 +165,7 @@ export const FundRewardDistributionComponent: ActionComponent< disabled={!isCreating} fieldName={(fieldNamePrefix + 'amount') as 'amount'} getValues={getValues} - min={HugeDecimal.one.toHumanReadableString( + min={HugeDecimal.one.toHumanReadableNumber( selectedDistribution.token.decimals )} register={register} diff --git a/packages/stateful/actions/core/actions/UpdateRewardDistribution/Component.tsx b/packages/stateful/actions/core/actions/UpdateRewardDistribution/Component.tsx index e846dca59..7a9f1da12 100644 --- a/packages/stateful/actions/core/actions/UpdateRewardDistribution/Component.tsx +++ b/packages/stateful/actions/core/actions/UpdateRewardDistribution/Component.tsx @@ -194,10 +194,10 @@ export const UpdateRewardDistributionComponent: ActionComponent< error={errors?.rate?.amount} fieldName={(fieldNamePrefix + 'rate.amount') as 'rate.amount'} getValues={getValues} - min={HugeDecimal.one.toHumanReadableString(decimals)} + min={HugeDecimal.one.toHumanReadableNumber(decimals)} register={register} setValue={setValue} - step={HugeDecimal.one.toHumanReadableString(decimals)} + step={HugeDecimal.one.toHumanReadableNumber(decimals)} unit={ selectedDistribution?.token ? '$' + selectedDistribution.token.symbol diff --git a/packages/stateful/components/dao/DaoTxTreasuryHistory.tsx b/packages/stateful/components/dao/DaoTxTreasuryHistory.tsx index 167c7b082..545455cbf 100644 --- a/packages/stateful/components/dao/DaoTxTreasuryHistory.tsx +++ b/packages/stateful/components/dao/DaoTxTreasuryHistory.tsx @@ -18,11 +18,12 @@ import { CopyToClipboard, LineGraph, Loader, + TokenAmountDisplay, useChainContext, useConfiguredChainContext, useDaoInfoContext, } from '@dao-dao/stateless' -import { processError } from '@dao-dao/utils' +import { processError, tokensEqual } from '@dao-dao/utils' import { IconButtonLink } from '../IconButtonLink' import { SuspenseLoader } from '../SuspenseLoader' @@ -163,28 +164,29 @@ export const InnerDaoTxTreasuryHistory = ({ }) ) const lineGraphValues = useMemo(() => { + if (!nativeToken) { + return [] + } + let runningTotal = HugeDecimal.from( nativeBalance.amount - ).toHumanReadableNumber(nativeToken?.decimals ?? 0) + ).toHumanReadableNumber(nativeToken.decimals) return ( transactions - .filter(({ denomLabel }) => denomLabel === nativeToken?.symbol) + .filter(({ token }) => tokensEqual(token, nativeToken)) .map(({ amount, outgoing }) => { let currentTotal = runningTotal - runningTotal -= (outgoing ? -1 : 1) * amount + runningTotal -= amount + .times(outgoing ? -1 : 1) + .toHumanReadableNumber(nativeToken.decimals) return currentTotal }) // Reverse since transactions are descending, but we want the graph to // display ascending balance. .reverse() ) - }, [ - nativeBalance.amount, - nativeToken?.decimals, - nativeToken?.symbol, - transactions, - ]) + }, [nativeBalance.amount, nativeToken, transactions]) return ( <div className="flex flex-col gap-y-4"> @@ -254,7 +256,7 @@ const TransactionRenderer = ({ sender, recipient, amount, - denomLabel, + token, outgoing, }, }: TransactionRendererProps) => { @@ -270,9 +272,11 @@ const TransactionRenderer = ({ ) : ( <East className="!h-4 !w-4" /> )} - <p> - {amount} ${denomLabel} - </p> + <TokenAmountDisplay + amount={amount} + decimals={token.decimals} + symbol={token.symbol} + /> </div> <p className="flex flex-row items-center gap-4 text-right font-mono text-xs leading-6"> diff --git a/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx b/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx index 0260a6d81..3ea166e7e 100644 --- a/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx +++ b/packages/stateful/components/gov/GovProposalStatusAndInfo.tsx @@ -172,11 +172,11 @@ const InnerGovProposalStatusAndInfo = ({ Value: (props) => ( <p {...props}> {t('format.token', { - amount: HugeDecimal.from(currentDepositAmount) - .toHumanReadableNumber(depositToken.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: depositToken.decimals, - }), + amount: HugeDecimal.from( + currentDepositAmount + ).toInternationalizedHumanReadableString({ + decimals: depositToken.decimals, + }), symbol: depositToken.symbol, })} </p> diff --git a/packages/stateful/components/vesting/VestingPaymentCard.tsx b/packages/stateful/components/vesting/VestingPaymentCard.tsx index d5287cf83..3d5e407d9 100644 --- a/packages/stateful/components/vesting/VestingPaymentCard.tsx +++ b/packages/stateful/components/vesting/VestingPaymentCard.tsx @@ -279,15 +279,11 @@ export const VestingPaymentCard = ({ // Canceled vests have their curves set to constant. 'constant' in vest.vested } - claimedAmount={HugeDecimal.from(vest.claimed).toHumanReadableNumber( - token.decimals - )} + claimedAmount={HugeDecimal.from(vest.claimed)} claiming={claiming} cw20Address={cw20Address} description={vest.description} - distributableAmount={HugeDecimal.from( - distributable - ).toHumanReadableNumber(token.decimals)} + distributableAmount={distributable} endDate={endDate} isWalletConnected={isWalletConnected} lazyInfo={lazyInfoLoading} @@ -300,9 +296,7 @@ export const VestingPaymentCard = ({ recipient={vest.recipient} recipientEntity={recipientEntity} recipientIsWallet={recipientIsWallet} - remainingBalanceVesting={HugeDecimal.from( - Number(total) - Number(vested) - ).toHumanReadableNumber(token.decimals)} + remainingBalanceVesting={total.minus(vested)} startDate={startDate} steps={steps} title={vest.title} diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.tsx index 2f0e2989d..a17d2621f 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/UpdateProposalConfigComponent.tsx @@ -127,11 +127,11 @@ export const UpdateProposalConfigComponent: ActionComponent< 'depositInfo.deposit') as 'depositInfo.deposit' } getValues={getValues} - min={HugeDecimal.one.toHumanReadableString( + min={HugeDecimal.one.toHumanReadableNumber( commonGovernanceTokenInfo.decimals )} register={register} - step={HugeDecimal.one.toHumanReadableString( + step={HugeDecimal.one.toHumanReadableNumber( commonGovernanceTokenInfo.decimals )} unit={'$' + commonGovernanceTokenInfo.symbol} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/MembersTab.tsx index 1a509ec7a..0dd0dd4d1 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/MembersTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/MembersTab.tsx @@ -41,9 +41,7 @@ export const MembersTab = () => { loading: false, data: { token: governanceToken, - amount: HugeDecimal.from(balance).toHumanReadableNumber( - governanceToken.decimals - ), + amount: HugeDecimal.from(balance), }, }, votingPowerPercent: { diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx index c19dfe4a4..8c889f0a1 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx @@ -135,11 +135,14 @@ export const ProfileCardMemberInfo = ({ refreshClaims?.() toast.success( - `Claimed ${HugeDecimal.from(sumClaimsAvailable) - .toHumanReadableNumber(governanceToken.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: governanceToken.decimals, - })} $${governanceToken.symbol}` + t('success.claimedTokens', { + amount: HugeDecimal.from( + sumClaimsAvailable + ).toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + }), + tokenSymbol: governanceToken.symbol, + }) ) } catch (err) { console.error(err) @@ -225,12 +228,8 @@ export const ProfileCardMemberInfo = ({ data: [ { token: governanceToken, - staked: HugeDecimal.from( - loadingWalletStakedValue.data - ).toHumanReadableNumber(governanceToken.decimals), - unstaked: HugeDecimal.from( - loadingUnstakedBalance.data - ).toHumanReadableNumber(governanceToken.decimals), + staked: HugeDecimal.from(loadingWalletStakedValue.data), + unstaked: HugeDecimal.from(loadingUnstakedBalance.data), }, ], } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx index 3be073e61..a599a0ffa 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx @@ -186,9 +186,12 @@ const InnerStakingModal = ({ setAmount(HugeDecimal.zero) toast.success( - `Staked ${amount.toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - })} $${governanceToken.symbol}` + t('success.stakedTokens', { + amount: amount.toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + }), + tokenSymbol: governanceToken.symbol, + }) ) // Close once done. @@ -256,9 +259,12 @@ const InnerStakingModal = ({ setAmount(HugeDecimal.zero) toast.success( - `Unstaked ${amount.toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - })} $${governanceToken.symbol}` + t('success.unstakedTokens', { + amount: amount.toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + }), + tokenSymbol: governanceToken.symbol, + }) ) // Close once done. @@ -274,7 +280,8 @@ const InnerStakingModal = ({ } case StakingMode.Claim: { if (sumClaimsAvailable === 0) { - return toast.error('No claims available.') + toast.error(t('error.noClaimsAvailable')) + return } setStakingLoading(true) @@ -299,11 +306,14 @@ const InnerStakingModal = ({ setAmount(HugeDecimal.zero) toast.success( - `Claimed ${HugeDecimal.from( - sumClaimsAvailable || 0 - ).toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - })} $${governanceToken.symbol}` + t('success.claimedTokens', { + amount: HugeDecimal.from( + sumClaimsAvailable || 0 + ).toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + }), + tokenSymbol: governanceToken.symbol, + }) ) // Close once done. diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx index bfb43b8bf..bd6afcc56 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx @@ -100,9 +100,10 @@ export const ProfileCardMemberInfo = ({ refreshClaims?.() toast.success( - `Claimed ${sumClaimsAvailable.toLocaleString()} $${ - collectionInfo.symbol - }` + t('success.claimedTokens', { + amount: sumClaimsAvailable.toLocaleString(), + tokenSymbol: collectionInfo.symbol, + }) ) } catch (err) { console.error(err) @@ -178,8 +179,8 @@ export const ProfileCardMemberInfo = ({ data: [ { token, - staked: loadingWalletStakedValue.data, - unstaked: loadingUnstakedBalance.data, + staked: HugeDecimal.from(loadingWalletStakedValue.data), + unstaked: HugeDecimal.from(loadingUnstakedBalance.data), }, ], } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/StakingModal.tsx index 3463c162b..b1958daa2 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/StakingModal.tsx @@ -144,7 +144,10 @@ const InnerStakingModal = ({ refreshDaoVotingPower() toast.success( - `Staked ${stakeTokenIds.length} $${collectionInfo.symbol}` + t('success.stakedTokens', { + amount: stakeTokenIds.length, + tokenSymbol: collectionInfo.symbol, + }) ) setStakeTokenIds([]) @@ -181,7 +184,10 @@ const InnerStakingModal = ({ refreshDaoVotingPower() toast.success( - `Unstaked ${unstakeTokenIds.length} $${collectionInfo.symbol}` + t('success.unstakedTokens', { + amount: unstakeTokenIds.length, + tokenSymbol: collectionInfo.symbol, + }) ) setUnstakeTokenIds([]) @@ -266,11 +272,11 @@ const InnerStakingModal = ({ selected={mode} tabs={[ { - label: t(`title.stakingModeNfts.stake`), + label: t('title.stakingModeNfts.stake'), value: StakingMode.Stake, }, { - label: t(`title.stakingModeNfts.unstake`), + label: t('title.stakingModeNfts.unstake'), value: StakingMode.Unstake, }, ]} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/MembersTab.tsx index bc0adf2ec..fbb86684c 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/MembersTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/MembersTab.tsx @@ -40,9 +40,7 @@ export const MembersTab = () => { balance: { loading: false, data: { - amount: HugeDecimal.from(balance).toHumanReadableNumber( - governanceToken.decimals - ), + amount: HugeDecimal.from(balance), token: governanceToken, }, }, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/ProfileCardMemberInfo.tsx index 9e48ad3f8..439ec79a2 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/ProfileCardMemberInfo.tsx @@ -96,11 +96,14 @@ export const ProfileCardMemberInfo = ({ refreshClaims?.() toast.success( - `Claimed ${HugeDecimal.from(sumClaimsAvailable) - .toHumanReadableNumber(governanceToken.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: governanceToken.decimals, - })} $${governanceToken.symbol}` + t('success.claimedTokens', { + amount: HugeDecimal.from( + sumClaimsAvailable + ).toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + }), + tokenSymbol: governanceToken.symbol, + }) ) } catch (err) { console.error(err) @@ -177,12 +180,8 @@ export const ProfileCardMemberInfo = ({ data: [ { token: governanceToken, - staked: HugeDecimal.from( - loadingWalletStakedValue.data - ).toHumanReadableNumber(governanceToken.decimals), - unstaked: HugeDecimal.from( - loadingUnstakedBalance.data - ).toHumanReadableNumber(governanceToken.decimals), + staked: HugeDecimal.from(loadingWalletStakedValue.data), + unstaked: HugeDecimal.from(loadingUnstakedBalance.data), }, ], } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx index 530db0e3e..d02b05ab5 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx @@ -117,9 +117,12 @@ const InnerStakingModal = ({ setAmount(HugeDecimal.zero) toast.success( - `Staked ${amount.toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - })} $${governanceToken.symbol}` + t('success.stakedTokens', { + amount: amount.toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + }), + tokenSymbol: governanceToken.symbol, + }) ) // Close once done. @@ -151,9 +154,12 @@ const InnerStakingModal = ({ setAmount(HugeDecimal.zero) toast.success( - `Unstaked ${amount.toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - })} $${governanceToken.symbol}` + t('success.unstakedTokens', { + amount: amount.toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + }), + tokenSymbol: governanceToken.symbol, + }) ) // Close once done. @@ -169,7 +175,8 @@ const InnerStakingModal = ({ } case StakingMode.Claim: { if (sumClaimsAvailable === 0) { - return toast.error('No claims available.') + toast.error(t('error.noClaimsAvailable')) + return } setStakingLoading(true) @@ -186,11 +193,14 @@ const InnerStakingModal = ({ setAmount(HugeDecimal.zero) toast.success( - `Claimed ${HugeDecimal.from( - sumClaimsAvailable || 0 - ).toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - })} $${governanceToken.symbol}` + t('success.claimedTokens', { + amount: HugeDecimal.from( + sumClaimsAvailable || 0 + ).toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + }), + tokenSymbol: governanceToken.symbol, + }) ) // Close once done. diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx index 743f6a2b8..d8f38271c 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx @@ -101,9 +101,10 @@ export const ProfileCardMemberInfo = ({ refreshClaims?.() toast.success( - `Claimed ${sumClaimsAvailable.toLocaleString()} $${ - collectionInfo.symbol - }` + t('success.claimedTokens', { + amount: sumClaimsAvailable.toLocaleString(), + tokenSymbol: collectionInfo.symbol, + }) ) } catch (err) { console.error(err) @@ -180,8 +181,8 @@ export const ProfileCardMemberInfo = ({ data: [ { token, - staked: loadingWalletStakedValue.data, - unstaked: loadingUnstakedBalance.data, + staked: HugeDecimal.from(loadingWalletStakedValue.data), + unstaked: HugeDecimal.from(loadingUnstakedBalance.data), }, ], } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/StakingModal.tsx index b89bd5e57..0921ddbc2 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/StakingModal.tsx @@ -166,7 +166,10 @@ const InnerStakingModal = ({ refreshTotals() toast.success( - `Staked ${stakeTokenIds.length} $${collectionInfo.symbol}` + t('success.stakedTokens', { + amount: stakeTokenIds.length, + tokenSymbol: collectionInfo.symbol, + }) ) setStakeTokenIds([]) @@ -210,7 +213,10 @@ const InnerStakingModal = ({ refreshClaims?.() toast.success( - `Unstaked ${unstakeTokenIds.length} $${collectionInfo.symbol}` + t('success.unstakedTokens', { + amount: unstakeTokenIds.length, + tokenSymbol: collectionInfo.symbol, + }) ) setUnstakeTokenIds([]) diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/MembersTab.tsx index f61a06877..28b9ee368 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/MembersTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/MembersTab.tsx @@ -40,9 +40,7 @@ export const MembersTab = () => { balance: { loading: false, data: { - amount: HugeDecimal.from(balance).toHumanReadableNumber( - governanceToken.decimals - ), + amount: HugeDecimal.from(balance), token: governanceToken, }, }, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx index 2da7c75c9..b820a0827 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx @@ -96,11 +96,14 @@ export const ProfileCardMemberInfo = ({ refreshClaims?.() toast.success( - `Claimed ${HugeDecimal.from(sumClaimsAvailable) - .toHumanReadableNumber(governanceToken.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: governanceToken.decimals, - })} $${governanceToken.symbol}` + t('success.claimedTokens', { + amount: HugeDecimal.from( + sumClaimsAvailable + ).toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + }), + tokenSymbol: governanceToken.symbol, + }) ) } catch (err) { console.error(err) @@ -177,12 +180,8 @@ export const ProfileCardMemberInfo = ({ data: [ { token: governanceToken, - staked: HugeDecimal.from( - loadingWalletStakedValue.data - ).toHumanReadableNumber(governanceToken.decimals), - unstaked: HugeDecimal.from( - loadingUnstakedBalance.data - ).toHumanReadableNumber(governanceToken.decimals), + staked: HugeDecimal.from(loadingWalletStakedValue.data), + unstaked: HugeDecimal.from(loadingUnstakedBalance.data), }, ], } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx index 5d87d0113..830798553 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx @@ -117,9 +117,12 @@ const InnerStakingModal = ({ setAmount(HugeDecimal.zero) toast.success( - `Staked ${amount.toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - })} $${governanceToken.symbol}` + t('success.stakedTokens', { + amount: amount.toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + }), + tokenSymbol: governanceToken.symbol, + }) ) // Close once done. @@ -151,9 +154,12 @@ const InnerStakingModal = ({ setAmount(HugeDecimal.zero) toast.success( - `Unstaked ${amount.toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - })} $${governanceToken.symbol}` + t('success.unstakedTokens', { + amount: amount.toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + }), + tokenSymbol: governanceToken.symbol, + }) ) // Close once done. @@ -169,7 +175,8 @@ const InnerStakingModal = ({ } case StakingMode.Claim: { if (sumClaimsAvailable === 0) { - return toast.error('No claims available.') + toast.error(t('error.noClaimsAvailable')) + return } setStakingLoading(true) @@ -186,11 +193,14 @@ const InnerStakingModal = ({ setAmount(HugeDecimal.zero) toast.success( - `Claimed ${HugeDecimal.from( - sumClaimsAvailable || 0 - ).toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - })} $${governanceToken.symbol}` + t('success.claimedTokens', { + amount: HugeDecimal.from( + sumClaimsAvailable || 0 + ).toInternationalizedHumanReadableString({ + decimals: governanceToken.decimals, + }), + tokenSymbol: governanceToken.symbol, + }) ) // Close once done. diff --git a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/ProfileCardMemberInfo.tsx index 7c532441d..d3ebc7cdb 100644 --- a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/ProfileCardMemberInfo.tsx @@ -117,10 +117,10 @@ export const ProfileCardMemberInfo = ({ token: bondToken, staked: HugeDecimal.from( loadingStakedTokens.data[index].unbondable_abount - ).toHumanReadableNumber(bondToken.decimals), + ), unstaked: HugeDecimal.from( loadingUnstakedTokens.data[index].balance - ).toHumanReadableNumber(bondToken.decimals), + ), })), } } diff --git a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx index 8d8f3c4ef..4c19cf0c4 100644 --- a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx @@ -179,9 +179,12 @@ const InnerStakingModal = ({ setAmount(HugeDecimal.zero) toast.success( - `Staked ${amount.toInternationalizedHumanReadableString({ - decimals: selectedVault.bondToken.decimals, - })} $${selectedVault.bondToken.symbol}` + t('success.stakedTokens', { + amount: amount.toInternationalizedHumanReadableString({ + decimals: selectedVault.bondToken.decimals, + }), + tokenSymbol: selectedVault.bondToken.symbol, + }) ) // Close once done. @@ -212,9 +215,12 @@ const InnerStakingModal = ({ setAmount(HugeDecimal.zero) toast.success( - `Unstaked ${amount.toInternationalizedHumanReadableString({ - decimals: selectedVault.bondToken.decimals, - })} $${selectedVault.bondToken.symbol}` + t('success.unstakedTokens', { + amount: amount.toInternationalizedHumanReadableString({ + decimals: selectedVault.bondToken.decimals, + }), + tokenSymbol: selectedVault.bondToken.symbol, + }) ) // Close once done. diff --git a/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.stories.tsx b/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.stories.tsx index d933e55b8..9c70392bc 100644 --- a/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.stories.tsx +++ b/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.stories.tsx @@ -1,5 +1,6 @@ import { ComponentMeta, ComponentStory } from '@storybook/react' +import { HugeDecimal } from '@dao-dao/math' import { makeProps as makeUnstakingLineProps } from '@dao-dao/stateless/components/token/UnstakingLine.stories' import { CHAIN_ID } from '@dao-dao/storybook' import { TokenType, UnstakingTask, UnstakingTaskStatus } from '@dao-dao/types' @@ -48,8 +49,8 @@ export const makeProps = ( decimals: 6, imageUrl: undefined, }, - staked: stakedTokens ?? 50, - unstaked: 45.413, + staked: HugeDecimal.fromHumanReadable(stakedTokens ?? 50, 6), + unstaked: HugeDecimal.fromHumanReadable(45.413, 6), }, ], }, @@ -85,8 +86,8 @@ export const makeCantVoteOnProposalProps = ( decimals: 6, imageUrl: undefined, }, - staked: 0, - unstaked: 45.413, + staked: HugeDecimal.zero, + unstaked: HugeDecimal.fromHumanReadable(45.413, 6), }, ], }, diff --git a/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx b/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx index a85fdf2de..9bd27672c 100644 --- a/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx +++ b/packages/stateful/voting-module-adapter/components/ProfileCardMemberInfoTokens.tsx @@ -31,8 +31,8 @@ export interface ProfileCardMemberInfoTokensProps loadingTokens: LoadingData< { token: GenericToken - staked: number - unstaked: number + staked: HugeDecimal + unstaked: HugeDecimal }[] > hideUnstaking?: boolean @@ -104,10 +104,10 @@ export const ProfileCardMemberInfoTokens = ({ const hasStaked = !loadingTokens.loading && - loadingTokens.data.some(({ staked }) => staked > 0) + loadingTokens.data.some(({ staked }) => staked.isPositive()) const hasUnstaked = !loadingTokens.loading && - loadingTokens.data.some(({ unstaked }) => unstaked > 0) + loadingTokens.data.some(({ unstaked }) => unstaked.isPositive()) const isMember = !loadingVotingPower.loading && loadingVotingPower.data > 0 const canBeMemberButIsnt = !isMember && hasUnstaked @@ -293,11 +293,9 @@ export const ProfileCardMemberInfoTokens = ({ !onlyOneToken ? t('button.claimYourTokens') : t('button.claimNumTokens', { - amount: HugeDecimal.from(claimableBalance) - .toHumanReadableNumber(loadingTokens.data[0].token.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: - loadingTokens.data[0].token.decimals, + amount: + claimableBalance.toInternationalizedHumanReadableString({ + decimals: loadingTokens.data[0].token.decimals, }), tokenSymbol: onlyTokenSymbol, })} diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Complete.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Complete.tsx index 8df82a3fd..4d86bc893 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Complete.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Complete.tsx @@ -456,18 +456,19 @@ export const InnerComplete = ({ .reduce( (acc, { denomOrAddress, amount }) => ({ ...acc, - [denomOrAddress]: - (acc[denomOrAddress] ?? 0) + - HugeDecimal.from(amount).toHumanReadableNumber( - tokenMap[denomOrAddress]?.token.decimals ?? 0 - ), + [denomOrAddress]: ( + acc[denomOrAddress] ?? HugeDecimal.zero + ).plus(amount), }), - {} as Record<string, number> + {} as Record<string, HugeDecimal> ) const totalUsdc = Object.entries(tokens) - .map( - ([denomOrAddress, amount]) => - (tokenMap[denomOrAddress]?.usdPrice ?? 0) * amount + .map(([denomOrAddress, amount]) => + amount + .times(tokenMap[denomOrAddress]?.usdPrice ?? 0) + .toHumanReadableNumber( + tokenMap[denomOrAddress]?.token.decimals ?? 0 + ) ) .reduce((acc, amount) => acc + amount, 0) diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Rate.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Rate.tsx index 1c2306f02..d624bc83f 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Rate.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Rate.tsx @@ -282,18 +282,19 @@ export const Rate = ({ .reduce( (acc, { denomOrAddress, amount }) => ({ ...acc, - [denomOrAddress]: - (acc[denomOrAddress] ?? 0) + - HugeDecimal.from(amount).toHumanReadableNumber( - tokenMap[denomOrAddress]?.token.decimals ?? 0 - ), + [denomOrAddress]: ( + acc[denomOrAddress] ?? HugeDecimal.zero + ).plus(amount), }), - {} as Record<string, number> + {} as Record<string, HugeDecimal> ) const projectedTotalUsdc = Object.entries(projectedTokens) - .map( - ([denomOrAddress, amount]) => - (tokenMap[denomOrAddress]?.usdPrice ?? 0) * amount + .map(([denomOrAddress, amount]) => + amount + .times(tokenMap[denomOrAddress]?.usdPrice ?? 0) + .toHumanReadableNumber( + tokenMap[denomOrAddress]?.token.decimals ?? 0 + ) ) .reduce((acc, amount) => acc + amount, 0) diff --git a/packages/stateless/components/dao/tabs/MembersTab.tsx b/packages/stateless/components/dao/tabs/MembersTab.tsx index 26641a7b5..ebe9347e3 100644 --- a/packages/stateless/components/dao/tabs/MembersTab.tsx +++ b/packages/stateless/components/dao/tabs/MembersTab.tsx @@ -4,6 +4,7 @@ import { ComponentType, Fragment, useRef, useState } from 'react' import { CSVLink } from 'react-csv' import { useTranslation } from 'react-i18next' +import { HugeDecimal } from '@dao-dao/math' import { ButtonLinkProps, LoadingDataWithError, @@ -298,7 +299,13 @@ export const MembersTab = ({ ...members.data.map( ({ address, balance, votingPowerPercent }) => [ address, - balance.loading ? '...' : balance.data.amount.toString(), + balance.loading + ? '...' + : HugeDecimal.from( + balance.data.amount + ).toHumanReadableString( + balance.data.token?.decimals ?? 0 + ), votingPowerPercent.loading ? '...' : votingPowerPercent.data, ] ), diff --git a/packages/stateless/components/token/TokenAmountDisplay.tsx b/packages/stateless/components/token/TokenAmountDisplay.tsx index 035b16cd4..33dbb899b 100644 --- a/packages/stateless/components/token/TokenAmountDisplay.tsx +++ b/packages/stateless/components/token/TokenAmountDisplay.tsx @@ -114,7 +114,7 @@ export const TokenAmountDisplay = ({ // Show full value in tooltip if compacted and not an estimated USD value. const shouldShowFullTooltip = !showFullAmount && - amount.toHumanReadableNumber(decimals) >= 1000 && + amount.toHumanReadable(decimals).gte(1000) && !estimatedUsdValue return ( diff --git a/packages/stateless/components/vesting/VestingPaymentCard.stories.tsx b/packages/stateless/components/vesting/VestingPaymentCard.stories.tsx index 5e81a476f..9da7ccaa6 100644 --- a/packages/stateless/components/vesting/VestingPaymentCard.stories.tsx +++ b/packages/stateless/components/vesting/VestingPaymentCard.stories.tsx @@ -59,9 +59,9 @@ Default.args = { claiming: false, onManageStake: () => alert('manage stake'), onAddToken: () => alert('add token'), - remainingBalanceVesting: 401239.5123, - distributableAmount: 1942.7984, - claimedAmount: 39.234, + remainingBalanceVesting: HugeDecimal.fromHumanReadable(401239.5123, 6), + distributableAmount: HugeDecimal.fromHumanReadable(1942.7984, 6), + claimedAmount: HugeDecimal.fromHumanReadable(39.234, 6), // Started 2 days ago. startDate: new Date(Date.now() - 1000 * 60 * 60 * 24 * 2), // Ends in 7 days. diff --git a/packages/stateless/components/vesting/VestingPaymentCard.tsx b/packages/stateless/components/vesting/VestingPaymentCard.tsx index ffe66d94f..f036f70d1 100644 --- a/packages/stateless/components/vesting/VestingPaymentCard.tsx +++ b/packages/stateless/components/vesting/VestingPaymentCard.tsx @@ -54,9 +54,9 @@ export type VestingPaymentCardProps = { title: string | undefined | null description: string | undefined | null - remainingBalanceVesting: number - distributableAmount: number - claimedAmount: number + remainingBalanceVesting: HugeDecimal + distributableAmount: HugeDecimal + claimedAmount: HugeDecimal startDate: Date endDate: Date steps: VestingStep[] @@ -158,7 +158,7 @@ export const VestingPaymentCard = ({ const canWithdraw = isWalletConnected && (recipientIsWallet || recipientIsDao) && - distributableAmount > 0 + distributableAmount.isPositive() const buttonPopupSections: ButtonPopupSection[] = useMemo( () => [ @@ -427,7 +427,8 @@ export const VestingPaymentCard = ({ <div className="flex flex-col gap-3 border-t border-border-secondary py-4 px-6"> {/* Show available balance to withdraw if it is nonzero OR if there is still a balance vesting. This ensures that it explicitly displays that there is no balance to withdraw when the vest is not yet over. There may not be any balance if all vested tokens are staked or still unstaking, and it might be confusing if this line remains hidden in that case. */} - {(distributableAmount > 0 || remainingBalanceVesting > 0) && ( + {(distributableAmount.isPositive() || + remainingBalanceVesting.isPositive()) && ( <div className="flex flex-row items-start justify-between gap-8"> <p className="link-text">{t('info.availableBalance')}</p> @@ -450,8 +451,9 @@ export const VestingPaymentCard = ({ lazyInfo.loading || !lazyInfo.data.usdUnitPrice?.usdPrice ? { loading: true } - : distributableAmount * - lazyInfo.data.usdUnitPrice.usdPrice + : distributableAmount + .times(lazyInfo.data.usdUnitPrice.usdPrice) + .toHumanReadableNumber(token.decimals) } dateFetched={ lazyInfo.loading || !lazyInfo.data.usdUnitPrice @@ -471,7 +473,7 @@ export const VestingPaymentCard = ({ </div> )} - {remainingBalanceVesting > 0 && ( + {remainingBalanceVesting.isPositive() && ( <div className="flex flex-row items-start justify-between gap-8"> <p className="link-text">{t('info.remainingBalanceVesting')}</p> @@ -494,8 +496,9 @@ export const VestingPaymentCard = ({ lazyInfo.loading || !lazyInfo.data.usdUnitPrice?.usdPrice ? { loading: true } - : remainingBalanceVesting * - lazyInfo.data.usdUnitPrice.usdPrice + : remainingBalanceVesting + .times(lazyInfo.data.usdUnitPrice.usdPrice) + .toHumanReadableNumber(token.decimals) } dateFetched={ lazyInfo.loading || !lazyInfo.data.usdUnitPrice diff --git a/packages/stateless/hooks/useTokenSortOptions.ts b/packages/stateless/hooks/useTokenSortOptions.ts index 08a2d3dc9..ffbf4f912 100644 --- a/packages/stateless/hooks/useTokenSortOptions.ts +++ b/packages/stateless/hooks/useTokenSortOptions.ts @@ -1,6 +1,5 @@ import { useTranslation } from 'react-i18next' -import { HugeDecimal } from '@dao-dao/math' import { SortFn, TokenCardInfo, TypedOption } from '@dao-dao/types' import { sortTokensValueDescending } from '@dao-dao/utils' @@ -25,17 +24,15 @@ export const useTokenSortOptions = (): TypedOption< const aPrice = a.lazyInfo.loading || !a.lazyInfo.data.usdUnitPrice?.usdPrice ? undefined - : HugeDecimal.from( - a.lazyInfo.data.totalBalance - ).toHumanReadableNumber(a.token.decimals) * - a.lazyInfo.data.usdUnitPrice.usdPrice + : a.lazyInfo.data.totalBalance.times( + a.lazyInfo.data.usdUnitPrice.usdPrice + ) const bPrice = b.lazyInfo.loading || !b.lazyInfo.data.usdUnitPrice?.usdPrice ? undefined - : HugeDecimal.from( - b.lazyInfo.data.totalBalance - ).toHumanReadableNumber(b.token.decimals) * - b.lazyInfo.data.usdUnitPrice.usdPrice + : b.lazyInfo.data.totalBalance.times( + b.lazyInfo.data.usdUnitPrice.usdPrice + ) // If prices are equal, sort alphabetically by symbol. return aPrice === bPrice @@ -46,7 +43,11 @@ export const useTokenSortOptions = (): TypedOption< ? 1 : bPrice === undefined ? -1 - : aPrice - bPrice + : aPrice.eq(bPrice) + ? 0 + : aPrice.gt(bPrice) + ? 1 + : -1 }, }, { diff --git a/packages/types/components/DaoMemberCard.ts b/packages/types/components/DaoMemberCard.ts index c4c93ed6e..6e7af1880 100644 --- a/packages/types/components/DaoMemberCard.ts +++ b/packages/types/components/DaoMemberCard.ts @@ -1,5 +1,7 @@ import { ComponentType } from 'react' +import { HugeDecimal } from '@dao-dao/math' + import { LoadingData } from '../misc' import { GenericToken } from '../token' import { ButtonLinkProps } from './Buttonifier' @@ -9,7 +11,7 @@ export type DaoMemberCardProps = { address: string balanceLabel: string balance: LoadingData<{ - amount: number + amount: number | HugeDecimal token?: GenericToken }> votingPowerPercent: LoadingData<number> diff --git a/packages/utils/dao.ts b/packages/utils/dao.ts index 0e01e4ae7..ab8124242 100644 --- a/packages/utils/dao.ts +++ b/packages/utils/dao.ts @@ -199,11 +199,9 @@ export const getHumanReadableRewardDistributionLabel = ( : t('info.amountEveryDuration', { amount: HugeDecimal.from( distribution.active_epoch.emission_rate.linear.amount - ) - .toHumanReadableNumber(distribution.token.decimals) - .toLocaleString(undefined, { - maximumFractionDigits: distribution.token.decimals, - }), + ).toInternationalizedHumanReadableString({ + decimals: distribution.token.decimals, + }), duration: convertDurationToHumanReadableString( t, distribution.active_epoch.emission_rate.linear.duration diff --git a/packages/utils/token.ts b/packages/utils/token.ts index 76707283d..384e7f3f6 100644 --- a/packages/utils/token.ts +++ b/packages/utils/token.ts @@ -1,6 +1,5 @@ import cloneDeep from 'lodash.clonedeep' -import { HugeDecimal } from '@dao-dao/math' import { GenericToken, GenericTokenSource, @@ -133,15 +132,15 @@ export const sortTokensValueDescending: SortFn< const aPrice = a.lazyInfo.loading || !a.lazyInfo.data.usdUnitPrice?.usdPrice ? undefined - : HugeDecimal.from(a.lazyInfo.data.totalBalance) - .times(a.lazyInfo.data.usdUnitPrice.usdPrice) - .toHumanReadableNumber(a.token.decimals) + : a.lazyInfo.data.totalBalance.times( + a.lazyInfo.data.usdUnitPrice.usdPrice + ) const bPrice = b.lazyInfo.loading || !b.lazyInfo.data.usdUnitPrice?.usdPrice ? undefined - : HugeDecimal.from(b.lazyInfo.data.totalBalance) - .times(b.lazyInfo.data.usdUnitPrice.usdPrice) - .toHumanReadableNumber(b.token.decimals) + : b.lazyInfo.data.totalBalance.times( + b.lazyInfo.data.usdUnitPrice.usdPrice + ) // If prices are equal, sort alphabetically by symbol. return aPrice === bPrice @@ -152,5 +151,9 @@ export const sortTokensValueDescending: SortFn< ? 1 : bPrice === undefined ? -1 - : bPrice - aPrice + : aPrice.eq(bPrice) + ? 0 + : aPrice.gt(bPrice) + ? 1 + : -1 } From 83c261ce232dc80a9ddd83fdaa598180cacaeb69 Mon Sep 17 00:00:00 2001 From: Noah Saso <noahsaso@gmail.com> Date: Wed, 2 Oct 2024 21:06:32 -0400 Subject: [PATCH 23/26] replaced a bunch of numbers with HugeDecimals, consolidated DaoVotingTokenStaked and DaoVotingNativeStaked, and added chainId getter to voting and proposal module clients --- packages/math/HugeDecimal.ts | 9 +- .../MultipleChoiceProposalModule.secret.ts | 18 +- .../MultipleChoiceProposalModule.ts | 18 +- .../SingleChoiceProposalModule.secret.ts | 18 +- .../SingleChoiceProposalModule.ts | 22 +- .../stateful/clients/proposal-module/base.ts | 7 + .../voting-module/Cw20StakedVotingModule.ts | 14 +- .../voting-module/Cw4VotingModule.secret.ts | 8 +- .../clients/voting-module/Cw4VotingModule.ts | 8 +- .../voting-module/Cw721StakedVotingModule.ts | 16 +- .../voting-module/FallbackVotingModule.ts | 4 +- .../voting-module/NativeStakedVotingModule.ts | 10 +- .../NeutronVotingRegistryVotingModule.ts | 4 +- .../voting-module/OnftStakedVotingModule.ts | 16 +- .../SgCommunityNftVotingModule.ts | 6 +- .../Snip20StakedVotingModule.secret.ts | 14 +- .../Snip721StakedVotingModule.secret.ts | 16 +- .../TokenStakedVotingModule.secret.ts | 12 +- .../voting-module/TokenStakedVotingModule.ts | 12 +- .../stateful/clients/voting-module/base.ts | 7 + packages/stateful/components/ProposalList.tsx | 2 +- .../actions/UpdatePreProposeConfig/index.tsx | 10 +- .../actions/UpdateProposalConfig/index.tsx | 12 +- .../hooks/useProposalDaoInfoCards.tsx | 6 +- .../hooks/useProposalRefreshers.ts | 2 +- .../adapters/DaoProposalMultiple/index.tsx | 8 +- .../actions/UpdatePreProposeConfig/index.tsx | 10 +- .../actions/UpdateProposalConfigV1/index.tsx | 8 +- .../actions/UpdateProposalConfigV2/index.tsx | 12 +- .../hooks/useProposalDaoInfoCards.tsx | 6 +- .../hooks/useProposalRefreshers.ts | 6 +- .../adapters/DaoProposalSingle/index.tsx | 12 +- .../components/MembersTab.tsx | 6 +- .../components/ProfileCardMemberInfo.tsx | 20 +- .../components/StakingModal.tsx | 34 +-- .../hooks/useGovernanceTokenInfo.ts | 86 +++--- .../hooks/useMainDaoInfoCards.tsx | 8 +- .../hooks/useStakingInfo.ts | 51 ++-- .../adapters/DaoVotingCw20Staked/types.ts | 13 +- .../actions/ManageMembers/index.tsx | 2 +- .../components/MembersTab.tsx | 6 +- .../components/ProfileCardMemberInfo.tsx | 16 +- .../components/StakingModal.tsx | 33 +-- .../hooks/useGovernanceCollectionInfo.ts | 52 ++-- .../hooks/useMainDaoInfoCards.tsx | 10 +- .../hooks/useStakingInfo.ts | 47 ++-- .../adapters/DaoVotingCw721Staked/types.ts | 13 +- .../adapters/DaoVotingNativeStaked/README.md | 19 -- .../UpdateStakingConfig/Component.stories.tsx | 37 --- .../actions/UpdateStakingConfig/Component.tsx | 113 -------- .../actions/UpdateStakingConfig/README.md | 24 -- .../actions/UpdateStakingConfig/index.ts | 112 -------- .../DaoVotingNativeStaked/actions/index.ts | 2 - .../components/MembersTab.tsx | 66 ----- .../components/ProfileCardMemberInfo.tsx | 223 --------------- .../components/StakingModal.tsx | 254 ------------------ .../DaoVotingNativeStaked/components/index.ts | 3 - .../DaoVotingNativeStaked/hooks/index.ts | 3 - .../hooks/useGovernanceTokenInfo.ts | 126 --------- .../hooks/useMainDaoInfoCards.tsx | 92 ------- .../hooks/useStakingInfo.ts | 144 ---------- .../adapters/DaoVotingNativeStaked/index.ts | 61 ----- .../adapters/DaoVotingNativeStaked/types.ts | 82 ------ .../components/MembersTab.tsx | 6 +- .../components/ProfileCardMemberInfo.tsx | 12 +- .../components/StakingModal.tsx | 2 +- .../hooks/useGovernanceCollectionInfo.ts | 32 ++- .../hooks/useMainDaoInfoCards.tsx | 8 +- .../hooks/useStakingInfo.ts | 75 +++--- .../adapters/DaoVotingOnftStaked/types.ts | 13 +- .../components/MembersTab.tsx | 6 +- .../hooks/useMainDaoInfoCards.ts | 6 +- .../adapters/DaoVotingTokenStaked/README.md | 8 +- .../MintComponent.stories.tsx | 2 +- .../MintComponent.tsx | 0 .../DaoVotingNativeStakedMint}/README.md | 3 +- .../DaoVotingNativeStakedMint}/index.tsx | 2 +- .../BitSongFantokenMintAction.ts | 0 .../Component.tsx | 0 .../MintAction.ts | 2 +- .../MintComponent.stories.tsx | 2 +- .../MintComponent.tsx | 0 .../README.md | 2 +- .../index.ts | 0 .../actions/UpdateStakingConfig/README.md | 2 +- .../DaoVotingTokenStaked/actions/index.ts | 3 +- .../components/MembersTab.tsx | 12 +- .../components/ProfileCardMemberInfo.tsx | 32 +-- .../components/StakingModal.tsx | 24 +- .../hooks/useGovernanceTokenInfo.ts | 45 ++-- .../hooks/useMainDaoInfoCards.tsx | 10 +- .../hooks/useStakingInfo.ts | 57 ++-- .../adapters/DaoVotingTokenStaked/index.ts | 18 +- .../adapters/DaoVotingTokenStaked/types.ts | 16 +- .../hooks/useVotingModule.ts | 8 +- .../voting-module-adapter/adapters/index.ts | 1 - .../stateful/voting-module-adapter/core.ts | 8 +- packages/types/clients/proposal-module.ts | 5 + packages/types/clients/voting-module.ts | 5 + packages/types/voting-module-adapter.ts | 2 +- 100 files changed, 587 insertions(+), 1933 deletions(-) delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/README.md delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.stories.tsx delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.tsx delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/README.md delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/index.ts delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/index.ts delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/MembersTab.tsx delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/ProfileCardMemberInfo.tsx delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/index.ts delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/index.ts delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useGovernanceTokenInfo.ts delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useMainDaoInfoCards.tsx delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useStakingInfo.ts delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/index.ts delete mode 100644 packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/types.ts rename packages/stateful/voting-module-adapter/adapters/{DaoVotingNativeStaked/actions/Mint => DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint}/MintComponent.stories.tsx (94%) rename packages/stateful/voting-module-adapter/adapters/{DaoVotingNativeStaked/actions/Mint => DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint}/MintComponent.tsx (100%) rename packages/stateful/voting-module-adapter/adapters/{DaoVotingNativeStaked/actions/Mint => DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint}/README.md (78%) rename packages/stateful/voting-module-adapter/adapters/{DaoVotingNativeStaked/actions/Mint => DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint}/index.tsx (97%) rename packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/{Mint => DaoVotingTokenStakedMint}/BitSongFantokenMintAction.ts (100%) rename packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/{Mint => DaoVotingTokenStakedMint}/Component.tsx (100%) rename packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/{Mint => DaoVotingTokenStakedMint}/MintAction.ts (98%) rename packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/{Mint => DaoVotingTokenStakedMint}/MintComponent.stories.tsx (94%) rename packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/{Mint => DaoVotingTokenStakedMint}/MintComponent.tsx (100%) rename packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/{Mint => DaoVotingTokenStakedMint}/README.md (81%) rename packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/{Mint => DaoVotingTokenStakedMint}/index.ts (100%) diff --git a/packages/math/HugeDecimal.ts b/packages/math/HugeDecimal.ts index c0f913127..62487202c 100644 --- a/packages/math/HugeDecimal.ts +++ b/packages/math/HugeDecimal.ts @@ -262,14 +262,15 @@ export class HugeDecimal { * @returns an internationalized human-readable string */ toInternationalizedHumanReadableString({ - decimals, + decimals = 0, showFullAmount = true, minDecimals = 0, }: { /** - * The number of decimals used to make this number human-readable. + * The number of decimals used to make this number human-readable. Defaults + * to 0. */ - decimals: number + decimals?: number /** * Whether or not to show the full amount. Large numbers will be abbreviated * if this is false. Defaults to true. @@ -280,7 +281,7 @@ export class HugeDecimal { * number of non-zero decimal places less than or equal to `decimals`. */ minDecimals?: number - }): string { + } = {}): string { // Get the decimal separator for the current locale. const decimalSeparator = (1.1).toLocaleString()[1] diff --git a/packages/stateful/clients/proposal-module/MultipleChoiceProposalModule.secret.ts b/packages/stateful/clients/proposal-module/MultipleChoiceProposalModule.secret.ts index e6a73425c..c390430bb 100644 --- a/packages/stateful/clients/proposal-module/MultipleChoiceProposalModule.secret.ts +++ b/packages/stateful/clients/proposal-module/MultipleChoiceProposalModule.secret.ts @@ -176,7 +176,7 @@ export class SecretMultipleChoiceProposalModule extends ProposalModuleBase< proposalNumber = Number( findWasmAttributeValue( - this.dao.chainId, + this.chainId, events, this.address, 'proposal_id' @@ -200,7 +200,7 @@ export class SecretMultipleChoiceProposalModule extends ProposalModuleBase< proposalNumber = Number( findWasmAttributeValue( - this.dao.chainId, + this.chainId, events, this.address, 'proposal_id' @@ -308,7 +308,7 @@ export class SecretMultipleChoiceProposalModule extends ProposalModuleBase< proposalId: number }): FetchQueryOptions<ProposalResponse> { return secretDaoProposalMultipleQueries.proposal({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { proposalId, @@ -334,7 +334,7 @@ export class SecretMultipleChoiceProposalModule extends ProposalModuleBase< // If no voter nor permit, return query in loading state. const permit = voter && this.dao.getExistingPermit(voter) return secretDaoProposalMultipleQueries.getVote({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, // Force type-cast since the query won't be enabled until this is set. // This allows us to pass an undefined `voter` argument in order to @@ -373,21 +373,21 @@ export class SecretMultipleChoiceProposalModule extends ProposalModuleBase< getProposalCountQuery(): FetchQueryOptions<number> { return secretDaoProposalMultipleQueries.proposalCount({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) } getDaoAddressQuery(): FetchQueryOptions<string> { return secretDaoProposalMultipleQueries.dao({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) } getConfigQuery(): FetchQueryOptions<Config> { return secretDaoProposalMultipleQueries.config({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) } @@ -398,7 +398,7 @@ export class SecretMultipleChoiceProposalModule extends ProposalModuleBase< 'secretMultipleChoiceProposalModule', 'depositInfo', { - chainId: this.dao.chainId, + chainId: this.chainId, address: this.address, }, ], @@ -407,7 +407,7 @@ export class SecretMultipleChoiceProposalModule extends ProposalModuleBase< const { deposit_info: depositInfo } = await this.queryClient.fetchQuery( secretDaoPreProposeMultipleQueries.config({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.prePropose.address, }) ) diff --git a/packages/stateful/clients/proposal-module/MultipleChoiceProposalModule.ts b/packages/stateful/clients/proposal-module/MultipleChoiceProposalModule.ts index 22a1745d0..6322cebb6 100644 --- a/packages/stateful/clients/proposal-module/MultipleChoiceProposalModule.ts +++ b/packages/stateful/clients/proposal-module/MultipleChoiceProposalModule.ts @@ -234,7 +234,7 @@ export class MultipleChoiceProposalModule extends ProposalModuleBase< proposalNumber = Number( findWasmAttributeValue( - this.dao.chainId, + this.chainId, events, this.address, 'proposal_id' @@ -258,7 +258,7 @@ export class MultipleChoiceProposalModule extends ProposalModuleBase< proposalNumber = Number( findWasmAttributeValue( - this.dao.chainId, + this.chainId, events, this.address, 'proposal_id' @@ -345,7 +345,7 @@ export class MultipleChoiceProposalModule extends ProposalModuleBase< proposalId: number }): FetchQueryOptions<ProposalResponse> { return daoProposalMultipleQueries.proposal(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { proposalId, @@ -367,7 +367,7 @@ export class MultipleChoiceProposalModule extends ProposalModuleBase< voter?: string }): FetchQueryOptions<VoteResponse> { return daoProposalMultipleQueries.getVote(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { proposalId, @@ -394,21 +394,21 @@ export class MultipleChoiceProposalModule extends ProposalModuleBase< getProposalCountQuery(): FetchQueryOptions<number> { return daoProposalMultipleQueries.proposalCount(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.info.address, }) } getDaoAddressQuery(): FetchQueryOptions<string> { return daoProposalMultipleQueries.dao(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) } getConfigQuery(): FetchQueryOptions<Config> { return daoProposalMultipleQueries.config(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) } @@ -419,7 +419,7 @@ export class MultipleChoiceProposalModule extends ProposalModuleBase< 'multipleChoiceProposalModule', 'depositInfo', { - chainId: this.dao.chainId, + chainId: this.chainId, address: this.address, }, ], @@ -428,7 +428,7 @@ export class MultipleChoiceProposalModule extends ProposalModuleBase< const { deposit_info: depositInfo } = await this.queryClient.fetchQuery( daoPreProposeMultipleQueries.config(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.prePropose.address, }) ) diff --git a/packages/stateful/clients/proposal-module/SingleChoiceProposalModule.secret.ts b/packages/stateful/clients/proposal-module/SingleChoiceProposalModule.secret.ts index 3377c551c..f76f7796f 100644 --- a/packages/stateful/clients/proposal-module/SingleChoiceProposalModule.secret.ts +++ b/packages/stateful/clients/proposal-module/SingleChoiceProposalModule.secret.ts @@ -202,7 +202,7 @@ export class SecretSingleChoiceProposalModule extends ProposalModuleBase< isPreProposeApprovalProposal ? Number( findWasmAttributeValue( - this.dao.chainId, + this.chainId, events, this.prePropose.address, 'id' @@ -210,7 +210,7 @@ export class SecretSingleChoiceProposalModule extends ProposalModuleBase< ) : Number( findWasmAttributeValue( - this.dao.chainId, + this.chainId, events, this.address, 'proposal_id' @@ -234,7 +234,7 @@ export class SecretSingleChoiceProposalModule extends ProposalModuleBase< proposalNumber = Number( findWasmAttributeValue( - this.dao.chainId, + this.chainId, events, this.address, 'proposal_id' @@ -339,7 +339,7 @@ export class SecretSingleChoiceProposalModule extends ProposalModuleBase< proposalId: number }): FetchQueryOptions<ProposalResponse> { return secretDaoProposalSingleQueries.proposal({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { proposalId, @@ -362,7 +362,7 @@ export class SecretSingleChoiceProposalModule extends ProposalModuleBase< }): FetchQueryOptions<VoteResponse> { const permit = voter && this.dao.getExistingPermit(voter) return secretDaoProposalSingleQueries.getVote({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.info.address, // Force type-cast since the query won't be enabled until this is set. // This allows us to pass an undefined `voter` argument in order to @@ -401,14 +401,14 @@ export class SecretSingleChoiceProposalModule extends ProposalModuleBase< getProposalCountQuery(): FetchQueryOptions<number> { return secretDaoProposalSingleQueries.proposalCount({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) } getConfigQuery(): FetchQueryOptions<Config> { return secretDaoProposalSingleQueries.config({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) } @@ -419,7 +419,7 @@ export class SecretSingleChoiceProposalModule extends ProposalModuleBase< 'secretSingleChoiceProposalModule', 'depositInfo', { - chainId: this.dao.chainId, + chainId: this.chainId, address: this.address, }, ], @@ -428,7 +428,7 @@ export class SecretSingleChoiceProposalModule extends ProposalModuleBase< const { deposit_info: depositInfo } = await this.queryClient.fetchQuery( secretDaoPreProposeSingleQueries.config({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.prePropose.address, }) ) diff --git a/packages/stateful/clients/proposal-module/SingleChoiceProposalModule.ts b/packages/stateful/clients/proposal-module/SingleChoiceProposalModule.ts index 382e3e258..326bedc74 100644 --- a/packages/stateful/clients/proposal-module/SingleChoiceProposalModule.ts +++ b/packages/stateful/clients/proposal-module/SingleChoiceProposalModule.ts @@ -212,7 +212,7 @@ export class SingleChoiceProposalModule extends ProposalModuleBase< proposalNumber = Number( findWasmAttributeValue( - this.dao.chainId, + this.chainId, events, this.address, 'proposal_id' @@ -249,7 +249,7 @@ export class SingleChoiceProposalModule extends ProposalModuleBase< isPreProposeApprovalProposal ? Number( findWasmAttributeValue( - this.dao.chainId, + this.chainId, events, this.prePropose.address, 'id' @@ -257,7 +257,7 @@ export class SingleChoiceProposalModule extends ProposalModuleBase< ) : Number( findWasmAttributeValue( - this.dao.chainId, + this.chainId, events, this.address, 'proposal_id' @@ -281,7 +281,7 @@ export class SingleChoiceProposalModule extends ProposalModuleBase< proposalNumber = Number( findWasmAttributeValue( - this.dao.chainId, + this.chainId, events, this.address, 'proposal_id' @@ -388,7 +388,7 @@ export class SingleChoiceProposalModule extends ProposalModuleBase< proposalId: number }): FetchQueryOptions<ProposalResponse> { return daoProposalSingleV2Queries.proposal(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { proposalId, @@ -415,7 +415,7 @@ export class SingleChoiceProposalModule extends ProposalModuleBase< : daoProposalSingleV2Queries.getVote return query(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { proposalId, @@ -447,14 +447,14 @@ export class SingleChoiceProposalModule extends ProposalModuleBase< : daoProposalSingleV2Queries.proposalCount return query(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) } getConfigQuery(): FetchQueryOptions<Config> { return daoProposalSingleV2Queries.config(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) } @@ -465,7 +465,7 @@ export class SingleChoiceProposalModule extends ProposalModuleBase< 'singleChoiceProposalModule', 'depositInfo', { - chainId: this.dao.chainId, + chainId: this.chainId, address: this.address, }, ], @@ -474,7 +474,7 @@ export class SingleChoiceProposalModule extends ProposalModuleBase< const { deposit_info: depositInfo } = await this.queryClient.fetchQuery( daoPreProposeSingleQueries.config(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.prePropose.address, }) ) @@ -488,7 +488,7 @@ export class SingleChoiceProposalModule extends ProposalModuleBase< const { deposit_info: depositInfo } = await this.queryClient.fetchQuery( cwProposalSingleV1Queries.config(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) ) diff --git a/packages/stateful/clients/proposal-module/base.ts b/packages/stateful/clients/proposal-module/base.ts index 022b564f7..2a49c0a7c 100644 --- a/packages/stateful/clients/proposal-module/base.ts +++ b/packages/stateful/clients/proposal-module/base.ts @@ -42,6 +42,13 @@ export abstract class ProposalModuleBase< public readonly info: ProposalModuleInfo ) {} + /** + * Chain ID of the proposal module. + */ + get chainId(): string { + return this.dao.chainId + } + /** * Contract address. */ diff --git a/packages/stateful/clients/voting-module/Cw20StakedVotingModule.ts b/packages/stateful/clients/voting-module/Cw20StakedVotingModule.ts index de7e6bf20..4afda1002 100644 --- a/packages/stateful/clients/voting-module/Cw20StakedVotingModule.ts +++ b/packages/stateful/clients/voting-module/Cw20StakedVotingModule.ts @@ -160,7 +160,7 @@ export class Cw20StakedVotingModule extends VotingModuleBase<CwDao> { } return daoVotingCw20StakedQueries.votingPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { address, @@ -173,7 +173,7 @@ export class Cw20StakedVotingModule extends VotingModuleBase<CwDao> { height?: number ): FetchQueryOptions<TotalPowerAtHeightResponse> { return daoVotingCw20StakedQueries.totalPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { height, @@ -187,21 +187,21 @@ export class Cw20StakedVotingModule extends VotingModuleBase<CwDao> { 'cw20StakedVotingModule', 'governanceToken', { - chainId: this.dao.chainId, + chainId: this.chainId, address: this.address, }, ], queryFn: async () => { const governanceTokenAddress = await this.queryClient.fetchQuery( daoVotingCw20StakedQueries.tokenContract(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) ) const token = await this.queryClient.fetchQuery( tokenQueries.info(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, type: TokenType.Cw20, denomOrAddress: governanceTokenAddress, }) @@ -215,7 +215,7 @@ export class Cw20StakedVotingModule extends VotingModuleBase<CwDao> { async getHookCaller(): Promise<string> { return this.queryClient.fetchQuery( daoVotingCw20StakedQueries.stakingContract(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) ) @@ -225,7 +225,7 @@ export class Cw20StakedVotingModule extends VotingModuleBase<CwDao> { return ( await this.queryClient.fetchQuery( cw20StakeQueries.getHooks(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: await this.getHookCaller(), }) ) diff --git a/packages/stateful/clients/voting-module/Cw4VotingModule.secret.ts b/packages/stateful/clients/voting-module/Cw4VotingModule.secret.ts index 6196e013a..f994a2e72 100644 --- a/packages/stateful/clients/voting-module/Cw4VotingModule.secret.ts +++ b/packages/stateful/clients/voting-module/Cw4VotingModule.secret.ts @@ -94,7 +94,7 @@ export class SecretCw4VotingModule extends VotingModuleBase<SecretCwDao> { } return secretDaoVotingCw4Queries.votingPowerAtHeight({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { auth: { permit }, @@ -130,7 +130,7 @@ export class SecretCw4VotingModule extends VotingModuleBase<SecretCwDao> { height?: number ): FetchQueryOptions<TotalPowerAtHeightResponse> { return secretDaoVotingCw4Queries.totalPowerAtHeight({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { height, @@ -142,7 +142,7 @@ export class SecretCw4VotingModule extends VotingModuleBase<SecretCwDao> { return ( await this.queryClient.fetchQuery( secretDaoVotingCw4Queries.groupContract({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) ) @@ -153,7 +153,7 @@ export class SecretCw4VotingModule extends VotingModuleBase<SecretCwDao> { return ( await this.queryClient.fetchQuery( secretCw4GroupQueries.hooks({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: await this.getHookCaller(), }) ) diff --git a/packages/stateful/clients/voting-module/Cw4VotingModule.ts b/packages/stateful/clients/voting-module/Cw4VotingModule.ts index f4ae2aa92..0687162cc 100644 --- a/packages/stateful/clients/voting-module/Cw4VotingModule.ts +++ b/packages/stateful/clients/voting-module/Cw4VotingModule.ts @@ -80,7 +80,7 @@ export class Cw4VotingModule extends VotingModuleBase<CwDao> { } return daoVotingCw4Queries.votingPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { address, @@ -93,7 +93,7 @@ export class Cw4VotingModule extends VotingModuleBase<CwDao> { height?: number ): FetchQueryOptions<TotalPowerAtHeightResponse> { return daoVotingCw4Queries.totalPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { height, @@ -104,7 +104,7 @@ export class Cw4VotingModule extends VotingModuleBase<CwDao> { async getHookCaller(): Promise<string> { return this.queryClient.fetchQuery( daoVotingCw4Queries.groupContract(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) ) @@ -114,7 +114,7 @@ export class Cw4VotingModule extends VotingModuleBase<CwDao> { return ( await this.queryClient.fetchQuery( cw4GroupQueries.hooks(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: await this.getHookCaller(), }) ) diff --git a/packages/stateful/clients/voting-module/Cw721StakedVotingModule.ts b/packages/stateful/clients/voting-module/Cw721StakedVotingModule.ts index 0321e1c3f..658c214e6 100644 --- a/packages/stateful/clients/voting-module/Cw721StakedVotingModule.ts +++ b/packages/stateful/clients/voting-module/Cw721StakedVotingModule.ts @@ -151,7 +151,7 @@ export class Cw721StakedVotingModule extends VotingModuleBase<CwDao> { } return daoVotingCw721StakedQueries.votingPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { address, @@ -164,7 +164,7 @@ export class Cw721StakedVotingModule extends VotingModuleBase<CwDao> { height?: number ): FetchQueryOptions<TotalPowerAtHeightResponse> { return daoVotingCw721StakedQueries.totalPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { height, @@ -178,7 +178,7 @@ export class Cw721StakedVotingModule extends VotingModuleBase<CwDao> { 'cw721StakedVotingModule', 'governanceToken', { - chainId: this.dao.chainId, + chainId: this.chainId, address: this.address, }, ], @@ -186,26 +186,26 @@ export class Cw721StakedVotingModule extends VotingModuleBase<CwDao> { const { nft_address: collectionAddress } = await this.queryClient.fetchQuery( daoVotingCw721StakedQueries.config(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) ) const contractInfo = await this.queryClient.fetchQuery( cw721BaseQueries.contractInfo({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: collectionAddress, }) ) return { - chainId: this.dao.chainId, + chainId: this.chainId, type: TokenType.Cw721, denomOrAddress: collectionAddress, symbol: contractInfo.symbol, decimals: 0, source: { - chainId: this.dao.chainId, + chainId: this.chainId, type: TokenType.Cw721, denomOrAddress: collectionAddress, }, @@ -222,7 +222,7 @@ export class Cw721StakedVotingModule extends VotingModuleBase<CwDao> { return ( await this.queryClient.fetchQuery( daoVotingCw721StakedQueries.hooks(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.getHookCaller(), }) ) diff --git a/packages/stateful/clients/voting-module/FallbackVotingModule.ts b/packages/stateful/clients/voting-module/FallbackVotingModule.ts index 3752b1bea..dab29f0e2 100644 --- a/packages/stateful/clients/voting-module/FallbackVotingModule.ts +++ b/packages/stateful/clients/voting-module/FallbackVotingModule.ts @@ -34,7 +34,7 @@ export class FallbackVotingModule extends VotingModuleBase<CwDao> { } return daoDaoCoreQueries.votingPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.dao.coreAddress, args: { address, @@ -47,7 +47,7 @@ export class FallbackVotingModule extends VotingModuleBase<CwDao> { height?: number ): FetchQueryOptions<TotalPowerAtHeightResponse> { return daoDaoCoreQueries.totalPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.dao.coreAddress, args: { height, diff --git a/packages/stateful/clients/voting-module/NativeStakedVotingModule.ts b/packages/stateful/clients/voting-module/NativeStakedVotingModule.ts index ea2281458..064ad9ae4 100644 --- a/packages/stateful/clients/voting-module/NativeStakedVotingModule.ts +++ b/packages/stateful/clients/voting-module/NativeStakedVotingModule.ts @@ -41,7 +41,7 @@ export class NativeStakedVotingModule extends VotingModuleBase<CwDao> { } return daoVotingNativeStakedQueries.votingPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { address, @@ -54,7 +54,7 @@ export class NativeStakedVotingModule extends VotingModuleBase<CwDao> { height?: number ): FetchQueryOptions<TotalPowerAtHeightResponse> { return daoVotingNativeStakedQueries.totalPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { height, @@ -68,21 +68,21 @@ export class NativeStakedVotingModule extends VotingModuleBase<CwDao> { 'nativeStakedVotingModule', 'governanceToken', { - chainId: this.dao.chainId, + chainId: this.chainId, address: this.address, }, ], queryFn: async () => { const { denom } = await this.queryClient.fetchQuery( daoVotingNativeStakedQueries.getConfig(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) ) const token = await this.queryClient.fetchQuery( tokenQueries.info(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, type: TokenType.Native, denomOrAddress: denom, }) diff --git a/packages/stateful/clients/voting-module/NeutronVotingRegistryVotingModule.ts b/packages/stateful/clients/voting-module/NeutronVotingRegistryVotingModule.ts index 525fa819b..7a144e003 100644 --- a/packages/stateful/clients/voting-module/NeutronVotingRegistryVotingModule.ts +++ b/packages/stateful/clients/voting-module/NeutronVotingRegistryVotingModule.ts @@ -36,7 +36,7 @@ export class NeutronVotingRegistryVotingModule extends VotingModuleBase<CwDao> { } return neutronVotingRegistryQueries.votingPowerAtHeight({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { address, @@ -49,7 +49,7 @@ export class NeutronVotingRegistryVotingModule extends VotingModuleBase<CwDao> { height?: number ): FetchQueryOptions<TotalPowerAtHeightResponse> { return neutronVotingRegistryQueries.totalPowerAtHeight({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { height, diff --git a/packages/stateful/clients/voting-module/OnftStakedVotingModule.ts b/packages/stateful/clients/voting-module/OnftStakedVotingModule.ts index 59bf89d85..b9bfdd5ec 100644 --- a/packages/stateful/clients/voting-module/OnftStakedVotingModule.ts +++ b/packages/stateful/clients/voting-module/OnftStakedVotingModule.ts @@ -79,7 +79,7 @@ export class OnftStakedVotingModule extends VotingModuleBase<CwDao> { } return daoVotingOnftStakedQueries.votingPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { address, @@ -92,7 +92,7 @@ export class OnftStakedVotingModule extends VotingModuleBase<CwDao> { height?: number ): FetchQueryOptions<TotalPowerAtHeightResponse> { return daoVotingOnftStakedQueries.totalPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { height, @@ -106,34 +106,34 @@ export class OnftStakedVotingModule extends VotingModuleBase<CwDao> { 'onftStakedVotingModule', 'governanceToken', { - chainId: this.dao.chainId, + chainId: this.chainId, address: this.address, }, ], queryFn: async () => { const { onft_collection_id } = await this.queryClient.fetchQuery( daoVotingOnftStakedQueries.config(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) ) const { symbol, previewUri } = await this.queryClient.fetchQuery( omniflixQueries.onftCollectionInfo({ - chainId: this.dao.chainId, + chainId: this.chainId, id: onft_collection_id, }) ) return { - chainId: this.dao.chainId, + chainId: this.chainId, type: TokenType.Onft, denomOrAddress: onft_collection_id, symbol, decimals: 0, imageUrl: previewUri, source: { - chainId: this.dao.chainId, + chainId: this.chainId, type: TokenType.Onft, denomOrAddress: onft_collection_id, }, @@ -150,7 +150,7 @@ export class OnftStakedVotingModule extends VotingModuleBase<CwDao> { return ( await this.queryClient.fetchQuery( daoVotingOnftStakedQueries.hooks(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.getHookCaller(), }) ) diff --git a/packages/stateful/clients/voting-module/SgCommunityNftVotingModule.ts b/packages/stateful/clients/voting-module/SgCommunityNftVotingModule.ts index 682ee3588..21397e568 100644 --- a/packages/stateful/clients/voting-module/SgCommunityNftVotingModule.ts +++ b/packages/stateful/clients/voting-module/SgCommunityNftVotingModule.ts @@ -38,7 +38,7 @@ export class SgCommunityNftVotingModule extends VotingModuleBase<CwDao> { return daoVotingSgCommunityNftQueries.votingPowerAtHeight( this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { address, @@ -52,7 +52,7 @@ export class SgCommunityNftVotingModule extends VotingModuleBase<CwDao> { height?: number ): FetchQueryOptions<TotalPowerAtHeightResponse> { return daoVotingSgCommunityNftQueries.totalPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { height, @@ -68,7 +68,7 @@ export class SgCommunityNftVotingModule extends VotingModuleBase<CwDao> { return ( await this.queryClient.fetchQuery( daoVotingSgCommunityNftQueries.hooks(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.getHookCaller(), }) ) diff --git a/packages/stateful/clients/voting-module/Snip20StakedVotingModule.secret.ts b/packages/stateful/clients/voting-module/Snip20StakedVotingModule.secret.ts index 660ca8411..cf83d37ab 100644 --- a/packages/stateful/clients/voting-module/Snip20StakedVotingModule.secret.ts +++ b/packages/stateful/clients/voting-module/Snip20StakedVotingModule.secret.ts @@ -174,7 +174,7 @@ export class SecretSnip20StakedVotingModule extends VotingModuleBase<SecretCwDao } return secretDaoVotingSnip20StakedQueries.votingPowerAtHeight({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { auth: { permit }, @@ -210,7 +210,7 @@ export class SecretSnip20StakedVotingModule extends VotingModuleBase<SecretCwDao height?: number ): FetchQueryOptions<TotalPowerAtHeightResponse> { return secretDaoVotingSnip20StakedQueries.totalPowerAtHeight({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { height, @@ -224,7 +224,7 @@ export class SecretSnip20StakedVotingModule extends VotingModuleBase<SecretCwDao 'snip20StakedVotingModule', 'governanceToken', { - chainId: this.dao.chainId, + chainId: this.chainId, address: this.address, }, ], @@ -232,14 +232,14 @@ export class SecretSnip20StakedVotingModule extends VotingModuleBase<SecretCwDao const { addr: governanceTokenAddress } = await this.queryClient.fetchQuery( secretDaoVotingSnip20StakedQueries.tokenContract({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) ) const token = await this.queryClient.fetchQuery( tokenQueries.info(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, type: TokenType.Cw20, denomOrAddress: governanceTokenAddress, }) @@ -254,7 +254,7 @@ export class SecretSnip20StakedVotingModule extends VotingModuleBase<SecretCwDao return ( await this.queryClient.fetchQuery( secretDaoVotingSnip20StakedQueries.stakingContract({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) ) @@ -265,7 +265,7 @@ export class SecretSnip20StakedVotingModule extends VotingModuleBase<SecretCwDao return ( await this.queryClient.fetchQuery( snip20StakeQueries.getHooks({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: await this.getHookCaller(), }) ) diff --git a/packages/stateful/clients/voting-module/Snip721StakedVotingModule.secret.ts b/packages/stateful/clients/voting-module/Snip721StakedVotingModule.secret.ts index cd7721bc1..1baac459a 100644 --- a/packages/stateful/clients/voting-module/Snip721StakedVotingModule.secret.ts +++ b/packages/stateful/clients/voting-module/Snip721StakedVotingModule.secret.ts @@ -88,7 +88,7 @@ export class SecretSnip721StakedVotingModule extends VotingModuleBase<SecretCwDa } return secretDaoVotingSnip721StakedQueries.votingPowerAtHeight({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { auth: { permit }, @@ -124,7 +124,7 @@ export class SecretSnip721StakedVotingModule extends VotingModuleBase<SecretCwDa height?: number ): FetchQueryOptions<TotalPowerAtHeightResponse> { return secretDaoVotingSnip721StakedQueries.totalPowerAtHeight({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { height, @@ -138,7 +138,7 @@ export class SecretSnip721StakedVotingModule extends VotingModuleBase<SecretCwDa 'snip721StakedVotingModule', 'governanceToken', { - chainId: this.dao.chainId, + chainId: this.chainId, address: this.address, }, ], @@ -146,26 +146,26 @@ export class SecretSnip721StakedVotingModule extends VotingModuleBase<SecretCwDa const { nft_address: collectionAddress } = await this.queryClient.fetchQuery( secretDaoVotingSnip721StakedQueries.config({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) ) const contractInfo = await this.queryClient.fetchQuery( cw721BaseQueries.contractInfo({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: collectionAddress, }) ) return { - chainId: this.dao.chainId, + chainId: this.chainId, type: TokenType.Cw721, denomOrAddress: collectionAddress, symbol: contractInfo.symbol, decimals: 0, source: { - chainId: this.dao.chainId, + chainId: this.chainId, type: TokenType.Cw721, denomOrAddress: collectionAddress, }, @@ -182,7 +182,7 @@ export class SecretSnip721StakedVotingModule extends VotingModuleBase<SecretCwDa return ( await this.queryClient.fetchQuery( secretDaoVotingSnip721StakedQueries.hooks({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.getHookCaller(), }) ) diff --git a/packages/stateful/clients/voting-module/TokenStakedVotingModule.secret.ts b/packages/stateful/clients/voting-module/TokenStakedVotingModule.secret.ts index 17d3d8c9d..3de76a1fe 100644 --- a/packages/stateful/clients/voting-module/TokenStakedVotingModule.secret.ts +++ b/packages/stateful/clients/voting-module/TokenStakedVotingModule.secret.ts @@ -87,7 +87,7 @@ export class SecretTokenStakedVotingModule extends VotingModuleBase<SecretCwDao> } return secretDaoVotingTokenStakedQueries.votingPowerAtHeight({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { auth: { permit }, @@ -123,7 +123,7 @@ export class SecretTokenStakedVotingModule extends VotingModuleBase<SecretCwDao> height?: number ): FetchQueryOptions<TotalPowerAtHeightResponse> { return secretDaoVotingTokenStakedQueries.totalPowerAtHeight({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { height, @@ -137,21 +137,21 @@ export class SecretTokenStakedVotingModule extends VotingModuleBase<SecretCwDao> 'secretTokenStakedVotingModule', 'governanceToken', { - chainId: this.dao.chainId, + chainId: this.chainId, address: this.address, }, ], queryFn: async () => { const { denom } = await this.queryClient.fetchQuery( secretDaoVotingTokenStakedQueries.denom({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) ) const token = await this.queryClient.fetchQuery( tokenQueries.info(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, type: TokenType.Native, denomOrAddress: denom, }) @@ -170,7 +170,7 @@ export class SecretTokenStakedVotingModule extends VotingModuleBase<SecretCwDao> return ( await this.queryClient.fetchQuery( secretDaoVotingTokenStakedQueries.getHooks({ - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.getHookCaller(), }) ) diff --git a/packages/stateful/clients/voting-module/TokenStakedVotingModule.ts b/packages/stateful/clients/voting-module/TokenStakedVotingModule.ts index c03d839b6..d5d59ec80 100644 --- a/packages/stateful/clients/voting-module/TokenStakedVotingModule.ts +++ b/packages/stateful/clients/voting-module/TokenStakedVotingModule.ts @@ -144,7 +144,7 @@ export class TokenStakedVotingModule extends VotingModuleBase<CwDao> { } return daoVotingTokenStakedQueries.votingPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { address, @@ -157,7 +157,7 @@ export class TokenStakedVotingModule extends VotingModuleBase<CwDao> { height?: number ): FetchQueryOptions<TotalPowerAtHeightResponse> { return daoVotingTokenStakedQueries.totalPowerAtHeight(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, args: { height, @@ -171,21 +171,21 @@ export class TokenStakedVotingModule extends VotingModuleBase<CwDao> { 'tokenStakedVotingModule', 'governanceToken', { - chainId: this.dao.chainId, + chainId: this.chainId, address: this.address, }, ], queryFn: async () => { const { denom } = await this.queryClient.fetchQuery( daoVotingTokenStakedQueries.denom(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.address, }) ) const token = await this.queryClient.fetchQuery( tokenQueries.info(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, type: TokenType.Native, denomOrAddress: denom, }) @@ -204,7 +204,7 @@ export class TokenStakedVotingModule extends VotingModuleBase<CwDao> { return ( await this.queryClient.fetchQuery( daoVotingTokenStakedQueries.getHooks(this.queryClient, { - chainId: this.dao.chainId, + chainId: this.chainId, contractAddress: this.getHookCaller(), }) ) diff --git a/packages/stateful/clients/voting-module/base.ts b/packages/stateful/clients/voting-module/base.ts index c0328fdc1..82fb114fc 100644 --- a/packages/stateful/clients/voting-module/base.ts +++ b/packages/stateful/clients/voting-module/base.ts @@ -42,6 +42,13 @@ export abstract class VotingModuleBase<Dao extends IDaoBase = IDaoBase> return this.info.contract } + /** + * Chain ID of the voting module. + */ + get chainId(): string { + return this.dao.chainId + } + /** * Query options to fetch the voting power for a given address. Optionally * specify a block height. If undefined, the latest block height will be used. diff --git a/packages/stateful/components/ProposalList.tsx b/packages/stateful/components/ProposalList.tsx index 319c71d3f..0ef37542b 100644 --- a/packages/stateful/components/ProposalList.tsx +++ b/packages/stateful/components/ProposalList.tsx @@ -294,7 +294,7 @@ export const ProposalList = ({ newProposalInfos = newProposalInfos.filter( (info) => !( - info.proposalModule.dao.chainId === ChainId.NeutronMainnet && + info.proposalModule.chainId === ChainId.NeutronMainnet && info.proposalModule.dao.coreAddress === NEUTRON_GOVERNANCE_DAO && (info.id === 'A47' || info.id === 'A48') diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/index.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/index.tsx index 2b20b0ab8..15756973c 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/index.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdatePreProposeConfig/index.tsx @@ -155,7 +155,7 @@ export class DaoProposalMultipleUpdatePreProposeConfigAction extends ActionBase< const config = await this.options.queryClient.fetchQuery( daoPreProposeMultipleQueries.config(this.options.queryClient, { - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, contractAddress: this.prePropose.address, }) ) @@ -165,7 +165,7 @@ export class DaoProposalMultipleUpdatePreProposeConfigAction extends ActionBase< const token = config.deposit_info ? await this.options.queryClient.fetchQuery( tokenQueries.info(this.options.queryClient, { - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, type: 'native' in config.deposit_info.denom ? TokenType.Native @@ -205,7 +205,7 @@ export class DaoProposalMultipleUpdatePreProposeConfigAction extends ActionBase< amount: '1', type: 'native', denomOrAddress: getNativeTokenForChainId( - this.proposalModule.dao.chainId + this.proposalModule.chainId ).denomOrAddress, token: undefined, refundPolicy: DepositRefundPolicy.OnlyPassed, @@ -299,7 +299,7 @@ export class DaoProposalMultipleUpdatePreProposeConfigAction extends ActionBase< } return makeExecuteSmartContractMessage({ - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, contractAddress: this.prePropose.address, sender: this.options.address, msg: updateConfigMessage, @@ -324,7 +324,7 @@ export class DaoProposalMultipleUpdatePreProposeConfigAction extends ActionBase< }, }, }) && - chainId === this.proposalModule.dao.chainId && + chainId === this.proposalModule.chainId && (await this.options.queryClient.fetchQuery( contractQueries.isContract(this.options.queryClient, { chainId, diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdateProposalConfig/index.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdateProposalConfig/index.tsx index 26f15a757..d054458bb 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdateProposalConfig/index.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/actions/UpdateProposalConfig/index.tsx @@ -180,7 +180,7 @@ export class DaoProposalMultipleUpdateConfigAction extends ActionBase<UpdateProp async setup() { const config = await this.options.queryClient.fetchQuery( daoProposalMultipleQueries.config(this.options.queryClient, { - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, contractAddress: this.proposalModule.address, }) ) @@ -193,7 +193,7 @@ export class DaoProposalMultipleUpdateConfigAction extends ActionBase<UpdateProp cw1WhitelistExtraQueries.adminsIfCw1Whitelist( this.options.queryClient, { - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, address: config.veto.vetoer, } ) @@ -214,7 +214,7 @@ export class DaoProposalMultipleUpdateConfigAction extends ActionBase<UpdateProp async encode(data: UpdateProposalConfigData): Promise<UnifiedCosmosMsg> { const config = await this.options.queryClient.fetchQuery( daoProposalMultipleQueries.config(this.options.queryClient, { - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, contractAddress: this.proposalModule.address, }) ) @@ -251,7 +251,7 @@ export class DaoProposalMultipleUpdateConfigAction extends ActionBase<UpdateProp } return makeExecuteSmartContractMessage({ - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, contractAddress: this.proposalModule.address, sender: this.options.address, msg: updateConfigMessage, @@ -288,7 +288,7 @@ export class DaoProposalMultipleUpdateConfigAction extends ActionBase<UpdateProp }, }, }) && - chainId === this.proposalModule.dao.chainId && + chainId === this.proposalModule.chainId && decodedMessage.wasm.execute.contract_addr === this.proposalModule.address ) } @@ -306,7 +306,7 @@ export class DaoProposalMultipleUpdateConfigAction extends ActionBase<UpdateProp cw1WhitelistExtraQueries.adminsIfCw1Whitelist( this.options.queryClient, { - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, address: config.veto.vetoer, } ) diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/hooks/useProposalDaoInfoCards.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/hooks/useProposalDaoInfoCards.tsx index ab83f366d..6961fa07f 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/hooks/useProposalDaoInfoCards.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/hooks/useProposalDaoInfoCards.tsx @@ -35,7 +35,7 @@ export const useProposalDaoInfoCards = (): DaoInfoCard[] => { const config = useCachedLoadingWithError( DaoProposalMultipleSelectors.configSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, contractAddress: proposalModule.address, }) ) @@ -45,7 +45,7 @@ export const useProposalDaoInfoCards = (): DaoInfoCard[] => { ? undefined : !depositInfo.errored && depositInfo.data ? genericTokenSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, type: 'native' in depositInfo.data.denom ? TokenType.Native @@ -73,7 +73,7 @@ export const useProposalDaoInfoCards = (): DaoInfoCard[] => { : config.errored || !('veto' in config.data) || !config.data.veto ? constSelector(undefined) : Cw1WhitelistSelectors.adminsIfCw1Whitelist({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, contractAddress: config.data.veto.vetoer, }) ) diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/hooks/useProposalRefreshers.ts b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/hooks/useProposalRefreshers.ts index 0fc01e69e..93aac5d20 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/hooks/useProposalRefreshers.ts +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/hooks/useProposalRefreshers.ts @@ -33,7 +33,7 @@ export const useProposalRefreshers = (): ProposalRefreshers => { // Invalidate indexer query first. queryClient.invalidateQueries({ queryKey: indexerQueries.queryContract(queryClient, { - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, contractAddress: proposalModule.address, formula: 'daoProposalMultiple/vote', args: { diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/index.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/index.tsx index 4e62385bb..1118a5302 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/index.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/index.tsx @@ -52,7 +52,7 @@ export const DaoProposalMultipleAdapter: ProposalModuleAdapter< loadCommon: ({ proposalModule }) => { // Make here so we can pass into common hooks and components that need it. const depositInfoSelector = makeDepositInfoSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, proposalModuleAddress: proposalModule.address, version: proposalModule.version, preProposeAddress: proposalModule.prePropose?.address ?? null, @@ -84,19 +84,19 @@ export const DaoProposalMultipleAdapter: ProposalModuleAdapter< // Selectors selectors: { proposalCount: proposalCountSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, proposalModuleAddress: proposalModule.address, }), reverseProposalInfos: (props) => reverseProposalInfosSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, proposalModuleAddress: proposalModule.address, proposalModulePrefix: proposalModule.prefix, ...props, }), depositInfo: depositInfoSelector, maxVotingPeriod: maxVotingPeriodSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, proposalModuleAddress: proposalModule.address, }), }, diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/index.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/index.tsx index f1db3b788..b5cbbd0d6 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/index.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdatePreProposeConfig/index.tsx @@ -164,7 +164,7 @@ export class DaoProposalSingleUpdatePreProposeConfigAction extends ActionBase<Up const config = await this.options.queryClient.fetchQuery( daoPreProposeSingleQueries.config(this.options.queryClient, { - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, contractAddress: this.prePropose.address, }) ) @@ -174,7 +174,7 @@ export class DaoProposalSingleUpdatePreProposeConfigAction extends ActionBase<Up const token = config.deposit_info ? await this.options.queryClient.fetchQuery( tokenQueries.info(this.options.queryClient, { - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, type: 'native' in config.deposit_info.denom ? TokenType.Native @@ -214,7 +214,7 @@ export class DaoProposalSingleUpdatePreProposeConfigAction extends ActionBase<Up amount: '1', type: 'native', denomOrAddress: getNativeTokenForChainId( - this.proposalModule.dao.chainId + this.proposalModule.chainId ).denomOrAddress, token: undefined, refundPolicy: DepositRefundPolicy.OnlyPassed, @@ -308,7 +308,7 @@ export class DaoProposalSingleUpdatePreProposeConfigAction extends ActionBase<Up } return makeExecuteSmartContractMessage({ - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, contractAddress: this.prePropose.address, sender: this.options.address, msg: updateConfigMessage, @@ -333,7 +333,7 @@ export class DaoProposalSingleUpdatePreProposeConfigAction extends ActionBase<Up }, }, }) && - chainId === this.proposalModule.dao.chainId && + chainId === this.proposalModule.chainId && (await this.options.queryClient.fetchQuery( contractQueries.isContract(this.options.queryClient, { chainId, diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/index.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/index.tsx index f4b8be277..979362ac4 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/index.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV1/index.tsx @@ -121,7 +121,7 @@ export class DaoProposalSingleV1UpdateConfigAction extends ActionBase<UpdateProp async setup() { const config = await this.options.queryClient.fetchQuery( cwProposalSingleV1Queries.config(this.options.queryClient, { - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, contractAddress: this.proposalModule.address, }) ) @@ -129,7 +129,7 @@ export class DaoProposalSingleV1UpdateConfigAction extends ActionBase<UpdateProp const token = config.deposit_info ? await this.options.queryClient.fetchQuery( tokenQueries.info(this.options.queryClient, { - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, type: TokenType.Cw20, denomOrAddress: config.deposit_info.token, }) @@ -168,7 +168,7 @@ export class DaoProposalSingleV1UpdateConfigAction extends ActionBase<UpdateProp async encode(data: UpdateProposalConfigData): Promise<UnifiedCosmosMsg> { return makeExecuteSmartContractMessage({ - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, contractAddress: this.proposalModule.address, sender: this.options.address, msg: { @@ -246,7 +246,7 @@ export class DaoProposalSingleV1UpdateConfigAction extends ActionBase<UpdateProp }, }, }) && - chainId === this.proposalModule.dao.chainId && + chainId === this.proposalModule.chainId && decodedMessage.wasm.execute.contract_addr === this.proposalModule.address ) } diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV2/index.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV2/index.tsx index 1fa03982b..0362d2b0b 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV2/index.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/actions/UpdateProposalConfigV2/index.tsx @@ -214,7 +214,7 @@ export class DaoProposalSingleV2UpdateConfigAction extends ActionBase<UpdateProp async setup() { const config = await this.options.queryClient.fetchQuery( daoProposalSingleV2Queries.config(this.options.queryClient, { - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, contractAddress: this.proposalModule.address, }) ) @@ -227,7 +227,7 @@ export class DaoProposalSingleV2UpdateConfigAction extends ActionBase<UpdateProp cw1WhitelistExtraQueries.adminsIfCw1Whitelist( this.options.queryClient, { - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, address: config.veto.vetoer, } ) @@ -248,7 +248,7 @@ export class DaoProposalSingleV2UpdateConfigAction extends ActionBase<UpdateProp async encode(data: UpdateProposalConfigData): Promise<UnifiedCosmosMsg> { const config = await this.options.queryClient.fetchQuery( daoProposalSingleV2Queries.config(this.options.queryClient, { - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, contractAddress: this.proposalModule.address, }) ) @@ -298,7 +298,7 @@ export class DaoProposalSingleV2UpdateConfigAction extends ActionBase<UpdateProp } return makeExecuteSmartContractMessage({ - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, contractAddress: this.proposalModule.address, sender: this.options.address, msg: updateConfigMessage, @@ -329,7 +329,7 @@ export class DaoProposalSingleV2UpdateConfigAction extends ActionBase<UpdateProp }, }, }) && - chainId === this.proposalModule.dao.chainId && + chainId === this.proposalModule.chainId && decodedMessage.wasm.execute.contract_addr === this.proposalModule.address ) } @@ -347,7 +347,7 @@ export class DaoProposalSingleV2UpdateConfigAction extends ActionBase<UpdateProp cw1WhitelistExtraQueries.adminsIfCw1Whitelist( this.options.queryClient, { - chainId: this.proposalModule.dao.chainId, + chainId: this.proposalModule.chainId, address: config.veto.vetoer, } ) diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/hooks/useProposalDaoInfoCards.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/hooks/useProposalDaoInfoCards.tsx index 756a36346..8645d79dc 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/hooks/useProposalDaoInfoCards.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/hooks/useProposalDaoInfoCards.tsx @@ -35,7 +35,7 @@ export const useProposalDaoInfoCards = (): DaoInfoCard[] => { const config = useCachedLoadingWithError( DaoProposalSingleCommonSelectors.configSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, contractAddress: proposalModule.address, }) ) @@ -45,7 +45,7 @@ export const useProposalDaoInfoCards = (): DaoInfoCard[] => { ? undefined : !depositInfo.errored && depositInfo.data ? genericTokenSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, type: 'native' in depositInfo.data.denom ? TokenType.Native @@ -73,7 +73,7 @@ export const useProposalDaoInfoCards = (): DaoInfoCard[] => { : config.errored || !('veto' in config.data) || !config.data.veto ? constSelector(undefined) : Cw1WhitelistSelectors.adminsIfCw1Whitelist({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, contractAddress: config.data.veto.vetoer, }) ) diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/hooks/useProposalRefreshers.ts b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/hooks/useProposalRefreshers.ts index 23d70233c..8247ae9d0 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/hooks/useProposalRefreshers.ts +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/hooks/useProposalRefreshers.ts @@ -38,7 +38,7 @@ export const useProposalRefreshers = (): ProposalRefreshers => { // Invalidate indexer query first. queryClient.invalidateQueries({ queryKey: indexerQueries.queryContract(queryClient, { - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, contractAddress: proposalModule.address, formula: 'daoProposalSingle/vote', args: { @@ -65,7 +65,7 @@ export const useProposalRefreshers = (): ProposalRefreshers => { !isPreProposeApprovalProposal ? DaoProposalSingleCommonSelectors.proposalSelector({ contractAddress: proposalModule.address, - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, params: [ { proposalId: proposalNumber, @@ -79,7 +79,7 @@ export const useProposalRefreshers = (): ProposalRefreshers => { const loadingPreProposeApprovalProposal = useCachedLoading( isPreProposeApprovalProposal && proposalModule.prePropose ? DaoPreProposeApprovalSingleSelectors.queryExtensionSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, contractAddress: proposalModule.prePropose.address, params: [ { diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/index.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/index.tsx index 7501c680f..0e6be0de0 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/index.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/index.tsx @@ -63,7 +63,7 @@ export const DaoProposalSingleAdapter: ProposalModuleAdapter< loadCommon: ({ proposalModule }) => { // Make here so we can pass into common hooks and components that need it. const depositInfoSelector = makeDepositInfoSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, proposalModuleAddress: proposalModule.address, version: proposalModule.version, preProposeAddress: proposalModule.prePropose?.address ?? null, @@ -97,12 +97,12 @@ export const DaoProposalSingleAdapter: ProposalModuleAdapter< // Selectors selectors: { proposalCount: proposalCountSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, proposalModuleAddress: proposalModule.address, }), reverseProposalInfos: (props) => reverseProposalInfosSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, proposalModuleAddress: proposalModule.address, proposalModulePrefix: proposalModule.prefix, ...props, @@ -112,14 +112,14 @@ export const DaoProposalSingleAdapter: ProposalModuleAdapter< ? { reversePreProposePendingProposalInfos: (props) => reversePreProposePendingProposalInfosSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, proposalModuleAddress: proposalModule.prePropose!.address, proposalModulePrefix: proposalModule.prefix, ...props, }), reversePreProposeCompletedProposalInfos: (props) => reversePreProposeCompletedProposalInfosSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, proposalModuleAddress: proposalModule.prePropose!.address, proposalModulePrefix: proposalModule.prefix, ...props, @@ -127,7 +127,7 @@ export const DaoProposalSingleAdapter: ProposalModuleAdapter< } : {}), maxVotingPeriod: maxVotingPeriodSelector({ - chainId: proposalModule.dao.chainId, + chainId: proposalModule.chainId, proposalModuleAddress: proposalModule.address, }), }, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/MembersTab.tsx index 0dd0dd4d1..45ef66a56 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/MembersTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/MembersTab.tsx @@ -17,14 +17,14 @@ import { useGovernanceTokenInfo } from '../hooks/useGovernanceTokenInfo' export const MembersTab = () => { const { t } = useTranslation() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() + const { votingModule } = useVotingModuleAdapterOptions() const { governanceToken } = useGovernanceTokenInfo() const queryClient = useQueryClient() const members = useQueryLoadingDataWithError( indexerQueries.queryContract(queryClient, { - chainId, - contractAddress: votingModuleAddress, + chainId: votingModule.chainId, + contractAddress: votingModule.address, formula: 'daoVotingCw20Staked/topStakers', noFallback: true, }), diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx index 8c889f0a1..3bc754910 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx @@ -64,7 +64,7 @@ export const ProfileCardMemberInfo = ({ refreshTotals, claimsPending, claimsAvailable, - sumClaimsAvailable, + sumClaimsAvailable = HugeDecimal.zero, loadingWalletStakedValue, loadingTotalStakedValue, refreshClaims, @@ -111,7 +111,7 @@ export const ProfileCardMemberInfo = ({ if (!isWalletConnected) { return toast.error(t('error.logInToContinue')) } - if (!sumClaimsAvailable) { + if (sumClaimsAvailable.isZero()) { return toast.error(t('error.noClaimsAvailable')) } @@ -136,9 +136,7 @@ export const ProfileCardMemberInfo = ({ toast.success( t('success.claimedTokens', { - amount: HugeDecimal.from( - sumClaimsAvailable - ).toInternationalizedHumanReadableString({ + amount: sumClaimsAvailable.toInternationalizedHumanReadableString({ decimals: governanceToken.decimals, }), tokenSymbol: governanceToken.symbol, @@ -228,8 +226,8 @@ export const ProfileCardMemberInfo = ({ data: [ { token: governanceToken, - staked: HugeDecimal.from(loadingWalletStakedValue.data), - unstaked: HugeDecimal.from(loadingUnstakedBalance.data), + staked: loadingWalletStakedValue.data, + unstaked: loadingUnstakedBalance.data, }, ], } @@ -242,10 +240,10 @@ export const ProfileCardMemberInfo = ({ ? { loading: true } : { loading: false, - data: - (loadingWalletStakedValue.data / - loadingTotalStakedValue.data) * - 100, + data: loadingWalletStakedValue.data + .div(loadingTotalStakedValue.data) + .times(100) + .toNumber(), } } onClaim={onClaim} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx index a599a0ffa..b091f8ea0 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx @@ -67,7 +67,7 @@ const InnerStakingModal = ({ stakingContractAddress, unstakingDuration, refreshTotals, - sumClaimsAvailable, + sumClaimsAvailable = HugeDecimal.zero, loadingWalletStakedValue, refreshClaims, } = useStakingInfo({ @@ -279,7 +279,7 @@ const InnerStakingModal = ({ break } case StakingMode.Claim: { - if (sumClaimsAvailable === 0) { + if (sumClaimsAvailable.isZero()) { toast.error(t('error.noClaimsAvailable')) return } @@ -307,11 +307,11 @@ const InnerStakingModal = ({ toast.success( t('success.claimedTokens', { - amount: HugeDecimal.from( - sumClaimsAvailable || 0 - ).toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - }), + amount: sumClaimsAvailable.toInternationalizedHumanReadableString( + { + decimals: governanceToken.decimals, + } + ), tokenSymbol: governanceToken.symbol, }) ) @@ -335,26 +335,12 @@ const InnerStakingModal = ({ return ( <StatelessStakingModal amount={amount} - claimableTokens={HugeDecimal.from(sumClaimsAvailable || 0)} + claimableTokens={sumClaimsAvailable} error={isWalletConnected ? undefined : t('error.logInToContinue')} initialMode={initialMode} loading={stakingLoading} - loadingStakableTokens={ - !loadingUnstakedBalance || loadingUnstakedBalance.loading - ? { loading: true } - : { - loading: false, - data: HugeDecimal.from(loadingUnstakedBalance.data), - } - } - loadingUnstakableTokens={ - !loadingWalletStakedValue || loadingWalletStakedValue.loading - ? { loading: true } - : { - loading: false, - data: HugeDecimal.from(loadingWalletStakedValue.data), - } - } + loadingStakableTokens={loadingUnstakedBalance ?? { loading: true }} + loadingUnstakableTokens={loadingWalletStakedValue} onAction={onAction} onClose={onClose} proposalDeposit={maxDeposit ? HugeDecimal.from(maxDeposit) : undefined} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useGovernanceTokenInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useGovernanceTokenInfo.ts index aa57d9136..ce9b64859 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useGovernanceTokenInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useGovernanceTokenInfo.ts @@ -1,17 +1,18 @@ -import { constSelector, useRecoilValue, waitForAll } from 'recoil' +import { useQueryClient, useSuspenseQueries } from '@tanstack/react-query' +import { constSelector } from 'recoil' import { HugeDecimal } from '@dao-dao/math' import { Cw20BaseSelectors, - DaoVotingCw20StakedSelectors, - genericTokenSelector, + cw20BaseQueries, + daoVotingCw20StakedQueries, + tokenQueries, usdPriceSelector, } from '@dao-dao/state' -import { useCachedLoading } from '@dao-dao/stateless' +import { useCachedLoading, useDaoContext } from '@dao-dao/stateless' import { TokenType } from '@dao-dao/types' import { useWallet } from '../../../../hooks/useWallet' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { UseGovernanceTokenInfoOptions, UseGovernanceTokenInfoResponse, @@ -22,39 +23,38 @@ export const useGovernanceTokenInfo = ({ fetchTreasuryBalance = false, fetchUsdcPrice = false, }: UseGovernanceTokenInfoOptions = {}): UseGovernanceTokenInfoResponse => { + const { dao } = useDaoContext() const { address: walletAddress } = useWallet() - const { chainId, coreAddress, votingModuleAddress } = - useVotingModuleAdapterOptions() + const queryClient = useQueryClient() - const [stakingContractAddress, governanceTokenAddress] = useRecoilValue( - waitForAll([ - DaoVotingCw20StakedSelectors.stakingContractSelector({ - chainId, - contractAddress: votingModuleAddress, - params: [], - }), - DaoVotingCw20StakedSelectors.tokenContractSelector({ - chainId, - contractAddress: votingModuleAddress, - params: [], - }), - ]) - ) + const [{ data: stakingContractAddress }, { data: governanceTokenAddress }] = + useSuspenseQueries({ + queries: [ + daoVotingCw20StakedQueries.stakingContract(queryClient, { + chainId: dao.chainId, + contractAddress: dao.votingModule.address, + }), + daoVotingCw20StakedQueries.tokenContract(queryClient, { + chainId: dao.chainId, + contractAddress: dao.votingModule.address, + }), + ], + }) - const [governanceToken, cw20TokenInfo] = useRecoilValue( - waitForAll([ - genericTokenSelector({ - chainId, - type: TokenType.Cw20, - denomOrAddress: governanceTokenAddress, - }), - Cw20BaseSelectors.tokenInfoSelector({ - chainId, - contractAddress: governanceTokenAddress, - params: [], - }), - ]) - ) + const [{ data: governanceToken }, { data: cw20TokenInfo }] = + useSuspenseQueries({ + queries: [ + tokenQueries.info(queryClient, { + chainId: dao.chainId, + type: TokenType.Cw20, + denomOrAddress: governanceTokenAddress, + }), + cw20BaseQueries.tokenInfo(queryClient, { + chainId: dao.chainId, + contractAddress: governanceTokenAddress, + }), + ], + }) /// Optional @@ -62,7 +62,7 @@ export const useGovernanceTokenInfo = ({ const loadingWalletBalance = useCachedLoading( fetchWalletBalance && walletAddress ? Cw20BaseSelectors.balanceSelector({ - chainId, + chainId: dao.chainId, contractAddress: governanceTokenAddress, params: [{ address: walletAddress }], }) @@ -74,9 +74,9 @@ export const useGovernanceTokenInfo = ({ const loadingTreasuryBalance = useCachedLoading( fetchTreasuryBalance ? Cw20BaseSelectors.balanceSelector({ - chainId, + chainId: dao.chainId, contractAddress: governanceTokenAddress, - params: [{ address: coreAddress }], + params: [{ address: dao.coreAddress }], }) : constSelector(undefined), undefined @@ -86,8 +86,8 @@ export const useGovernanceTokenInfo = ({ const loadingPrice = useCachedLoading( fetchUsdcPrice ? usdPriceSelector({ + chainId: dao.chainId, type: TokenType.Cw20, - chainId, denomOrAddress: governanceTokenAddress, }) : constSelector(undefined), @@ -96,9 +96,7 @@ export const useGovernanceTokenInfo = ({ return { governanceToken, - supply: HugeDecimal.from(cw20TokenInfo.total_supply).toHumanReadableNumber( - governanceToken.decimals - ), + supply: HugeDecimal.from(cw20TokenInfo.total_supply), stakingContractAddress, /// Optional // Wallet balance @@ -108,7 +106,7 @@ export const useGovernanceTokenInfo = ({ ? undefined : { loading: false, - data: Number(loadingWalletBalance.data.balance), + data: HugeDecimal.from(loadingWalletBalance.data.balance), }, // Treasury balance loadingTreasuryBalance: loadingTreasuryBalance.loading @@ -117,7 +115,7 @@ export const useGovernanceTokenInfo = ({ ? undefined : { loading: false, - data: Number(loadingTreasuryBalance.data.balance), + data: HugeDecimal.from(loadingTreasuryBalance.data.balance), }, // Price loadingPrice: loadingPrice.loading diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useMainDaoInfoCards.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useMainDaoInfoCards.tsx index 224abb4e4..6b6c0714b 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useMainDaoInfoCards.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useMainDaoInfoCards.tsx @@ -18,7 +18,7 @@ import { useStakingInfo } from './useStakingInfo' export const useMainDaoInfoCards = (): DaoInfoCard[] => { const { t } = useTranslation() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() + const { votingModule } = useVotingModuleAdapterOptions() const { totalVotingWeight } = useMembership() const { unstakingDuration } = useStakingInfo() @@ -31,8 +31,8 @@ export const useMainDaoInfoCards = (): DaoInfoCard[] => { const queryClient = useQueryClient() const loadingMembers = useQueryLoadingDataWithError( indexerQueries.queryContract(queryClient, { - chainId, - contractAddress: votingModuleAddress, + chainId: votingModule.chainId, + contractAddress: votingModule.address, formula: 'daoVotingCw20Staked/topStakers', noFallback: true, }) @@ -40,7 +40,7 @@ export const useMainDaoInfoCards = (): DaoInfoCard[] => { return [ // Can't view members on Secret Network. - ...(isSecretNetwork(chainId) + ...(isSecretNetwork(votingModule.chainId) ? [] : [ { diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useStakingInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useStakingInfo.ts index e510f16f8..facddfbb2 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useStakingInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useStakingInfo.ts @@ -1,18 +1,24 @@ +import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query' import { useCallback } from 'react' -import { constSelector, useRecoilValue, useSetRecoilState } from 'recoil' +import { constSelector, useSetRecoilState } from 'recoil' +import { HugeDecimal } from '@dao-dao/math' import { Cw20StakeSelectors, - DaoVotingCw20StakedSelectors, blockHeightSelector, + cw20StakeQueries, + daoVotingCw20StakedQueries, refreshClaimsIdAtom, refreshWalletBalancesIdAtom, } from '@dao-dao/state' -import { useCachedLoadable, useCachedLoading } from '@dao-dao/stateless' +import { + useCachedLoadable, + useCachedLoading, + useDaoContext, +} from '@dao-dao/stateless' import { claimAvailable } from '@dao-dao/utils' import { useWallet } from '../../../../hooks/useWallet' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { UseStakingInfoOptions, UseStakingInfoResponse } from '../types' export const useStakingInfo = ({ @@ -20,25 +26,24 @@ export const useStakingInfo = ({ fetchTotalStakedValue = false, fetchWalletStakedValue = false, }: UseStakingInfoOptions = {}): UseStakingInfoResponse => { + const { dao } = useDaoContext() const { address: walletAddress } = useWallet() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() + const queryClient = useQueryClient() - const stakingContractAddress = useRecoilValue( - DaoVotingCw20StakedSelectors.stakingContractSelector({ - chainId, - contractAddress: votingModuleAddress, - params: [], + const { data: stakingContractAddress } = useSuspenseQuery( + daoVotingCw20StakedQueries.stakingContract(queryClient, { + chainId: dao.chainId, + contractAddress: dao.votingModule.address, }) ) const unstakingDuration = - useRecoilValue( - Cw20StakeSelectors.getConfigSelector({ - chainId, + useSuspenseQuery( + cw20StakeQueries.getConfig(queryClient, { + chainId: dao.chainId, contractAddress: stakingContractAddress, - params: [], }) - ).unstaking_duration ?? undefined + ).data.unstaking_duration ?? undefined const setRefreshTotalBalancesId = useSetRecoilState( refreshWalletBalancesIdAtom(undefined) @@ -55,7 +60,7 @@ export const useStakingInfo = ({ const blockHeightLoadable = useCachedLoadable( fetchClaims ? blockHeightSelector({ - chainId, + chainId: dao.chainId, }) : undefined ) @@ -73,7 +78,7 @@ export const useStakingInfo = ({ const loadingClaims = useCachedLoading( fetchClaims && walletAddress ? Cw20StakeSelectors.claimsSelector({ - chainId, + chainId: dao.chainId, contractAddress: stakingContractAddress, params: [{ address: walletAddress }], }) @@ -93,15 +98,15 @@ export const useStakingInfo = ({ ? claims?.filter((c) => claimAvailable(c, blockHeight)) : undefined const sumClaimsAvailable = claimsAvailable?.reduce( - (p, c) => p + Number(c.amount), - 0 + (sum, c) => sum.plus(c.amount), + HugeDecimal.zero ) // Total staked value const loadingTotalStakedValue = useCachedLoading( fetchTotalStakedValue ? Cw20StakeSelectors.totalValueSelector({ - chainId, + chainId: dao.chainId, contractAddress: stakingContractAddress, params: [], }) @@ -113,7 +118,7 @@ export const useStakingInfo = ({ const loadingWalletStakedValue = useCachedLoading( fetchWalletStakedValue && walletAddress ? Cw20StakeSelectors.stakedValueSelector({ - chainId, + chainId: dao.chainId, contractAddress: stakingContractAddress, params: [{ address: walletAddress }], }) @@ -143,7 +148,7 @@ export const useStakingInfo = ({ ? undefined : { loading: false, - data: Number(loadingTotalStakedValue.data.total), + data: HugeDecimal.from(loadingTotalStakedValue.data.total), }, // Wallet staked value loadingWalletStakedValue: loadingWalletStakedValue.loading @@ -152,7 +157,7 @@ export const useStakingInfo = ({ ? undefined : { loading: false, - data: Number(loadingWalletStakedValue.data.value), + data: HugeDecimal.from(loadingWalletStakedValue.data.value), }, } } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/types.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/types.ts index 408269401..1318242de 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/types.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/types.ts @@ -1,3 +1,4 @@ +import { HugeDecimal } from '@dao-dao/math' import { Duration, GenericToken, @@ -23,11 +24,11 @@ export interface UseStakingInfoResponse { claims?: Claim[] claimsPending?: Claim[] claimsAvailable?: Claim[] - sumClaimsAvailable?: number + sumClaimsAvailable?: HugeDecimal // Total staked value - loadingTotalStakedValue?: LoadingData<number> + loadingTotalStakedValue?: LoadingData<HugeDecimal> // Wallet staked value - loadingWalletStakedValue?: LoadingData<number> + loadingWalletStakedValue?: LoadingData<HugeDecimal> } export type UseGovernanceTokenInfoOptions = { @@ -53,7 +54,7 @@ export type UseGovernanceTokenInfoResponse = { /** * The supply of the governance token converted to the appropriate decimals. */ - supply: number + supply: HugeDecimal /** * The staking contract address for the governance token. */ @@ -65,12 +66,12 @@ export type UseGovernanceTokenInfoResponse = { * Unstaked governance token balance. Only defined if a wallet is connected * and the option to fetch this is true. */ - loadingWalletBalance?: LoadingData<number> + loadingWalletBalance?: LoadingData<HugeDecimal> /** * The treasury balance of the governance token. Only defined if the option to * fetch this is true. */ - loadingTreasuryBalance?: LoadingData<number> + loadingTreasuryBalance?: LoadingData<HugeDecimal> /** * The price of the governance token. Only defined if the option to fetch this * is true. diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/index.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/index.tsx index 686aa7bb5..d657fe9b3 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/index.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/index.tsx @@ -82,7 +82,7 @@ export class ManageMembersAction extends ActionBase<ManageMembersData> { async setup() { this.cw4GroupAddress = await this.options.queryClient.fetchQuery( daoVotingCw4Queries.groupContract(this.options.queryClient, { - chainId: this.votingModule.dao.chainId, + chainId: this.votingModule.chainId, contractAddress: this.votingModule.address, }) ) diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/MembersTab.tsx index bb4c4feb3..0e6043c88 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/MembersTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/MembersTab.tsx @@ -18,14 +18,14 @@ import { useVotingModuleAdapterOptions } from '../../../react/context' export const MembersTab = () => { const { t } = useTranslation() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() + const { votingModule } = useVotingModuleAdapterOptions() const token = useDaoGovernanceToken() ?? undefined const queryClient = useQueryClient() const members = useQueryLoadingDataWithError( daoVotingCw721StakedExtraQueries.topStakers(queryClient, { - chainId, - address: votingModuleAddress, + chainId: votingModule.chainId, + address: votingModule.address, }), (data) => data?.map( diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx index bd6afcc56..14f59886a 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx @@ -84,7 +84,7 @@ export const ProfileCardMemberInfo = ({ if (!isWalletConnected) { return toast.error(t('error.logInToContinue')) } - if (!sumClaimsAvailable) { + if (!sumClaimsAvailable?.isPositive()) { return toast.error(t('error.noClaimsAvailable')) } @@ -101,7 +101,7 @@ export const ProfileCardMemberInfo = ({ toast.success( t('success.claimedTokens', { - amount: sumClaimsAvailable.toLocaleString(), + amount: sumClaimsAvailable.toInternationalizedHumanReadableString(), tokenSymbol: collectionInfo.symbol, }) ) @@ -179,8 +179,8 @@ export const ProfileCardMemberInfo = ({ data: [ { token, - staked: HugeDecimal.from(loadingWalletStakedValue.data), - unstaked: HugeDecimal.from(loadingUnstakedBalance.data), + staked: loadingWalletStakedValue.data, + unstaked: loadingUnstakedBalance.data, }, ], } @@ -193,10 +193,10 @@ export const ProfileCardMemberInfo = ({ ? { loading: true } : { loading: false, - data: - (loadingWalletStakedValue.data / - loadingTotalStakedValue.data) * - 100, + data: loadingWalletStakedValue.data + .div(loadingTotalStakedValue.data) + .times(100) + .toNumber(), } } onClaim={onClaim} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/StakingModal.tsx index b1958daa2..cbda5d03e 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/StakingModal.tsx @@ -1,19 +1,14 @@ import { useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' -import { constSelector, useRecoilState, useSetRecoilState } from 'recoil' +import { useRecoilState, useSetRecoilState } from 'recoil' import { - DaoVotingCw721StakedSelectors, refreshDaoVotingPowerAtom, refreshWalletBalancesIdAtom, stakingLoadingAtom, } from '@dao-dao/state' -import { - ModalLoader, - SegmentedControls, - useCachedLoadable, -} from '@dao-dao/stateless' +import { ModalLoader, SegmentedControls } from '@dao-dao/stateless' import { BaseStakingModalProps, LazyNftCardInfo, @@ -82,24 +77,9 @@ const InnerStakingModal = ({ }) const hasStake = - loadingWalletStakedValue !== undefined && + loadingWalletStakedValue && !loadingWalletStakedValue.loading && - loadingWalletStakedValue.data > 0 - - const walletStakedBalanceLoadable = useCachedLoadable( - walletAddress - ? DaoVotingCw721StakedSelectors.votingPowerAtHeightSelector({ - chainId, - contractAddress: stakingContractAddress, - params: [{ address: walletAddress }], - }) - : constSelector(undefined) - ) - const walletStakedBalance = - walletStakedBalanceLoadable.state === 'hasValue' && - walletStakedBalanceLoadable.contents - ? Number(walletStakedBalanceLoadable.contents.power) - : undefined + loadingWalletStakedValue.data.isPositive() const doStakeMultiple = Cw721BaseHooks.useSendNftMultiple({ contractAddress: collectionAddress, @@ -163,11 +143,6 @@ const InnerStakingModal = ({ break } case StakingMode.Unstake: { - if (walletStakedBalance === undefined) { - toast.error(t('error.loadingData')) - return - } - setStakingLoading(true) try { diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useGovernanceCollectionInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useGovernanceCollectionInfo.ts index 4290f9d2a..76791e327 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useGovernanceCollectionInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useGovernanceCollectionInfo.ts @@ -1,14 +1,12 @@ +import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query' import { constSelector, useRecoilValue, waitForAll } from 'recoil' -import { - CommonNftSelectors, - DaoVotingCw721StakedSelectors, -} from '@dao-dao/state' -import { useCachedLoading } from '@dao-dao/stateless' +import { HugeDecimal } from '@dao-dao/math' +import { CommonNftSelectors, daoVotingCw721StakedQueries } from '@dao-dao/state' +import { useCachedLoading, useDaoContext } from '@dao-dao/stateless' import { TokenType } from '@dao-dao/types' import { useWallet } from '../../../../hooks/useWallet' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { UseGovernanceCollectionInfoOptions, UseGovernanceCollectionInfoResponse, @@ -18,30 +16,28 @@ export const useGovernanceCollectionInfo = ({ fetchWalletBalance = false, fetchTreasuryBalance = false, }: UseGovernanceCollectionInfoOptions = {}): UseGovernanceCollectionInfoResponse => { - const { chainId, coreAddress, votingModuleAddress } = - useVotingModuleAdapterOptions() - const { address: walletAddress } = useWallet({ - chainId, - }) + const { dao } = useDaoContext() + const { address: walletAddress } = useWallet() + const queryClient = useQueryClient() - const { nft_address: collectionAddress } = useRecoilValue( - DaoVotingCw721StakedSelectors.configSelector({ - chainId, - contractAddress: votingModuleAddress, - params: [], + const { + data: { nft_address: collectionAddress }, + } = useSuspenseQuery( + daoVotingCw721StakedQueries.config(queryClient, { + chainId: dao.chainId, + contractAddress: dao.votingModule.address, }) ) const [contractInfo, tokenSupplyInfo] = useRecoilValue( waitForAll([ CommonNftSelectors.contractInfoSelector({ - chainId, + chainId: dao.chainId, contractAddress: collectionAddress, params: [], }), - CommonNftSelectors.numTokensSelector({ - chainId, + chainId: dao.chainId, contractAddress: collectionAddress, params: [], }), @@ -54,7 +50,7 @@ export const useGovernanceCollectionInfo = ({ const loadingWalletBalance = useCachedLoading( fetchWalletBalance && walletAddress ? CommonNftSelectors.unpaginatedAllTokensForOwnerSelector({ - chainId, + chainId: dao.chainId, contractAddress: collectionAddress, owner: walletAddress, }) @@ -66,31 +62,31 @@ export const useGovernanceCollectionInfo = ({ const loadingTreasuryBalance = useCachedLoading( fetchTreasuryBalance ? CommonNftSelectors.unpaginatedAllTokensForOwnerSelector({ - chainId, + chainId: dao.chainId, contractAddress: collectionAddress, - owner: coreAddress, + owner: dao.coreAddress, }) : constSelector(undefined), undefined ) return { - stakingContractAddress: votingModuleAddress, + stakingContractAddress: dao.votingModule.address, collectionAddress, collectionInfo: { name: contractInfo.name, symbol: contractInfo.symbol, - totalSupply: tokenSupplyInfo.count, + totalSupply: HugeDecimal.from(tokenSupplyInfo.count), }, token: { - chainId, + chainId: dao.chainId, type: TokenType.Cw721, denomOrAddress: collectionAddress, symbol: contractInfo.symbol, decimals: 0, imageUrl: undefined, source: { - chainId, + chainId: dao.chainId, type: TokenType.Cw721, denomOrAddress: collectionAddress, }, @@ -103,7 +99,7 @@ export const useGovernanceCollectionInfo = ({ ? undefined : { loading: false, - data: Number(loadingWalletBalance.data.length), + data: HugeDecimal.from(loadingWalletBalance.data.length), }, // Treasury balance loadingTreasuryBalance: loadingTreasuryBalance.loading @@ -112,7 +108,7 @@ export const useGovernanceCollectionInfo = ({ ? undefined : { loading: false, - data: loadingTreasuryBalance.data.length, + data: HugeDecimal.from(loadingTreasuryBalance.data.length), }, } } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useMainDaoInfoCards.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useMainDaoInfoCards.tsx index 0994d5c56..2ffef1e89 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useMainDaoInfoCards.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useMainDaoInfoCards.tsx @@ -17,7 +17,7 @@ import { useStakingInfo } from './useStakingInfo' export const useMainDaoInfoCards = (): DaoInfoCard[] => { const { t } = useTranslation() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() + const { votingModule } = useVotingModuleAdapterOptions() const { loadingTotalStakedValue, unstakingDuration } = useStakingInfo({ fetchTotalStakedValue: true, @@ -34,14 +34,14 @@ export const useMainDaoInfoCards = (): DaoInfoCard[] => { const queryClient = useQueryClient() const loadingMembers = useQueryLoadingDataWithError( daoVotingCw721StakedExtraQueries.topStakers(queryClient, { - chainId, - address: votingModuleAddress, + chainId: votingModule.chainId, + address: votingModule.address, }) ) return [ // Can't view members on Secret Network. - ...(isSecretNetwork(chainId) + ...(isSecretNetwork(votingModule.chainId) ? [] : [ { @@ -72,7 +72,7 @@ export const useMainDaoInfoCards = (): DaoInfoCard[] => { value: loadingTotalStakedValue.loading ? '...' : formatPercentOf100( - (loadingTotalStakedValue.data / totalSupply) * 100 + loadingTotalStakedValue.data.div(totalSupply).times(100).toNumber() ), }, { diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useStakingInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useStakingInfo.ts index 1486fc339..3bd53b998 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useStakingInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useStakingInfo.ts @@ -6,6 +6,7 @@ import { waitForAll, } from 'recoil' +import { HugeDecimal } from '@dao-dao/math' import { CommonNftSelectors, DaoVotingCw721StakedSelectors, @@ -19,12 +20,12 @@ import { useCachedLoadable, useCachedLoading, useCachedLoadingWithError, + useDaoContext, } from '@dao-dao/stateless' import { NftClaim } from '@dao-dao/types/contracts/DaoVotingCw721Staked' import { claimAvailable } from '@dao-dao/utils' import { useWallet } from '../../../../hooks/useWallet' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { UseStakingInfoOptions, UseStakingInfoResponse } from '../types' import { useGovernanceCollectionInfo } from './useGovernanceCollectionInfo' @@ -34,10 +35,8 @@ export const useStakingInfo = ({ fetchWalletStakedValue = false, fetchWalletUnstakedNfts = false, }: UseStakingInfoOptions = {}): UseStakingInfoResponse => { - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() - const { address: walletAddress } = useWallet({ - chainId, - }) + const { dao } = useDaoContext() + const { address: walletAddress } = useWallet() const { collectionAddress: governanceTokenAddress } = useGovernanceCollectionInfo() @@ -46,12 +45,12 @@ export const useStakingInfo = ({ useRecoilValue( waitForAll([ contractVersionSelector({ - chainId, - contractAddress: votingModuleAddress, + chainId: dao.chainId, + contractAddress: dao.votingModule.address, }), DaoVotingCw721StakedSelectors.configSelector({ - chainId, - contractAddress: votingModuleAddress, + chainId: dao.chainId, + contractAddress: dao.votingModule.address, params: [], }), ]) @@ -62,7 +61,7 @@ export const useStakingInfo = ({ ) // Refresh NFTs owned by staking contract. const setRefreshStakedNftsId = useSetRecoilState( - refreshWalletBalancesIdAtom(votingModuleAddress) + refreshWalletBalancesIdAtom(dao.votingModule.address) ) // Refresh totals, mostly for total staked power. const refreshTotals = useCallback(() => { @@ -76,7 +75,7 @@ export const useStakingInfo = ({ const blockHeightLoadable = useCachedLoadable( fetchClaims ? blockHeightSelector({ - chainId, + chainId: dao.chainId, }) : undefined ) @@ -92,8 +91,8 @@ export const useStakingInfo = ({ const loadingClaims = useCachedLoading( fetchClaims && walletAddress ? DaoVotingCw721StakedSelectors.nftClaimsSelector({ - chainId, - contractAddress: votingModuleAddress, + chainId: dao.chainId, + contractAddress: dao.votingModule.address, params: [{ address: walletAddress }], }) : constSelector(undefined), @@ -116,14 +115,14 @@ export const useStakingInfo = ({ const claimsAvailable = blockHeight ? nftClaims?.filter((c) => claimAvailable(c, blockHeight)) : undefined - const sumClaimsAvailable = claimsAvailable?.length + const sumClaimsAvailable = HugeDecimal.from(claimsAvailable?.length || 0) // Total staked value const loadingTotalStakedValue = useCachedLoading( fetchTotalStakedValue ? DaoVotingCw721StakedSelectors.totalPowerAtHeightSelector({ - chainId, - contractAddress: votingModuleAddress, + chainId: dao.chainId, + contractAddress: dao.votingModule.address, params: [{}], }) : constSelector(undefined), @@ -134,8 +133,8 @@ export const useStakingInfo = ({ const loadingWalletStakedNftsLoadable = useCachedLoading( fetchWalletStakedValue && walletAddress ? DaoVotingCw721StakedSelectors.stakedNftsSelector({ - chainId, - contractAddress: votingModuleAddress, + chainId: dao.chainId, + contractAddress: dao.votingModule.address, params: [{ address: walletAddress }], }) : undefined, @@ -148,7 +147,7 @@ export const useStakingInfo = ({ ? waitForAll( loadingWalletStakedNftsLoadable.data?.map((tokenId) => nftCardInfoSelector({ - chainId, + chainId: dao.chainId, collection: governanceTokenAddress, tokenId, }) @@ -160,7 +159,7 @@ export const useStakingInfo = ({ const loadingWalletUnstakedNftsLoadable = useCachedLoadingWithError( fetchWalletUnstakedNfts && walletAddress && governanceTokenAddress ? CommonNftSelectors.unpaginatedAllTokensForOwnerSelector({ - chainId, + chainId: dao.chainId, contractAddress: governanceTokenAddress, owner: walletAddress, }) @@ -174,7 +173,7 @@ export const useStakingInfo = ({ ? waitForAll( loadingWalletUnstakedNftsLoadable.data?.map((tokenId) => nftCardInfoSelector({ - chainId, + chainId: dao.chainId, collection: governanceTokenAddress, tokenId, }) @@ -185,7 +184,7 @@ export const useStakingInfo = ({ return { stakingContractVersion, - stakingContractAddress: votingModuleAddress, + stakingContractAddress: dao.votingModule.address, unstakingDuration: unstakingDuration ?? undefined, refreshTotals, /// Optional @@ -206,7 +205,7 @@ export const useStakingInfo = ({ ? undefined : { loading: false, - data: Number(loadingTotalStakedValue.data.power), + data: HugeDecimal.from(loadingTotalStakedValue.data.power), }, // Wallet staked value loadingWalletStakedValue: loadingWalletStakedNftsLoadable.loading @@ -215,7 +214,7 @@ export const useStakingInfo = ({ ? undefined : { loading: false, - data: loadingWalletStakedNftsLoadable.data.length, + data: HugeDecimal.from(loadingWalletStakedNftsLoadable.data.length), }, loadingWalletStakedNfts, loadingWalletUnstakedNfts, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/types.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/types.ts index 6a1c30b14..4b6d38032 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/types.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/types.ts @@ -1,3 +1,4 @@ +import { HugeDecimal } from '@dao-dao/math' import { ContractVersion, Duration, @@ -27,11 +28,11 @@ export interface UseStakingInfoResponse { claims?: NftClaim[] claimsPending?: NftClaim[] claimsAvailable?: NftClaim[] - sumClaimsAvailable?: number + sumClaimsAvailable?: HugeDecimal // Total staked value - loadingTotalStakedValue?: LoadingData<number> + loadingTotalStakedValue?: LoadingData<HugeDecimal> // Wallet staked value - loadingWalletStakedValue?: LoadingData<number> + loadingWalletStakedValue?: LoadingData<HugeDecimal> loadingWalletStakedNfts?: LoadingDataWithError<NftCardInfo[]> loadingWalletUnstakedNfts?: LoadingDataWithError<NftCardInfo[]> } @@ -48,14 +49,14 @@ export interface UseGovernanceCollectionInfoResponse { collectionInfo: { name: string symbol: string - totalSupply: number + totalSupply: HugeDecimal } token: GenericToken /// Optional // Wallet balance - loadingWalletBalance?: LoadingData<number> + loadingWalletBalance?: LoadingData<HugeDecimal> // Treasury balance - loadingTreasuryBalance?: LoadingData<number> + loadingTreasuryBalance?: LoadingData<HugeDecimal> // Price // loadingPrice?: LoadingData<GenericTokenWithUsdPrice> } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/README.md b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/README.md deleted file mode 100644 index df523e401..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# DaoVotingNativeStaked - -This is the voting module adapter for the -[`dao-voting-native-staked`](https://github.com/DA0-DA0/dao-contracts/tree/7f89ad1604e8022f202aef729853b0c8c7196988/contracts/voting/dao-voting-native-staked) -contract, which determines DAO voting power based on the staked balance of its -chosen native token (IBC, factory, or otherwise), such as `JUNO` or -`factory/junoContract/subdenom`. This is an alternative to the -[CW20](https://docs.cosmwasm.com/cw-plus/0.9.0/cw20/spec) governance token-based -DAO structure, where members are still free to exchange their unstaked tokens -with other parties at any time. However, it uses a native token instead of a -[CW20](https://docs.cosmwasm.com/cw-plus/0.9.0/cw20/spec) token. - -## Layout - -| Location | Summary | -| -------------------------- | ------------------- | -| [components](./components) | React components. | -| [hooks](./hooks) | React hooks. | -| [index.tsx](./index.tsx) | Adapter definition. | diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.stories.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.stories.tsx deleted file mode 100644 index 444a3487e..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.stories.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react' - -import { makeReactHookFormDecorator } from '@dao-dao/storybook' -import { DurationUnits } from '@dao-dao/types' - -import { - UpdateStakingConfigComponent, - UpdateStakingConfigData, -} from './Component' - -export default { - title: - 'DAO DAO / packages / stateful / voting-module-adapter / adapters / DaoVotingNativeStaked / actions / UpdateStakingConfig', - component: UpdateStakingConfigComponent, - decorators: [ - makeReactHookFormDecorator<UpdateStakingConfigData>({ - unstakingDurationEnabled: true, - unstakingDuration: { - value: 4, - units: DurationUnits.Days, - }, - }), - ], -} as ComponentMeta<typeof UpdateStakingConfigComponent> - -const Template: ComponentStory<typeof UpdateStakingConfigComponent> = ( - args -) => <UpdateStakingConfigComponent {...args} /> - -export const Default = Template.bind({}) -Default.args = { - fieldNamePrefix: '', - allActionsWithData: [], - index: 0, - data: {}, - isCreating: true, -} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.tsx deleted file mode 100644 index b6fdbdd12..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/Component.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { useFormContext } from 'react-hook-form' -import { useTranslation } from 'react-i18next' - -import { - FormSwitchCard, - InputErrorMessage, - NumericInput, - SelectInput, -} from '@dao-dao/stateless' -import { - ActionComponent, - BooleanFieldNames, - DurationUnitsValues, - DurationWithUnits, -} from '@dao-dao/types' -import { validatePositive, validateRequired } from '@dao-dao/utils' - -export type UpdateStakingConfigData = { - unstakingDurationEnabled: boolean - unstakingDuration: DurationWithUnits -} - -export const UpdateStakingConfigComponent: ActionComponent = ({ - fieldNamePrefix, - errors, - isCreating, -}) => { - const { t } = useTranslation() - const { register, watch, setValue, getValues } = - useFormContext<UpdateStakingConfigData>() - - const unstakingDurationEnabled = watch( - (fieldNamePrefix + 'unstakingDurationEnabled') as 'unstakingDurationEnabled' - ) - const unstakingDuration = watch( - (fieldNamePrefix + 'unstakingDuration') as 'unstakingDuration' - ) - - return ( - <> - <FormSwitchCard< - UpdateStakingConfigData, - BooleanFieldNames<UpdateStakingConfigData> - > - containerClassName="self-start" - fieldName={ - (fieldNamePrefix + - 'unstakingDurationEnabled') as 'unstakingDurationEnabled' - } - label={t('form.unstakingDurationTitle')} - readOnly={!isCreating} - setValue={setValue} - tooltip={t('form.unstakingDurationDescription')} - value={unstakingDurationEnabled} - /> - - {unstakingDurationEnabled && ( - <> - <div className="flex flex-row gap-2"> - <NumericInput - disabled={!isCreating} - error={errors?.unstakingDuration?.value} - fieldName={ - (fieldNamePrefix + - 'unstakingDuration.value') as 'unstakingDuration.value' - } - getValues={getValues} - min={1} - numericValue - register={register} - setValue={setValue} - sizing="md" - step={1} - unit={ - !isCreating - ? t(`unit.${unstakingDuration.units}`, { - count: unstakingDuration.value, - }).toLocaleLowerCase() - : undefined - } - validation={[validatePositive, validateRequired]} - /> - - {isCreating && ( - <SelectInput - error={errors?.unstakingDuration?.units} - fieldName={ - (fieldNamePrefix + - 'unstakingDuration.units') as 'unstakingDuration.units' - } - register={register} - validation={[validateRequired]} - > - {DurationUnitsValues.map((type, idx) => ( - <option key={idx} value={type}> - {t(`unit.${type}`, { - count: unstakingDuration.value, - }).toLocaleLowerCase()} - </option> - ))} - </SelectInput> - )} - </div> - - <InputErrorMessage - className="-mt-2" - error={errors?.unstakingDuration?.value} - /> - </> - )} - </> - ) -} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/README.md b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/README.md deleted file mode 100644 index 79c2dc74c..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# UpdateStakingConfig - -Update token staking configuration. - -## Bulk import format - -This is relevant when bulk importing actions, as described in [this -guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). - -### Key - -`updateStakingConfig` - -### Data format - -```json -{ - "unstakingDurationEnabled": <true | false>, - "unstakingDuration": { - "value": <NUMBER>, - "units": "<seconds | minutes | hours | days | weeks | months | years>" - } -} -``` diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/index.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/index.ts deleted file mode 100644 index 8185b4b0e..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/UpdateStakingConfig/index.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { daoVotingNativeStakedQueries } from '@dao-dao/state/query' -import { ActionBase, GearEmoji } from '@dao-dao/stateless' -import { DurationUnits, UnifiedCosmosMsg } from '@dao-dao/types' -import { - ActionContextType, - ActionKey, - ActionMatch, - ActionOptions, - ProcessedMessage, -} from '@dao-dao/types/actions' -import { - convertDurationToDurationWithUnits, - convertDurationWithUnitsToDuration, - makeExecuteSmartContractMessage, - objectMatchesStructure, -} from '@dao-dao/utils' - -import { - UpdateStakingConfigComponent as Component, - UpdateStakingConfigData, -} from './Component' - -export class UpdateStakingConfigAction extends ActionBase<UpdateStakingConfigData> { - public readonly key = ActionKey.UpdateStakingConfig - public readonly Component = Component - - private stakingContractAddress: string - - constructor(options: ActionOptions) { - // Type-check. - if (options.context.type !== ActionContextType.Dao) { - throw new Error('Invalid context for update staking config action') - } - - super(options, { - Icon: GearEmoji, - label: options.t('title.updateStakingConfig'), - description: options.t('info.updateStakingConfigDescription'), - }) - - this.stakingContractAddress = options.context.dao.votingModule.address - } - - async setup() { - const { unstaking_duration } = await this.options.queryClient.fetchQuery( - daoVotingNativeStakedQueries.getConfig(this.options.queryClient, { - chainId: this.options.chain.chain_id, - contractAddress: this.stakingContractAddress, - }) - ) - - this.defaults = { - unstakingDurationEnabled: !!unstaking_duration, - unstakingDuration: unstaking_duration - ? convertDurationToDurationWithUnits(unstaking_duration) - : { - value: 2, - units: DurationUnits.Weeks, - }, - } - } - - encode({ - unstakingDurationEnabled, - unstakingDuration, - }: UpdateStakingConfigData): UnifiedCosmosMsg { - return makeExecuteSmartContractMessage({ - chainId: this.options.chain.chain_id, - sender: this.options.address, - contractAddress: this.stakingContractAddress, - msg: { - update_config: { - duration: unstakingDurationEnabled - ? convertDurationWithUnitsToDuration(unstakingDuration) - : null, - }, - }, - }) - } - - match([{ decodedMessage }]: ProcessedMessage[]): ActionMatch { - return ( - objectMatchesStructure(decodedMessage, { - wasm: { - execute: { - contract_addr: {}, - funds: {}, - msg: { - update_config: {}, - }, - }, - }, - }) && - decodedMessage.wasm.execute.contract_addr === this.stakingContractAddress - ) - } - - decode([{ decodedMessage }]: ProcessedMessage[]): UpdateStakingConfigData { - return { - unstakingDurationEnabled: - !!decodedMessage.wasm.execute.msg.update_config.duration, - unstakingDuration: decodedMessage.wasm.execute.msg.update_config.duration - ? convertDurationToDurationWithUnits( - decodedMessage.wasm.execute.msg.update_config.duration - ) - : { - value: 2, - units: DurationUnits.Weeks, - }, - } - } -} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/index.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/index.ts deleted file mode 100644 index 40591c371..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './Mint' -export * from './UpdateStakingConfig' diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/MembersTab.tsx deleted file mode 100644 index fbb86684c..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/MembersTab.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { useQueryClient } from '@tanstack/react-query' -import { useTranslation } from 'react-i18next' - -import { HugeDecimal } from '@dao-dao/math' -import { indexerQueries } from '@dao-dao/state/query' -import { MembersTab as StatelessMembersTab } from '@dao-dao/stateless' -import { StatefulDaoMemberCardProps } from '@dao-dao/types' - -import { - ButtonLink, - DaoMemberCard, - EntityDisplay, -} from '../../../../components' -import { useQueryLoadingDataWithError } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' -import { useGovernanceTokenInfo } from '../hooks/useGovernanceTokenInfo' - -export const MembersTab = () => { - const { t } = useTranslation() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() - const { governanceToken } = useGovernanceTokenInfo() - - const queryClient = useQueryClient() - const members = useQueryLoadingDataWithError( - indexerQueries.queryContract(queryClient, { - chainId, - contractAddress: votingModuleAddress, - formula: 'daoVotingNativeStaked/topStakers', - noFallback: true, - }), - (data) => - data?.map( - ({ - address, - balance, - votingPowerPercent, - }: any): StatefulDaoMemberCardProps => ({ - address, - balanceLabel: t('title.staked'), - balance: { - loading: false, - data: { - amount: HugeDecimal.from(balance), - token: governanceToken, - }, - }, - votingPowerPercent: { - loading: false, - data: votingPowerPercent, - }, - }) - ) ?? [] - ) - - return ( - <StatelessMembersTab - ButtonLink={ButtonLink} - DaoMemberCard={DaoMemberCard} - members={members} - topVoters={{ - show: true, - EntityDisplay, - }} - /> - ) -} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/ProfileCardMemberInfo.tsx deleted file mode 100644 index 439ec79a2..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/ProfileCardMemberInfo.tsx +++ /dev/null @@ -1,223 +0,0 @@ -import { useCallback, useState } from 'react' -import toast from 'react-hot-toast' -import { useTranslation } from 'react-i18next' -import { useRecoilValue } from 'recoil' - -import { HugeDecimal } from '@dao-dao/math' -import { - blockHeightSelector, - blocksPerYearSelector, - stakingLoadingAtom, -} from '@dao-dao/state' -import { useCachedLoadable, useDaoInfoContext } from '@dao-dao/stateless' -import { - BaseProfileCardMemberInfoProps, - UnstakingTask, - UnstakingTaskStatus, -} from '@dao-dao/types' -import { - convertExpirationToDate, - durationToSeconds, - processError, -} from '@dao-dao/utils' - -import { - DaoVotingNativeStakedHooks, - useAwaitNextBlock, - useWallet, -} from '../../../../hooks' -import { ProfileCardMemberInfoTokens } from '../../../components' -import { useVotingModuleAdapterOptions } from '../../../react/context' -import { useGovernanceTokenInfo, useStakingInfo } from '../hooks' -import { StakingModal } from './StakingModal' - -export const ProfileCardMemberInfo = ({ - maxGovernanceTokenDeposit, - ...props -}: BaseProfileCardMemberInfoProps) => { - const { t } = useTranslation() - const { name: daoName } = useDaoInfoContext() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() - const { - address: walletAddress, - isWalletConnected, - refreshBalances, - } = useWallet({ - chainId, - }) - - const [showStakingModal, setShowStakingModal] = useState(false) - const [claimingLoading, setClaimingLoading] = useState(false) - const stakingLoading = useRecoilValue(stakingLoadingAtom) - - const { governanceToken, loadingWalletBalance: loadingUnstakedBalance } = - useGovernanceTokenInfo({ - fetchWalletBalance: true, - }) - - const { - unstakingDuration, - refreshTotals, - claimsPending, - claimsAvailable, - sumClaimsAvailable, - loadingWalletStakedValue, - loadingTotalStakedValue, - refreshClaims, - } = useStakingInfo({ - fetchClaims: true, - fetchWalletStakedValue: true, - fetchTotalStakedValue: true, - }) - - const doClaim = DaoVotingNativeStakedHooks.useClaim({ - contractAddress: votingModuleAddress, - sender: walletAddress ?? '', - }) - - const awaitNextBlock = useAwaitNextBlock() - const onClaim = useCallback(async () => { - if (!isWalletConnected) { - return toast.error(t('error.logInToContinue')) - } - if (!sumClaimsAvailable) { - return toast.error(t('error.noClaimsAvailable')) - } - - setClaimingLoading(true) - try { - await doClaim() - - // New balances will not appear until the next block. - await awaitNextBlock() - - refreshBalances() - refreshTotals() - refreshClaims?.() - - toast.success( - t('success.claimedTokens', { - amount: HugeDecimal.from( - sumClaimsAvailable - ).toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - }), - tokenSymbol: governanceToken.symbol, - }) - ) - } catch (err) { - console.error(err) - toast.error(processError(err)) - } finally { - setClaimingLoading(false) - } - }, [ - awaitNextBlock, - isWalletConnected, - doClaim, - governanceToken.decimals, - governanceToken.symbol, - refreshBalances, - refreshClaims, - refreshTotals, - sumClaimsAvailable, - t, - ]) - - const blockHeightLoadable = useCachedLoadable( - blockHeightSelector({ - chainId, - }) - ) - const blocksPerYear = useRecoilValue( - blocksPerYearSelector({ - chainId, - }) - ) - - const unstakingTasks: UnstakingTask[] = [ - ...(claimsPending ?? []).map(({ amount, release_at }) => ({ - token: governanceToken, - status: UnstakingTaskStatus.Unstaking, - amount: HugeDecimal.from(amount), - date: convertExpirationToDate( - blocksPerYear, - release_at, - blockHeightLoadable.state === 'hasValue' - ? blockHeightLoadable.contents - : 0 - ), - })), - ...(claimsAvailable ?? []).map(({ amount, release_at }) => ({ - token: governanceToken, - status: UnstakingTaskStatus.ReadyToClaim, - amount: HugeDecimal.from(amount), - date: convertExpirationToDate( - blocksPerYear, - release_at, - blockHeightLoadable.state === 'hasValue' - ? blockHeightLoadable.contents - : 0 - ), - })), - ] - - return ( - <> - <ProfileCardMemberInfoTokens - claimingLoading={claimingLoading} - daoName={daoName} - loadingTokens={ - !loadingWalletStakedValue || - loadingWalletStakedValue.loading || - !loadingUnstakedBalance || - loadingUnstakedBalance.loading - ? { - loading: true, - } - : { - loading: false, - data: [ - { - token: governanceToken, - staked: HugeDecimal.from(loadingWalletStakedValue.data), - unstaked: HugeDecimal.from(loadingUnstakedBalance.data), - }, - ], - } - } - loadingVotingPower={ - !loadingWalletStakedValue || - loadingWalletStakedValue.loading || - !loadingTotalStakedValue || - loadingTotalStakedValue.loading - ? { loading: true } - : { - loading: false, - data: - (loadingWalletStakedValue.data / - loadingTotalStakedValue.data) * - 100, - } - } - onClaim={onClaim} - onStake={() => setShowStakingModal(true)} - refreshUnstakingTasks={() => refreshClaims?.()} - stakingLoading={stakingLoading} - unstakingDurationSeconds={ - (unstakingDuration && - durationToSeconds(blocksPerYear, unstakingDuration)) || - undefined - } - unstakingTasks={unstakingTasks} - {...props} - /> - - <StakingModal - maxDeposit={maxGovernanceTokenDeposit} - onClose={() => setShowStakingModal(false)} - visible={showStakingModal} - /> - </> - ) -} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx deleted file mode 100644 index d02b05ab5..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/StakingModal.tsx +++ /dev/null @@ -1,254 +0,0 @@ -import { useState } from 'react' -import toast from 'react-hot-toast' -import { useTranslation } from 'react-i18next' -import { useRecoilState, useSetRecoilState } from 'recoil' - -import { HugeDecimal } from '@dao-dao/math' -import { - refreshDaoVotingPowerAtom, - refreshFollowingDaosAtom, - stakingLoadingAtom, -} from '@dao-dao/state' -import { - ModalLoader, - StakingModal as StatelessStakingModal, -} from '@dao-dao/stateless' -import { BaseStakingModalProps, StakingMode } from '@dao-dao/types' -import { CHAIN_GAS_MULTIPLIER, processError } from '@dao-dao/utils' - -import { SuspenseLoader } from '../../../../components' -import { - DaoVotingNativeStakedHooks, - useAwaitNextBlock, - useWallet, -} from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' -import { useGovernanceTokenInfo, useStakingInfo } from '../hooks' - -export const StakingModal = (props: BaseStakingModalProps) => ( - <SuspenseLoader - fallback={<ModalLoader onClose={props.onClose} visible={props.visible} />} - > - <InnerStakingModal {...props} /> - </SuspenseLoader> -) - -const InnerStakingModal = ({ - visible, - onClose, - initialMode = StakingMode.Stake, - maxDeposit, -}: BaseStakingModalProps) => { - const { t } = useTranslation() - const { - address: walletAddress, - isWalletConnected, - refreshBalances, - } = useWallet() - const { coreAddress, votingModuleAddress } = useVotingModuleAdapterOptions() - - const [stakingLoading, setStakingLoading] = useRecoilState(stakingLoadingAtom) - - const { governanceToken, loadingWalletBalance: loadingUnstakedBalance } = - useGovernanceTokenInfo({ - fetchWalletBalance: true, - }) - const { - unstakingDuration, - refreshTotals, - sumClaimsAvailable, - loadingWalletStakedValue, - refreshClaims, - } = useStakingInfo({ - fetchClaims: true, - fetchWalletStakedValue: true, - }) - - const [amount, setAmount] = useState(HugeDecimal.zero) - - const doStake = DaoVotingNativeStakedHooks.useStake({ - contractAddress: votingModuleAddress, - sender: walletAddress ?? '', - }) - const doUnstake = DaoVotingNativeStakedHooks.useUnstake({ - contractAddress: votingModuleAddress, - sender: walletAddress ?? '', - }) - const doClaim = DaoVotingNativeStakedHooks.useClaim({ - contractAddress: votingModuleAddress, - sender: walletAddress ?? '', - }) - - const setRefreshDaoVotingPower = useSetRecoilState( - refreshDaoVotingPowerAtom(coreAddress) - ) - const setRefreshFollowedDaos = useSetRecoilState(refreshFollowingDaosAtom) - const refreshDaoVotingPower = () => { - setRefreshDaoVotingPower((id) => id + 1) - setRefreshFollowedDaos((id) => id + 1) - } - - const awaitNextBlock = useAwaitNextBlock() - const onAction = async (mode: StakingMode, amount: HugeDecimal) => { - if (!isWalletConnected) { - toast.error(t('error.logInToContinue')) - return - } - - setStakingLoading(true) - - switch (mode) { - case StakingMode.Stake: { - setStakingLoading(true) - - try { - await doStake( - CHAIN_GAS_MULTIPLIER, - undefined, - amount.toCoins(governanceToken.denomOrAddress) - ) - - // New balances will not appear until the next block. - await awaitNextBlock() - - refreshBalances() - refreshTotals() - refreshDaoVotingPower() - - setAmount(HugeDecimal.zero) - toast.success( - t('success.stakedTokens', { - amount: amount.toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - }), - tokenSymbol: governanceToken.symbol, - }) - ) - - // Close once done. - onClose() - } catch (err) { - console.error(err) - toast.error(processError(err)) - } finally { - setStakingLoading(false) - } - - break - } - case StakingMode.Unstake: { - setStakingLoading(true) - - try { - await doUnstake({ - amount: amount.toFixed(0), - }) - - // New balances will not appear until the next block. - await awaitNextBlock() - - refreshBalances() - refreshTotals() - refreshClaims?.() - refreshDaoVotingPower() - - setAmount(HugeDecimal.zero) - toast.success( - t('success.unstakedTokens', { - amount: amount.toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - }), - tokenSymbol: governanceToken.symbol, - }) - ) - - // Close once done. - onClose() - } catch (err) { - console.error(err) - toast.error(processError(err)) - } finally { - setStakingLoading(false) - } - - break - } - case StakingMode.Claim: { - if (sumClaimsAvailable === 0) { - toast.error(t('error.noClaimsAvailable')) - return - } - - setStakingLoading(true) - try { - await doClaim() - - // New balances will not appear until the next block. - await awaitNextBlock() - - refreshBalances() - refreshTotals() - refreshClaims?.() - - setAmount(HugeDecimal.zero) - - toast.success( - t('success.claimedTokens', { - amount: HugeDecimal.from( - sumClaimsAvailable || 0 - ).toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - }), - tokenSymbol: governanceToken.symbol, - }) - ) - - // Close once done. - onClose() - } catch (err) { - console.error(err) - toast.error(processError(err)) - } finally { - setStakingLoading(false) - } - - break - } - default: - toast.error('Internal error while staking. Unrecognized mode.') - } - } - - return ( - <StatelessStakingModal - amount={amount} - claimableTokens={HugeDecimal.from(sumClaimsAvailable || 0)} - error={isWalletConnected ? undefined : t('error.logInToContinue')} - initialMode={initialMode} - loading={stakingLoading} - loadingStakableTokens={ - !loadingUnstakedBalance || loadingUnstakedBalance.loading - ? { loading: true } - : { - loading: false, - data: HugeDecimal.from(loadingUnstakedBalance.data), - } - } - loadingUnstakableTokens={ - !loadingWalletStakedValue || loadingWalletStakedValue.loading - ? { loading: true } - : { - loading: false, - data: HugeDecimal.from(loadingWalletStakedValue.data), - } - } - onAction={onAction} - onClose={onClose} - proposalDeposit={maxDeposit ? HugeDecimal.from(maxDeposit) : undefined} - setAmount={(newAmount) => setAmount(newAmount)} - token={governanceToken} - unstakingDuration={unstakingDuration ?? null} - visible={visible} - /> - ) -} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/index.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/index.ts deleted file mode 100644 index 8b6d3650b..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/components/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './MembersTab' -export * from './ProfileCardMemberInfo' -export * from './StakingModal' diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/index.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/index.ts deleted file mode 100644 index ca93746a8..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './useGovernanceTokenInfo' -export * from './useMainDaoInfoCards' -export * from './useStakingInfo' diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useGovernanceTokenInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useGovernanceTokenInfo.ts deleted file mode 100644 index ce48277bf..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useGovernanceTokenInfo.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { constSelector, useRecoilValue, waitForAll } from 'recoil' - -import { HugeDecimal } from '@dao-dao/math' -import { - DaoVotingNativeStakedSelectors, - genericTokenSelector, - nativeDenomBalanceSelector, - nativeSupplySelector, - usdPriceSelector, -} from '@dao-dao/state' -import { useCachedLoading } from '@dao-dao/stateless' -import { TokenType } from '@dao-dao/types' - -import { useWallet } from '../../../../hooks/useWallet' -import { useVotingModuleAdapterOptions } from '../../../react/context' -import { - UseGovernanceTokenInfoOptions, - UseGovernanceTokenInfoResponse, -} from '../types' - -export const useGovernanceTokenInfo = ({ - fetchWalletBalance = false, - fetchTreasuryBalance = false, - fetchUsdcPrice = false, -}: UseGovernanceTokenInfoOptions = {}): UseGovernanceTokenInfoResponse => { - const { chainId, coreAddress, votingModuleAddress } = - useVotingModuleAdapterOptions() - const { address: walletAddress } = useWallet({ - chainId, - }) - - const { denom } = useRecoilValue( - DaoVotingNativeStakedSelectors.getConfigSelector({ - chainId, - contractAddress: votingModuleAddress, - params: [], - }) - ) - - const [governanceToken, supply] = useRecoilValue( - waitForAll([ - genericTokenSelector({ - chainId, - type: TokenType.Native, - denomOrAddress: denom, - }), - nativeSupplySelector({ - chainId, - denom, - }), - ]) - ) - - /// Optional - - // Wallet balance - const loadingWalletBalance = useCachedLoading( - fetchWalletBalance && walletAddress - ? nativeDenomBalanceSelector({ - chainId, - walletAddress, - denom, - }) - : constSelector(undefined), - undefined - ) - - // Treasury balance - const loadingTreasuryBalance = useCachedLoading( - fetchTreasuryBalance - ? nativeDenomBalanceSelector({ - chainId, - walletAddress: coreAddress, - denom, - }) - : constSelector(undefined), - undefined - ) - - // Price info - const loadingPrice = useCachedLoading( - fetchUsdcPrice - ? usdPriceSelector({ - type: TokenType.Native, - chainId, - denomOrAddress: denom, - }) - : constSelector(undefined), - undefined - ) - - return { - governanceToken, - supply: HugeDecimal.from(supply).toHumanReadableNumber( - governanceToken.decimals - ), - /// Optional - // Wallet balance - loadingWalletBalance: loadingWalletBalance.loading - ? { loading: true } - : !loadingWalletBalance.data - ? undefined - : { - loading: false, - data: Number(loadingWalletBalance.data.amount), - }, - // Treasury balance - loadingTreasuryBalance: loadingTreasuryBalance.loading - ? { loading: true } - : !loadingTreasuryBalance.data - ? undefined - : { - loading: false, - data: Number(loadingTreasuryBalance.data.amount), - }, - // Price - loadingPrice: loadingPrice.loading - ? { loading: true } - : !loadingPrice.data - ? undefined - : { - loading: false, - data: loadingPrice.data, - }, - } -} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useMainDaoInfoCards.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useMainDaoInfoCards.tsx deleted file mode 100644 index 223624889..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useMainDaoInfoCards.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { useQueryClient } from '@tanstack/react-query' -import { useTranslation } from 'react-i18next' - -import { HugeDecimal } from '@dao-dao/math' -import { indexerQueries } from '@dao-dao/state' -import { TokenAmountDisplay } from '@dao-dao/stateless' -import { DaoInfoCard } from '@dao-dao/types' -import { convertDurationToHumanReadableString } from '@dao-dao/utils' - -import { useQueryLoadingDataWithError } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' -import { useGovernanceTokenInfo } from './useGovernanceTokenInfo' -import { useStakingInfo } from './useStakingInfo' - -export const useMainDaoInfoCards = (): DaoInfoCard[] => { - const { t } = useTranslation() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() - const { loadingTotalStakedValue, unstakingDuration } = useStakingInfo({ - fetchTotalStakedValue: true, - }) - - if (loadingTotalStakedValue === undefined) { - throw new Error(t('error.loadingData')) - } - - const { - governanceToken: { decimals, symbol }, - supply, - } = useGovernanceTokenInfo() - - const queryClient = useQueryClient() - const loadingMembers = useQueryLoadingDataWithError( - indexerQueries.queryContract(queryClient, { - chainId, - contractAddress: votingModuleAddress, - formula: 'daoVotingNativeStaked/topStakers', - noFallback: true, - }) - ) - - return [ - { - label: t('title.members'), - tooltip: t('info.membersTooltip'), - loading: loadingMembers.loading, - value: loadingMembers.loading - ? undefined - : loadingMembers.errored - ? '<error>' - : loadingMembers.data?.length ?? '<error>', - }, - { - label: t('title.totalSupply'), - tooltip: t('info.totalSupplyTooltip', { - tokenSymbol: symbol, - }), - value: ( - <TokenAmountDisplay - amount={supply} - decimals={decimals} - symbol={symbol} - /> - ), - }, - { - label: t('title.totalStaked'), - tooltip: t('info.totalStakedTooltip', { - tokenSymbol: symbol, - }), - value: ( - <TokenAmountDisplay - amount={ - loadingTotalStakedValue.loading - ? { loading: true } - : HugeDecimal.from(loadingTotalStakedValue.data) - } - decimals={decimals} - symbol={symbol} - /> - ), - }, - { - label: t('title.unstakingPeriod'), - tooltip: t('info.unstakingPeriodTooltip', { - tokenSymbol: symbol, - }), - value: unstakingDuration - ? convertDurationToHumanReadableString(t, unstakingDuration) - : t('info.none'), - }, - ] -} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useStakingInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useStakingInfo.ts deleted file mode 100644 index c6449b7f8..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/hooks/useStakingInfo.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { useCallback } from 'react' -import { constSelector, useRecoilValue, useSetRecoilState } from 'recoil' - -import { - DaoVotingNativeStakedSelectors, - blockHeightSelector, - refreshClaimsIdAtom, - refreshWalletBalancesIdAtom, -} from '@dao-dao/state' -import { useCachedLoadable, useCachedLoading } from '@dao-dao/stateless' -import { claimAvailable } from '@dao-dao/utils' - -import { useWallet } from '../../../../hooks/useWallet' -import { useVotingModuleAdapterOptions } from '../../../react/context' -import { UseStakingInfoOptions, UseStakingInfoResponse } from '../types' - -export const useStakingInfo = ({ - fetchClaims = false, - fetchTotalStakedValue = false, - fetchWalletStakedValue = false, -}: UseStakingInfoOptions = {}): UseStakingInfoResponse => { - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() - const { address: walletAddress } = useWallet({ - chainId, - }) - - const config = useRecoilValue( - DaoVotingNativeStakedSelectors.getConfigSelector({ - chainId, - contractAddress: votingModuleAddress, - params: [], - }) - ) - - const setRefreshTotalBalancesId = useSetRecoilState( - refreshWalletBalancesIdAtom(undefined) - ) - // Refresh totals, mostly for total staked power. - const refreshTotals = useCallback( - () => setRefreshTotalBalancesId((id) => id + 1), - [setRefreshTotalBalancesId] - ) - - /// Optional - - // Claims - const blockHeightLoadable = useCachedLoadable( - fetchClaims - ? blockHeightSelector({ - chainId, - }) - : undefined - ) - const blockHeight = - blockHeightLoadable.state === 'hasValue' - ? blockHeightLoadable.contents - : undefined - - const _setClaimsId = useSetRecoilState(refreshClaimsIdAtom(walletAddress)) - const refreshClaims = () => _setClaimsId((id) => id + 1) - - const loadingClaims = useCachedLoading( - fetchClaims && walletAddress - ? DaoVotingNativeStakedSelectors.claimsSelector({ - chainId, - contractAddress: votingModuleAddress, - params: [{ address: walletAddress }], - }) - : constSelector(undefined), - undefined - ) - const claims = loadingClaims.loading - ? [] - : !loadingClaims.data - ? undefined - : loadingClaims.data.claims - - const claimsPending = blockHeight - ? claims?.filter((c) => !claimAvailable(c, blockHeight)) - : undefined - const claimsAvailable = blockHeight - ? claims?.filter((c) => claimAvailable(c, blockHeight)) - : undefined - const sumClaimsAvailable = claimsAvailable?.reduce( - (p, c) => p + Number(c.amount), - 0 - ) - - // Total staked value - const loadingTotalStakedValue = useCachedLoading( - fetchTotalStakedValue - ? DaoVotingNativeStakedSelectors.totalPowerAtHeightSelector({ - chainId, - contractAddress: votingModuleAddress, - params: [{}], - }) - : constSelector(undefined), - undefined - ) - - // Wallet staked value - const loadingWalletStakedValue = useCachedLoading( - fetchWalletStakedValue && walletAddress - ? DaoVotingNativeStakedSelectors.votingPowerAtHeightSelector({ - chainId, - contractAddress: votingModuleAddress, - params: [{ address: walletAddress }], - }) - : constSelector(undefined), - undefined - ) - - return { - stakingContractAddress: votingModuleAddress, - unstakingDuration: config.unstaking_duration ?? undefined, - refreshTotals, - /// Optional - // Claims - blockHeight, - refreshClaims: fetchClaims ? refreshClaims : undefined, - claims, - claimsPending, - claimsAvailable, - sumClaimsAvailable, - // Total staked value - loadingTotalStakedValue: loadingTotalStakedValue.loading - ? { loading: true } - : !loadingTotalStakedValue.data - ? undefined - : { - loading: false, - data: Number(loadingTotalStakedValue.data.power), - }, - // Wallet staked value - loadingWalletStakedValue: loadingWalletStakedValue.loading - ? { loading: true } - : !loadingWalletStakedValue.data - ? undefined - : { - loading: false, - data: Number(loadingWalletStakedValue.data.power), - }, - } -} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/index.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/index.ts deleted file mode 100644 index 5ee03e09f..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/index.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { PeopleAltOutlined, PeopleAltRounded } from '@mui/icons-material' - -import { MainDaoInfoCardsTokenLoader } from '@dao-dao/stateless' -import { - ActionCategoryKey, - ActionKey, - DaoTabId, - VotingModuleAdapter, -} from '@dao-dao/types' -import { - DAO_VOTING_NATIVE_STAKED_CONTRACT_NAMES, - DaoVotingNativeStakedAdapterId, -} from '@dao-dao/utils' - -import { MintAction, UpdateStakingConfigAction } from './actions' -import { MembersTab, ProfileCardMemberInfo, StakingModal } from './components' -import { useMainDaoInfoCards } from './hooks' - -export const DaoVotingNativeStakedAdapter: VotingModuleAdapter = { - id: DaoVotingNativeStakedAdapterId, - contractNames: DAO_VOTING_NATIVE_STAKED_CONTRACT_NAMES, - - load: () => ({ - // Hooks - hooks: { - useMainDaoInfoCards, - useVotingModuleRelevantAddresses: () => [], - }, - - // Components - components: { - MainDaoInfoCardsLoader: MainDaoInfoCardsTokenLoader, - ProfileCardMemberInfo, - StakingModal, - - extraTabs: [ - { - id: DaoTabId.Members, - labelI18nKey: 'title.members', - Component: MembersTab, - Icon: PeopleAltOutlined, - IconFilled: PeopleAltRounded, - }, - ], - }, - - // Functions - fields: { - actions: { - actions: [MintAction, UpdateStakingConfigAction], - categoryMakers: [ - // Add to DAO Governance category. - () => ({ - key: ActionCategoryKey.DaoGovernance, - actionKeys: [ActionKey.Mint, ActionKey.UpdateStakingConfig], - }), - ], - }, - }, - }), -} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/types.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/types.ts deleted file mode 100644 index b2ab2d052..000000000 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/types.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { - Duration, - DurationWithUnits, - GenericToken, - GenericTokenWithUsdPrice, - LoadingData, -} from '@dao-dao/types' -import { Claim } from '@dao-dao/types/contracts/DaoVotingNativeStaked' - -export interface DaoCreationConfig { - denom: string - _tokenError?: string - unstakingDuration: DurationWithUnits -} - -export interface UseStakingInfoOptions { - fetchClaims?: boolean - fetchTotalStakedValue?: boolean - fetchWalletStakedValue?: boolean -} - -export interface UseStakingInfoResponse { - stakingContractAddress: string - unstakingDuration?: Duration - refreshTotals: () => void - /// Optional - // Claims - blockHeight?: number - refreshClaims?: () => void - claims?: Claim[] - claimsPending?: Claim[] - claimsAvailable?: Claim[] - sumClaimsAvailable?: number - // Total staked value - loadingTotalStakedValue?: LoadingData<number> - // Wallet staked value - loadingWalletStakedValue?: LoadingData<number> -} - -export type UseGovernanceTokenInfoOptions = { - /** - * Optionally fetch wallet balance. Defaults to false. - */ - fetchWalletBalance?: boolean - /** - * Optionally fetch treasury balance. Defaults to false. - */ - fetchTreasuryBalance?: boolean - /** - * Optionally fetch USDC price. Defaults to false. - */ - fetchUsdcPrice?: boolean -} - -export type UseGovernanceTokenInfoResponse = { - /** - * The generic governance token. - */ - governanceToken: GenericToken - /** - * The supply of the governance token converted to the appropriate decimals. - */ - supply: number - - // Optional, defined if options are set to true. - - /** - * Unstaked governance token balance. Only defined if a wallet is connected - * and the option to fetch this is true. - */ - loadingWalletBalance?: LoadingData<number> - /** - * The treasury balance of the governance token. Only defined if the option to - * fetch this is true. - */ - loadingTreasuryBalance?: LoadingData<number> - /** - * The price of the governance token. Only defined if the option to fetch this - * is true. - */ - loadingPrice?: LoadingData<GenericTokenWithUsdPrice> -} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/MembersTab.tsx index e7f33143d..d3b3a8f43 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/MembersTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/MembersTab.tsx @@ -18,14 +18,14 @@ import { useVotingModuleAdapterOptions } from '../../../react/context' export const MembersTab = () => { const { t } = useTranslation() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() + const { votingModule } = useVotingModuleAdapterOptions() const token = useDaoGovernanceToken() ?? undefined const queryClient = useQueryClient() const members = useQueryLoadingDataWithError( daoVotingOnftStakedExtraQueries.topStakers(queryClient, { - chainId, - address: votingModuleAddress, + chainId: votingModule.chainId, + address: votingModule.address, }), (data) => data?.map( diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx index d8f38271c..ba8d2f7fe 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx @@ -181,8 +181,8 @@ export const ProfileCardMemberInfo = ({ data: [ { token, - staked: HugeDecimal.from(loadingWalletStakedValue.data), - unstaked: HugeDecimal.from(loadingUnstakedBalance.data), + staked: loadingWalletStakedValue.data, + unstaked: loadingUnstakedBalance.data, }, ], } @@ -195,10 +195,10 @@ export const ProfileCardMemberInfo = ({ ? { loading: true } : { loading: false, - data: - (loadingWalletStakedValue.data / - loadingTotalStakedValue.data) * - 100, + data: loadingWalletStakedValue.data + .div(loadingTotalStakedValue.data) + .times(100) + .toNumber(), } } onClaim={onClaim} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/StakingModal.tsx index 0921ddbc2..57885399c 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/StakingModal.tsx @@ -84,7 +84,7 @@ const InnerStakingModal = ({ const hasStake = loadingWalletStakedValue !== undefined && !loadingWalletStakedValue.loading && - loadingWalletStakedValue.data > 0 + loadingWalletStakedValue.data.isPositive() const walletStakedBalanceLoading = useQueryLoadingDataWithError( dao.votingModule.getVotingPowerQuery(walletAddress) diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useGovernanceCollectionInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useGovernanceCollectionInfo.ts index f9f1a81ad..2835f3868 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useGovernanceCollectionInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useGovernanceCollectionInfo.ts @@ -4,6 +4,7 @@ import { useSuspenseQuery, } from '@tanstack/react-query' +import { HugeDecimal } from '@dao-dao/math' import { daoVotingOnftStakedQueries, omniflixQueries } from '@dao-dao/state' import { TokenType } from '@dao-dao/types' @@ -19,19 +20,16 @@ export const useGovernanceCollectionInfo = ({ fetchWalletBalance = false, fetchTreasuryBalance = false, }: UseGovernanceCollectionInfoOptions = {}): UseGovernanceCollectionInfoResponse => { - const { chainId, coreAddress, votingModuleAddress } = - useVotingModuleAdapterOptions() - const { address: walletAddress } = useWallet({ - chainId, - }) + const { coreAddress, votingModule } = useVotingModuleAdapterOptions() + const { address: walletAddress } = useWallet() const queryClient = useQueryClient() const { data: { onft_collection_id }, } = useSuspenseQuery( daoVotingOnftStakedQueries.config(queryClient, { - chainId, - contractAddress: votingModuleAddress, + chainId: votingModule.chainId, + contractAddress: votingModule.address, }) ) @@ -43,11 +41,11 @@ export const useGovernanceCollectionInfo = ({ ] = useSuspenseQueries({ queries: [ omniflixQueries.onftCollectionInfo({ - chainId, + chainId: votingModule.chainId, id: onft_collection_id, }), omniflixQueries.onftCollectionSupply({ - chainId, + chainId: votingModule.chainId, id: onft_collection_id, }), ], @@ -60,7 +58,7 @@ export const useGovernanceCollectionInfo = ({ omniflixQueries.onftCollectionSupply( fetchWalletBalance && walletAddress ? { - chainId, + chainId: votingModule.chainId, id: onft_collection_id, owner: walletAddress, } @@ -73,7 +71,7 @@ export const useGovernanceCollectionInfo = ({ omniflixQueries.onftCollectionSupply( fetchTreasuryBalance ? { - chainId, + chainId: votingModule.chainId, id: onft_collection_id, owner: coreAddress, } @@ -82,22 +80,22 @@ export const useGovernanceCollectionInfo = ({ ) return { - stakingContractAddress: votingModuleAddress, + stakingContractAddress: votingModule.address, collectionAddress: onft_collection_id, collectionInfo: { name, symbol, - totalSupply, + totalSupply: HugeDecimal.from(totalSupply), }, token: { - chainId, + chainId: votingModule.chainId, type: TokenType.Onft, denomOrAddress: onft_collection_id, symbol, decimals: 0, imageUrl: previewUri, source: { - chainId, + chainId: votingModule.chainId, type: TokenType.Onft, denomOrAddress: onft_collection_id, }, @@ -111,7 +109,7 @@ export const useGovernanceCollectionInfo = ({ ? { loading: true } : { loading: false, - data: loadingWalletBalance.data, + data: HugeDecimal.from(loadingWalletBalance.data), }, // Treasury balance loadingTreasuryBalance: @@ -121,7 +119,7 @@ export const useGovernanceCollectionInfo = ({ ? { loading: true } : { loading: false, - data: loadingTreasuryBalance.data, + data: HugeDecimal.from(loadingTreasuryBalance.data), }, } } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useMainDaoInfoCards.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useMainDaoInfoCards.tsx index 0dab5d00d..7c9d00759 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useMainDaoInfoCards.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useMainDaoInfoCards.tsx @@ -16,7 +16,7 @@ import { useStakingInfo } from './useStakingInfo' export const useMainDaoInfoCards = (): DaoInfoCard[] => { const { t } = useTranslation() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() + const { votingModule } = useVotingModuleAdapterOptions() const { loadingTotalStakedValue, unstakingDuration } = useStakingInfo({ fetchTotalStakedValue: true, @@ -33,8 +33,8 @@ export const useMainDaoInfoCards = (): DaoInfoCard[] => { const queryClient = useQueryClient() const loadingMembers = useQueryLoadingDataWithError( daoVotingOnftStakedExtraQueries.topStakers(queryClient, { - chainId, - address: votingModuleAddress, + chainId: votingModule.chainId, + address: votingModule.address, }) ) @@ -66,7 +66,7 @@ export const useMainDaoInfoCards = (): DaoInfoCard[] => { value: loadingTotalStakedValue.loading ? '...' : formatPercentOf100( - (loadingTotalStakedValue.data / totalSupply) * 100 + loadingTotalStakedValue.data.div(totalSupply).times(100).toNumber() ), }, { diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useStakingInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useStakingInfo.ts index 1ef6ea285..9e2600640 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useStakingInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useStakingInfo.ts @@ -2,6 +2,7 @@ import { useQueryClient, useSuspenseQueries } from '@tanstack/react-query' import { useCallback } from 'react' import { useSetRecoilState, waitForAll } from 'recoil' +import { HugeDecimal } from '@dao-dao/math' import { blockHeightSelector, contractQueries, @@ -24,7 +25,6 @@ import { useQueryLoadingDataWithError, } from '../../../../hooks' import { useWallet } from '../../../../hooks/useWallet' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { UseStakingInfoOptions, UseStakingInfoResponse } from '../types' import { useGovernanceCollectionInfo } from './useGovernanceCollectionInfo' @@ -35,23 +35,21 @@ export const useStakingInfo = ({ fetchWalletUnstakedNfts = false, }: UseStakingInfoOptions = {}): UseStakingInfoResponse => { const { dao } = useDaoContext() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() - const { address: walletAddress } = useWallet({ - chainId, - }) - + const { address: walletAddress } = useWallet() const { collectionAddress } = useGovernanceCollectionInfo() - const queryClient = useQueryClient() + + const { votingModule } = dao + const [stakingContractVersion, unstakingDuration] = useSuspenseQueries({ queries: [ contractQueries.info(queryClient, { - chainId, - address: votingModuleAddress, + chainId: votingModule.chainId, + address: votingModule.address, }), daoVotingOnftStakedQueries.config(queryClient, { - chainId, - contractAddress: votingModuleAddress, + chainId: votingModule.chainId, + contractAddress: votingModule.address, }), ], combine: ([ @@ -86,7 +84,7 @@ export const useStakingInfo = ({ }) queryClient.invalidateQueries({ queryKey: omniflixQueries.onftCollectionSupply({ - chainId, + chainId: votingModule.chainId, id: collectionAddress, }).queryKey, }) @@ -95,7 +93,7 @@ export const useStakingInfo = ({ 'omniflix', 'paginatedOnfts', { - chainId, + chainId: votingModule.chainId, id: collectionAddress, }, ], @@ -105,7 +103,7 @@ export const useStakingInfo = ({ 'omniflix', 'allOnfts', { - chainId, + chainId: votingModule.chainId, id: collectionAddress, }, ], @@ -115,8 +113,8 @@ export const useStakingInfo = ({ 'indexer', 'query', { - chainId, - contractAddress: votingModuleAddress, + chainId: votingModule.chainId, + contractAddress: votingModule.address, formula: 'daoVotingOnftStaked/topStakers', }, ], @@ -128,8 +126,8 @@ export const useStakingInfo = ({ 'indexer', 'query', { - chainId, - contractAddress: votingModuleAddress, + chainId: votingModule.chainId, + contractAddress: votingModule.address, formula: 'daoVotingOnftStaked/stakedNfts', args: { address: walletAddress, @@ -140,20 +138,19 @@ export const useStakingInfo = ({ // Then invalidate contract query that uses indexer query. queryClient.invalidateQueries({ queryKey: daoVotingOnftStakedQueryKeys.stakedNfts( - chainId, - votingModuleAddress, + votingModule.chainId, + votingModule.address, { address: walletAddress, } ), }) }, [ - chainId, + votingModule, dao, collectionAddress, queryClient, setRefreshDaoVotingPower, - votingModuleAddress, walletAddress, ]) @@ -163,7 +160,7 @@ export const useStakingInfo = ({ const blockHeightLoadable = useCachedLoadable( fetchClaims ? blockHeightSelector({ - chainId, + chainId: votingModule.chainId, }) : undefined ) @@ -177,8 +174,8 @@ export const useStakingInfo = ({ 'indexer', 'query', { - chainId, - contractAddress: votingModuleAddress, + chainId: votingModule.chainId, + contractAddress: votingModule.address, formula: 'daoVotingOnftStaked/nftClaims', args: { address: walletAddress, @@ -189,19 +186,19 @@ export const useStakingInfo = ({ // Then invalidate contract query that uses indexer query. queryClient.invalidateQueries({ queryKey: daoVotingOnftStakedQueryKeys.nftClaims( - chainId, - votingModuleAddress, + votingModule.chainId, + votingModule.address, { address: walletAddress, } ), }) - }, [chainId, queryClient, votingModuleAddress, walletAddress]) + }, [votingModule, queryClient, walletAddress]) const loadingClaims = useQueryLoadingData( daoVotingOnftStakedQueries.nftClaims(queryClient, { - chainId, - contractAddress: votingModuleAddress, + chainId: votingModule.chainId, + contractAddress: votingModule.address, args: { address: walletAddress || '', }, @@ -226,7 +223,7 @@ export const useStakingInfo = ({ const claimsAvailable = blockHeight ? nftClaims?.filter((c) => claimAvailable(c, blockHeight)) : undefined - const sumClaimsAvailable = claimsAvailable?.length + const sumClaimsAvailable = HugeDecimal.from(claimsAvailable?.length || 0) // Total staked value const loadingTotalStakedValue = useQueryLoadingDataWithError({ @@ -237,8 +234,8 @@ export const useStakingInfo = ({ // Wallet staked value const loadingWalletStakedNftIds = useQueryLoadingDataWithError( daoVotingOnftStakedQueries.stakedNfts(queryClient, { - chainId, - contractAddress: votingModuleAddress, + chainId: votingModule.chainId, + contractAddress: votingModule.address, args: { address: walletAddress ?? '', }, @@ -253,7 +250,7 @@ export const useStakingInfo = ({ ? waitForAll( loadingWalletStakedNftIds.data.map((tokenId) => nftCardInfoSelector({ - chainId, + chainId: votingModule.chainId, collection: collectionAddress, tokenId, }) @@ -264,7 +261,7 @@ export const useStakingInfo = ({ const loadingWalletUnstakedOnfts = useQueryLoadingDataWithError({ ...omniflixQueries.allOnfts(queryClient, { - chainId, + chainId: votingModule.chainId, id: collectionAddress, owner: walletAddress ?? '', }), @@ -276,7 +273,7 @@ export const useStakingInfo = ({ ? waitForAll( loadingWalletUnstakedOnfts.data.map(({ id }) => nftCardInfoSelector({ - chainId, + chainId: votingModule.chainId, collection: collectionAddress, tokenId: id, }) @@ -287,7 +284,7 @@ export const useStakingInfo = ({ return { stakingContractVersion, - stakingContractAddress: votingModuleAddress, + stakingContractAddress: votingModule.address, unstakingDuration, refreshTotals, /// Optional @@ -309,7 +306,7 @@ export const useStakingInfo = ({ ? { loading: true } : { loading: false, - data: Number(loadingTotalStakedValue.data.power), + data: HugeDecimal.from(loadingTotalStakedValue.data.power), }, // Wallet staked value loadingWalletStakedValue: @@ -319,7 +316,7 @@ export const useStakingInfo = ({ ? { loading: true } : { loading: false, - data: loadingWalletStakedNftIds.data.length, + data: HugeDecimal.from(loadingWalletStakedNftIds.data.length), }, loadingWalletStakedNfts, loadingWalletUnstakedNfts, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/types.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/types.ts index 67b25e23b..a5e89eb65 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/types.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/types.ts @@ -1,3 +1,4 @@ +import { HugeDecimal } from '@dao-dao/math' import { ContractVersion, Duration, @@ -27,11 +28,11 @@ export interface UseStakingInfoResponse { claims?: NftClaim[] claimsPending?: NftClaim[] claimsAvailable?: NftClaim[] - sumClaimsAvailable?: number + sumClaimsAvailable?: HugeDecimal // Total staked value - loadingTotalStakedValue?: LoadingData<number> + loadingTotalStakedValue?: LoadingData<HugeDecimal> // Wallet staked value - loadingWalletStakedValue?: LoadingData<number> + loadingWalletStakedValue?: LoadingData<HugeDecimal> loadingWalletStakedNfts?: LoadingDataWithError<NftCardInfo[]> loadingWalletUnstakedNfts?: LoadingDataWithError<NftCardInfo[]> } @@ -48,14 +49,14 @@ export interface UseGovernanceCollectionInfoResponse { collectionInfo: { name: string symbol: string - totalSupply: number + totalSupply: HugeDecimal } token: GenericToken /// Optional // Wallet balance - loadingWalletBalance?: LoadingData<number> + loadingWalletBalance?: LoadingData<HugeDecimal> // Treasury balance - loadingTreasuryBalance?: LoadingData<number> + loadingTreasuryBalance?: LoadingData<HugeDecimal> // Price // loadingPrice?: LoadingData<GenericTokenWithUsdPrice> } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/components/MembersTab.tsx index aa346a36b..747bd0d2f 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/components/MembersTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/components/MembersTab.tsx @@ -15,13 +15,13 @@ import { useVotingModuleAdapterOptions } from '../../../react/context' export const MembersTab = () => { const { t } = useTranslation() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() + const { votingModule } = useVotingModuleAdapterOptions() const queryClient = useQueryClient() const members = useQueryLoadingDataWithError( daoVotingSgCommunityNftExtraQueries.allVoters(queryClient, { - chainId, - address: votingModuleAddress, + chainId: votingModule.chainId, + address: votingModule.address, }), (data) => data?.map( diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/hooks/useMainDaoInfoCards.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/hooks/useMainDaoInfoCards.ts index 49ed2e8a8..5ddb7c0fd 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/hooks/useMainDaoInfoCards.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/hooks/useMainDaoInfoCards.ts @@ -9,13 +9,13 @@ import { useVotingModuleAdapterOptions } from '../../../react/context' export const useMainDaoInfoCards = (): DaoInfoCard[] => { const { t } = useTranslation() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() + const { votingModule } = useVotingModuleAdapterOptions() const queryClient = useQueryClient() const loadingMembers = useQueryLoadingDataWithError( daoVotingSgCommunityNftExtraQueries.allVoters(queryClient, { - chainId, - address: votingModuleAddress, + chainId: votingModule.chainId, + address: votingModule.address, }) ) diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/README.md b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/README.md index e7d3e936b..bb8bed8f2 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/README.md +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/README.md @@ -4,12 +4,18 @@ This is the voting module adapter for the [`dao-voting-token-staked`](https://github.com/DA0-DA0/dao-contracts/tree/95e4f73a170705deeb3689341c51f3a3b126e3e9/contracts/voting/dao-voting-token-staked) contract, which determines DAO voting power based on the staked balance of a native token (IBC, factory, or otherwise), such as `JUNO` or -`factory/junoContract/subdenom`. This is an alternative to the +`factory/junoContract/subdenom`. + +This is an alternative to the [CW20](https://docs.cosmwasm.com/cw-plus/0.9.0/cw20/spec) governance token-based DAO structure, where members are still free to exchange their unstaked tokens with other parties at any time. However, it uses a native token instead of a [CW20](https://docs.cosmwasm.com/cw-plus/0.9.0/cw20/spec) token. +This also supports the deprecated +[`dao-voting-native-staked`](https://github.com/DA0-DA0/dao-contracts/tree/7f89ad1604e8022f202aef729853b0c8c7196988/contracts/voting/dao-voting-native-staked) +contract. + ## Layout | Location | Summary | diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.stories.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint/MintComponent.stories.tsx similarity index 94% rename from packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.stories.tsx rename to packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint/MintComponent.stories.tsx index 5225850a8..e5886bacb 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.stories.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint/MintComponent.stories.tsx @@ -7,7 +7,7 @@ import { MintComponent, MintData } from './MintComponent' export default { title: - 'DAO DAO / packages / stateful / voting-module-adapter / adapters / DaoVotingNativeStaked / actions / Mint', + 'DAO DAO / packages / stateful / voting-module-adapter / adapters / DaoVotingTokenStaked / actions / DaoVotingNativeStakedMint', component: MintComponent, decorators: [ makeReactHookFormDecorator<MintData>({ diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint/MintComponent.tsx similarity index 100% rename from packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/MintComponent.tsx rename to packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint/MintComponent.tsx diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/README.md b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint/README.md similarity index 78% rename from packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/README.md rename to packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint/README.md index ebb1e7824..b037b7d41 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/README.md +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint/README.md @@ -1,6 +1,6 @@ # Mint -Mint new governance tokens. +Mint new governance tokens for the dao-voting-native-staked module. ## Bulk import format @@ -15,7 +15,6 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). ```json { - "to": "<RECIPIENT ADDRESS>", "amount": "<AMOUNT>" } ``` diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/index.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint/index.tsx similarity index 97% rename from packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/index.tsx rename to packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint/index.tsx index 4846f19ee..17dd8e04e 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingNativeStaked/actions/Mint/index.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingNativeStakedMint/index.tsx @@ -37,7 +37,7 @@ const Component: ActionComponent = (props) => { ) } -export class MintAction extends ActionBase<MintData> { +export class DaoVotingNativeStakedMintAction extends ActionBase<MintData> { public readonly key = ActionKey.Mint public readonly Component = Component diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/BitSongFantokenMintAction.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/BitSongFantokenMintAction.ts similarity index 100% rename from packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/BitSongFantokenMintAction.ts rename to packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/BitSongFantokenMintAction.ts diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/Component.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/Component.tsx similarity index 100% rename from packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/Component.tsx rename to packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/Component.tsx diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintAction.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/MintAction.ts similarity index 98% rename from packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintAction.ts rename to packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/MintAction.ts index f15c83d4c..b711cf560 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintAction.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/MintAction.ts @@ -20,7 +20,7 @@ import { MintData } from './MintComponent' /** * A mint action for tokenfactory tokens. */ -export class MintAction extends ActionBase<MintData> { +export class DaoVotingTokenStakedMintAction extends ActionBase<MintData> { public readonly key = ActionKey.Mint public readonly Component = Component diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.stories.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/MintComponent.stories.tsx similarity index 94% rename from packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.stories.tsx rename to packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/MintComponent.stories.tsx index a0af43982..7bcc9d1b9 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.stories.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/MintComponent.stories.tsx @@ -8,7 +8,7 @@ import { MintComponent, MintData } from './MintComponent' export default { title: - 'DAO DAO / packages / stateful / voting-module-adapter / adapters / DaoVotingTokenStaked / actions / Mint', + 'DAO DAO / packages / stateful / voting-module-adapter / adapters / DaoVotingTokenStaked / actions / DaoVotingTokenStakedMint', component: MintComponent, decorators: [ makeReactHookFormDecorator<MintData>({ diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/MintComponent.tsx similarity index 100% rename from packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/MintComponent.tsx rename to packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/MintComponent.tsx diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/README.md b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/README.md similarity index 81% rename from packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/README.md rename to packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/README.md index 101c63406..198ec9f71 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/README.md +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/README.md @@ -1,6 +1,6 @@ # Mint -Mint new governance tokens. +Mint new governance tokens for the dao-voting-token-staked module. ## Bulk import format diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/index.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/index.ts similarity index 100% rename from packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/Mint/index.ts rename to packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/DaoVotingTokenStakedMint/index.ts diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/UpdateStakingConfig/README.md b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/UpdateStakingConfig/README.md index 74171ed2c..79c2dc74c 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/UpdateStakingConfig/README.md +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/UpdateStakingConfig/README.md @@ -17,7 +17,7 @@ guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions). { "unstakingDurationEnabled": <true | false>, "unstakingDuration": { - "value": "<NUMBER>", + "value": <NUMBER>, "units": "<seconds | minutes | hours | days | weeks | months | years>" } } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/index.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/index.ts index 40591c371..51fb505b1 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/index.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/actions/index.ts @@ -1,2 +1,3 @@ -export * from './Mint' +export * from './DaoVotingNativeStakedMint' +export * from './DaoVotingTokenStakedMint' export * from './UpdateStakingConfig' diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/MembersTab.tsx index 28b9ee368..48df76de1 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/MembersTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/MembersTab.tsx @@ -6,6 +6,7 @@ import { indexerQueries } from '@dao-dao/state/query' import { MembersTab as StatelessMembersTab } from '@dao-dao/stateless' import { StatefulDaoMemberCardProps } from '@dao-dao/types' +import { TokenStakedVotingModule } from '../../../../clients' import { ButtonLink, DaoMemberCard, @@ -17,15 +18,18 @@ import { useGovernanceTokenInfo } from '../hooks/useGovernanceTokenInfo' export const MembersTab = () => { const { t } = useTranslation() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() + const { votingModule } = useVotingModuleAdapterOptions() const { governanceToken } = useGovernanceTokenInfo() const queryClient = useQueryClient() const members = useQueryLoadingDataWithError( indexerQueries.queryContract(queryClient, { - chainId, - contractAddress: votingModuleAddress, - formula: 'daoVotingTokenStaked/topStakers', + chainId: votingModule.chainId, + contractAddress: votingModule.address, + formula: + votingModule instanceof TokenStakedVotingModule + ? 'daoVotingTokenStaked/topStakers' + : 'daoVotingNativeStaked/topStakers', noFallback: true, }), (data) => diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx index b820a0827..ef9d8b190 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx @@ -37,14 +37,12 @@ export const ProfileCardMemberInfo = ({ }: BaseProfileCardMemberInfoProps) => { const { t } = useTranslation() const { name: daoName } = useDaoInfoContext() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() + const { votingModule } = useVotingModuleAdapterOptions() const { address: walletAddress, isWalletConnected, refreshBalances, - } = useWallet({ - chainId, - }) + } = useWallet() const [showStakingModal, setShowStakingModal] = useState(false) const [claimingLoading, setClaimingLoading] = useState(false) @@ -60,7 +58,7 @@ export const ProfileCardMemberInfo = ({ refreshTotals, claimsPending, claimsAvailable, - sumClaimsAvailable, + sumClaimsAvailable = HugeDecimal.zero, loadingWalletStakedValue, loadingTotalStakedValue, refreshClaims, @@ -71,7 +69,7 @@ export const ProfileCardMemberInfo = ({ }) const doClaim = DaoVotingTokenStakedHooks.useClaim({ - contractAddress: votingModuleAddress, + contractAddress: votingModule.address, sender: walletAddress ?? '', }) @@ -80,7 +78,7 @@ export const ProfileCardMemberInfo = ({ if (!isWalletConnected) { return toast.error(t('error.logInToContinue')) } - if (!sumClaimsAvailable) { + if (sumClaimsAvailable.isZero()) { return toast.error(t('error.noClaimsAvailable')) } @@ -97,9 +95,7 @@ export const ProfileCardMemberInfo = ({ toast.success( t('success.claimedTokens', { - amount: HugeDecimal.from( - sumClaimsAvailable - ).toInternationalizedHumanReadableString({ + amount: sumClaimsAvailable.toInternationalizedHumanReadableString({ decimals: governanceToken.decimals, }), tokenSymbol: governanceToken.symbol, @@ -126,12 +122,12 @@ export const ProfileCardMemberInfo = ({ const blockHeightLoadable = useCachedLoadable( blockHeightSelector({ - chainId, + chainId: votingModule.chainId, }) ) const blocksPerYear = useRecoilValue( blocksPerYearSelector({ - chainId, + chainId: votingModule.chainId, }) ) @@ -180,8 +176,8 @@ export const ProfileCardMemberInfo = ({ data: [ { token: governanceToken, - staked: HugeDecimal.from(loadingWalletStakedValue.data), - unstaked: HugeDecimal.from(loadingUnstakedBalance.data), + staked: loadingWalletStakedValue.data, + unstaked: loadingUnstakedBalance.data, }, ], } @@ -194,10 +190,10 @@ export const ProfileCardMemberInfo = ({ ? { loading: true } : { loading: false, - data: - (loadingWalletStakedValue.data / - loadingTotalStakedValue.data) * - 100, + data: loadingWalletStakedValue.data + .div(loadingTotalStakedValue.data) + .times(100) + .toNumber(), } } onClaim={onClaim} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx index 830798553..148768d90 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx @@ -45,7 +45,7 @@ const InnerStakingModal = ({ isWalletConnected, refreshBalances, } = useWallet() - const { coreAddress, votingModuleAddress } = useVotingModuleAdapterOptions() + const { coreAddress, votingModule } = useVotingModuleAdapterOptions() const [stakingLoading, setStakingLoading] = useRecoilState(stakingLoadingAtom) @@ -56,7 +56,7 @@ const InnerStakingModal = ({ const { unstakingDuration, refreshTotals, - sumClaimsAvailable, + sumClaimsAvailable = HugeDecimal.zero, loadingWalletStakedValue, refreshClaims, } = useStakingInfo({ @@ -67,15 +67,15 @@ const InnerStakingModal = ({ const [amount, setAmount] = useState(HugeDecimal.zero) const doStake = DaoVotingTokenStakedHooks.useStake({ - contractAddress: votingModuleAddress, + contractAddress: votingModule.address, sender: walletAddress ?? '', }) const doUnstake = DaoVotingTokenStakedHooks.useUnstake({ - contractAddress: votingModuleAddress, + contractAddress: votingModule.address, sender: walletAddress ?? '', }) const doClaim = DaoVotingTokenStakedHooks.useClaim({ - contractAddress: votingModuleAddress, + contractAddress: votingModule.address, sender: walletAddress ?? '', }) @@ -174,7 +174,7 @@ const InnerStakingModal = ({ break } case StakingMode.Claim: { - if (sumClaimsAvailable === 0) { + if (sumClaimsAvailable.isZero()) { toast.error(t('error.noClaimsAvailable')) return } @@ -194,11 +194,11 @@ const InnerStakingModal = ({ toast.success( t('success.claimedTokens', { - amount: HugeDecimal.from( - sumClaimsAvailable || 0 - ).toInternationalizedHumanReadableString({ - decimals: governanceToken.decimals, - }), + amount: sumClaimsAvailable.toInternationalizedHumanReadableString( + { + decimals: governanceToken.decimals, + } + ), tokenSymbol: governanceToken.symbol, }) ) @@ -222,7 +222,7 @@ const InnerStakingModal = ({ return ( <StatelessStakingModal amount={amount} - claimableTokens={HugeDecimal.from(sumClaimsAvailable || 0)} + claimableTokens={sumClaimsAvailable} error={isWalletConnected ? undefined : t('error.logInToContinue')} initialMode={initialMode} loading={stakingLoading} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useGovernanceTokenInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useGovernanceTokenInfo.ts index 7b5cc6845..87ca3b6f0 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useGovernanceTokenInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useGovernanceTokenInfo.ts @@ -2,6 +2,7 @@ import { constSelector, useRecoilValue, waitForAll } from 'recoil' import { HugeDecimal } from '@dao-dao/math' import { + DaoVotingNativeStakedSelectors, DaoVotingTokenStakedSelectors, genericTokenSelector, nativeDenomBalanceSelector, @@ -11,6 +12,7 @@ import { import { useCachedLoading } from '@dao-dao/stateless' import { TokenType } from '@dao-dao/types' +import { TokenStakedVotingModule } from '../../../../clients' import { useWallet } from '../../../../hooks/useWallet' import { useVotingModuleAdapterOptions } from '../../../react/context' import { @@ -23,18 +25,25 @@ export const useGovernanceTokenInfo = ({ fetchTreasuryBalance = false, fetchUsdcPrice = false, }: UseGovernanceTokenInfoOptions = {}): UseGovernanceTokenInfoResponse => { - const { chainId, coreAddress, votingModuleAddress } = - useVotingModuleAdapterOptions() + const { chainId, coreAddress, votingModule } = useVotingModuleAdapterOptions() const { address: walletAddress } = useWallet({ chainId, }) + const isTokenStaked = votingModule instanceof TokenStakedVotingModule + const { denom } = useRecoilValue( - DaoVotingTokenStakedSelectors.denomSelector({ - chainId, - contractAddress: votingModuleAddress, - params: [], - }) + isTokenStaked + ? DaoVotingTokenStakedSelectors.denomSelector({ + chainId, + contractAddress: votingModule.address, + params: [], + }) + : DaoVotingNativeStakedSelectors.getConfigSelector({ + chainId, + contractAddress: votingModule.address, + params: [], + }) ) const [governanceToken, supply, tokenFactoryIssuerAddress] = useRecoilValue( @@ -48,12 +57,14 @@ export const useGovernanceTokenInfo = ({ chainId, denom, }), - DaoVotingTokenStakedSelectors.validatedTokenfactoryIssuerContractSelector( - { - contractAddress: votingModuleAddress, - chainId, - } - ), + isTokenStaked + ? DaoVotingTokenStakedSelectors.validatedTokenfactoryIssuerContractSelector( + { + chainId, + contractAddress: votingModule.address, + } + ) + : constSelector(undefined), ]) ) @@ -98,9 +109,7 @@ export const useGovernanceTokenInfo = ({ return { tokenFactoryIssuerAddress, governanceToken, - supply: HugeDecimal.from(supply).toHumanReadableNumber( - governanceToken.decimals - ), + supply: HugeDecimal.from(supply), /// Optional // Wallet balance loadingWalletBalance: loadingWalletBalance.loading @@ -109,7 +118,7 @@ export const useGovernanceTokenInfo = ({ ? undefined : { loading: false, - data: Number(loadingWalletBalance.data.amount), + data: HugeDecimal.from(loadingWalletBalance.data.amount), }, // Treasury balance loadingTreasuryBalance: loadingTreasuryBalance.loading @@ -118,7 +127,7 @@ export const useGovernanceTokenInfo = ({ ? undefined : { loading: false, - data: Number(loadingTreasuryBalance.data.amount), + data: HugeDecimal.from(loadingTreasuryBalance.data.amount), }, // Price loadingPrice: loadingPrice.loading diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useMainDaoInfoCards.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useMainDaoInfoCards.tsx index b97491378..165cd571c 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useMainDaoInfoCards.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useMainDaoInfoCards.tsx @@ -10,6 +10,7 @@ import { isSecretNetwork, } from '@dao-dao/utils' +import { TokenStakedVotingModule } from '../../../../clients' import { useQueryLoadingDataWithError } from '../../../../hooks' import { useVotingModuleAdapterOptions } from '../../../react/context' import { useGovernanceTokenInfo } from './useGovernanceTokenInfo' @@ -17,7 +18,7 @@ import { useStakingInfo } from './useStakingInfo' export const useMainDaoInfoCards = (): DaoInfoCard[] => { const { t } = useTranslation() - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() + const { chainId, votingModule } = useVotingModuleAdapterOptions() const { loadingTotalStakedValue, unstakingDuration } = useStakingInfo({ fetchTotalStakedValue: true, }) @@ -35,8 +36,11 @@ export const useMainDaoInfoCards = (): DaoInfoCard[] => { const loadingMembers = useQueryLoadingDataWithError( indexerQueries.queryContract(queryClient, { chainId, - contractAddress: votingModuleAddress, - formula: 'daoVotingTokenStaked/topStakers', + contractAddress: votingModule.address, + formula: + votingModule instanceof TokenStakedVotingModule + ? 'daoVotingTokenStaked/topStakers' + : 'daoVotingNativeStaked/topStakers', noFallback: true, }) ) diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useStakingInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useStakingInfo.ts index ba62dd72f..c38d47604 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useStakingInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useStakingInfo.ts @@ -1,15 +1,20 @@ +import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query' import { useCallback } from 'react' -import { constSelector, useRecoilValue, useSetRecoilState } from 'recoil' +import { constSelector, useSetRecoilState } from 'recoil' +import { HugeDecimal } from '@dao-dao/math' import { + DaoVotingNativeStakedSelectors, DaoVotingTokenStakedSelectors, blockHeightSelector, + daoVotingTokenStakedQueries, refreshClaimsIdAtom, refreshWalletBalancesIdAtom, } from '@dao-dao/state' import { useCachedLoadable, useCachedLoading } from '@dao-dao/stateless' import { claimAvailable } from '@dao-dao/utils' +import { TokenStakedVotingModule } from '../../../../clients' import { useWallet } from '../../../../hooks/useWallet' import { useVotingModuleAdapterOptions } from '../../../react/context' import { UseStakingInfoOptions, UseStakingInfoResponse } from '../types' @@ -19,16 +24,14 @@ export const useStakingInfo = ({ fetchTotalStakedValue = false, fetchWalletStakedValue = false, }: UseStakingInfoOptions = {}): UseStakingInfoResponse => { - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() - const { address: walletAddress } = useWallet({ - chainId, - }) + const { votingModule } = useVotingModuleAdapterOptions() + const { address: walletAddress } = useWallet() + const queryClient = useQueryClient() - const config = useRecoilValue( - DaoVotingTokenStakedSelectors.getConfigSelector({ - chainId, - contractAddress: votingModuleAddress, - params: [], + const { data: config } = useSuspenseQuery( + daoVotingTokenStakedQueries.getConfig(queryClient, { + chainId: votingModule.chainId, + contractAddress: votingModule.address, }) ) @@ -47,7 +50,7 @@ export const useStakingInfo = ({ const blockHeightLoadable = useCachedLoadable( fetchClaims ? blockHeightSelector({ - chainId, + chainId: votingModule.chainId, }) : undefined ) @@ -62,8 +65,8 @@ export const useStakingInfo = ({ const loadingClaims = useCachedLoading( fetchClaims && walletAddress ? DaoVotingTokenStakedSelectors.claimsSelector({ - chainId, - contractAddress: votingModuleAddress, + chainId: votingModule.chainId, + contractAddress: votingModule.address, params: [{ address: walletAddress }], }) : constSelector(undefined), @@ -82,16 +85,19 @@ export const useStakingInfo = ({ ? claims?.filter((c) => claimAvailable(c, blockHeight)) : undefined const sumClaimsAvailable = claimsAvailable?.reduce( - (p, c) => p + Number(c.amount), - 0 + (sum, c) => sum.plus(c.amount), + HugeDecimal.zero ) // Total staked value const loadingTotalStakedValue = useCachedLoading( fetchTotalStakedValue - ? DaoVotingTokenStakedSelectors.totalPowerAtHeightSelector({ - chainId, - contractAddress: votingModuleAddress, + ? (votingModule instanceof TokenStakedVotingModule + ? DaoVotingTokenStakedSelectors + : DaoVotingNativeStakedSelectors + ).totalPowerAtHeightSelector({ + chainId: votingModule.chainId, + contractAddress: votingModule.address, params: [{}], }) : constSelector(undefined), @@ -101,9 +107,12 @@ export const useStakingInfo = ({ // Wallet staked value const loadingWalletStakedValue = useCachedLoading( fetchWalletStakedValue && walletAddress - ? DaoVotingTokenStakedSelectors.votingPowerAtHeightSelector({ - chainId, - contractAddress: votingModuleAddress, + ? (votingModule instanceof TokenStakedVotingModule + ? DaoVotingTokenStakedSelectors + : DaoVotingNativeStakedSelectors + ).votingPowerAtHeightSelector({ + chainId: votingModule.chainId, + contractAddress: votingModule.address, params: [{ address: walletAddress }], }) : constSelector(undefined), @@ -111,7 +120,7 @@ export const useStakingInfo = ({ ) return { - stakingContractAddress: votingModuleAddress, + stakingContractAddress: votingModule.address, unstakingDuration: config.unstaking_duration ?? undefined, refreshTotals, /// Optional @@ -129,7 +138,7 @@ export const useStakingInfo = ({ ? undefined : { loading: false, - data: Number(loadingTotalStakedValue.data.power), + data: HugeDecimal.from(loadingTotalStakedValue.data.power), }, // Wallet staked value loadingWalletStakedValue: loadingWalletStakedValue.loading @@ -138,7 +147,7 @@ export const useStakingInfo = ({ ? undefined : { loading: false, - data: Number(loadingWalletStakedValue.data.power), + data: HugeDecimal.from(loadingWalletStakedValue.data.power), }, } } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/index.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/index.ts index 45931a716..801b140c9 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/index.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/index.ts @@ -9,14 +9,17 @@ import { VotingModuleAdapter, } from '@dao-dao/types' import { + DAO_VOTING_NATIVE_STAKED_CONTRACT_NAMES, DAO_VOTING_TOKEN_STAKED_CONTRACT_NAMES, DaoVotingTokenStakedAdapterId, isSecretNetwork, } from '@dao-dao/utils' +import { TokenStakedVotingModule } from '../../../clients' import { BitSongFantokenMintAction, - MintAction, + DaoVotingNativeStakedMintAction, + DaoVotingTokenStakedMintAction, UpdateStakingConfigAction, } from './actions' import { MembersTab, ProfileCardMemberInfo, StakingModal } from './components' @@ -24,9 +27,12 @@ import { useMainDaoInfoCards } from './hooks' export const DaoVotingTokenStakedAdapter: VotingModuleAdapter = { id: DaoVotingTokenStakedAdapterId, - contractNames: DAO_VOTING_TOKEN_STAKED_CONTRACT_NAMES, + contractNames: [ + ...DAO_VOTING_TOKEN_STAKED_CONTRACT_NAMES, + ...DAO_VOTING_NATIVE_STAKED_CONTRACT_NAMES, + ], - load: ({ chainId }) => ({ + load: ({ chainId, votingModule }) => ({ // Hooks hooks: { useMainDaoInfoCards, @@ -60,7 +66,11 @@ export const DaoVotingTokenStakedAdapter: VotingModuleAdapter = { ...(chainId === ChainId.BitsongMainnet || chainId === ChainId.BitsongTestnet ? [BitSongFantokenMintAction] - : [MintAction]), + : [ + votingModule instanceof TokenStakedVotingModule + ? DaoVotingTokenStakedMintAction + : DaoVotingNativeStakedMintAction, + ]), UpdateStakingConfigAction, ], categoryMakers: [ diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/types.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/types.ts index 54c794113..3d1e7548e 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/types.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/types.ts @@ -1,3 +1,4 @@ +import { HugeDecimal } from '@dao-dao/math' import { Duration, DurationWithUnits, @@ -30,11 +31,11 @@ export interface UseStakingInfoResponse { claims?: Claim[] claimsPending?: Claim[] claimsAvailable?: Claim[] - sumClaimsAvailable?: number + sumClaimsAvailable?: HugeDecimal // Total staked value - loadingTotalStakedValue?: LoadingData<number> + loadingTotalStakedValue?: LoadingData<HugeDecimal> // Wallet staked value - loadingWalletStakedValue?: LoadingData<number> + loadingWalletStakedValue?: LoadingData<HugeDecimal> } export type UseGovernanceTokenInfoOptions = { @@ -55,7 +56,8 @@ export type UseGovernanceTokenInfoOptions = { export type UseGovernanceTokenInfoResponse = { /** * The token factory issuer contract address, if the governance token is a - * token factory denom and a token factory issuer contract exists. + * token factory denom and a token factory issuer contract exists. This will + * always be undefined for the deprecated dao-voting-native-staked module. */ tokenFactoryIssuerAddress: string | undefined /** @@ -65,7 +67,7 @@ export type UseGovernanceTokenInfoResponse = { /** * The supply of the governance token converted to the appropriate decimals. */ - supply: number + supply: HugeDecimal // Optional, defined if options are set to true. @@ -73,12 +75,12 @@ export type UseGovernanceTokenInfoResponse = { * Unstaked governance token balance. Only defined if a wallet is connected * and the option to fetch this is true. */ - loadingWalletBalance?: LoadingData<number> + loadingWalletBalance?: LoadingData<HugeDecimal> /** * The treasury balance of the governance token. Only defined if the option to * fetch this is true. */ - loadingTreasuryBalance?: LoadingData<number> + loadingTreasuryBalance?: LoadingData<HugeDecimal> /** * The price of the governance token. Only defined if the option to fetch this * is true. diff --git a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useVotingModule.ts b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useVotingModule.ts index 7811d37a4..d18c6702c 100644 --- a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useVotingModule.ts +++ b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useVotingModule.ts @@ -14,18 +14,18 @@ export type UseVotingModuleReturn = { } export const useVotingModule = (): UseVotingModuleReturn => { - const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions() + const { votingModule } = useVotingModuleAdapterOptions() const queryClient = useQueryClient() const loadingVaults = useQueryLoadingDataWithError( neutronVotingRegistryExtraQueries.vaultsWithInfo(queryClient, { - chainId, - address: votingModuleAddress, + chainId: votingModule.chainId, + address: votingModule.address, }) ) return { - votingRegistryAddress: votingModuleAddress, + votingRegistryAddress: votingModule.address, loadingVaults, } } diff --git a/packages/stateful/voting-module-adapter/adapters/index.ts b/packages/stateful/voting-module-adapter/adapters/index.ts index 0a98fcc4b..90f603ed4 100644 --- a/packages/stateful/voting-module-adapter/adapters/index.ts +++ b/packages/stateful/voting-module-adapter/adapters/index.ts @@ -1,7 +1,6 @@ export * from './DaoVotingCw4' export * from './DaoVotingCw20Staked' export * from './DaoVotingCw721Staked' -export * from './DaoVotingNativeStaked' export * from './DaoVotingOnftStaked' export * from './DaoVotingSgCommunityNft' export * from './DaoVotingTokenStaked' diff --git a/packages/stateful/voting-module-adapter/core.ts b/packages/stateful/voting-module-adapter/core.ts index 902911d39..746eeb977 100644 --- a/packages/stateful/voting-module-adapter/core.ts +++ b/packages/stateful/voting-module-adapter/core.ts @@ -9,7 +9,6 @@ import { DaoVotingCw20StakedAdapter, DaoVotingCw4Adapter, DaoVotingCw721StakedAdapter, - DaoVotingNativeStakedAdapter, DaoVotingOnftStakedAdapter, DaoVotingSgCommunityNftAdapter, DaoVotingTokenStakedAdapter, @@ -30,7 +29,6 @@ export const getAdapters = (): readonly VotingModuleAdapter[] => [ DaoVotingCw4Adapter, DaoVotingCw20StakedAdapter, DaoVotingCw721StakedAdapter, - DaoVotingNativeStakedAdapter, DaoVotingOnftStakedAdapter, DaoVotingSgCommunityNftAdapter, DaoVotingTokenStakedAdapter, @@ -42,9 +40,7 @@ export const getAdapterById = (id: string) => export const matchAdapter = (contractNameToMatch: string) => getAdapters().find((adapter) => - adapter.contractNames.some( - (contractName) => contractNameToMatch === contractName - ) + adapter.contractNames.includes(contractNameToMatch) ) || FallbackAdapter export const matchAndLoadAdapter = ( @@ -64,8 +60,8 @@ export const matchAndLoadAdapter = ( const options: IVotingModuleAdapterOptions = { chainId: dao.chainId, - votingModuleAddress: dao.info.votingModuleAddress, coreAddress: dao.coreAddress, + votingModule: dao.votingModule, } return { diff --git a/packages/types/clients/proposal-module.ts b/packages/types/clients/proposal-module.ts index fb05aeb91..7ceb55801 100644 --- a/packages/types/clients/proposal-module.ts +++ b/packages/types/clients/proposal-module.ts @@ -25,6 +25,11 @@ export interface IProposalModuleBase< */ info: ProposalModuleInfo + /** + * Chain ID of the proposal module. + */ + chainId: string + /** * Contract address. */ diff --git a/packages/types/clients/voting-module.ts b/packages/types/clients/voting-module.ts index c8496c8db..f44ca1a18 100644 --- a/packages/types/clients/voting-module.ts +++ b/packages/types/clients/voting-module.ts @@ -14,6 +14,11 @@ export interface IVotingModuleBase<Dao extends IDaoBase = IDaoBase> { */ dao: Dao + /** + * Chain ID of the voting module. + */ + chainId: string + /** * Address of the voting module. */ diff --git a/packages/types/voting-module-adapter.ts b/packages/types/voting-module-adapter.ts index 9c322a894..df421331b 100644 --- a/packages/types/voting-module-adapter.ts +++ b/packages/types/voting-module-adapter.ts @@ -59,7 +59,7 @@ export type VotingModuleAdapter = { export interface IVotingModuleAdapterOptions { chainId: string coreAddress: string - votingModuleAddress: string + votingModule: IVotingModuleBase } export interface IVotingModuleAdapterContext { From 7aa813a5fd3c15c071abf9bf5adcb39de02c8961 Mon Sep 17 00:00:00 2001 From: Noah Saso <noahsaso@gmail.com> Date: Wed, 2 Oct 2024 21:52:17 -0400 Subject: [PATCH 24/26] consolidated DAO context hooks --- .../core/actions/BecomeApprover/Component.tsx | 9 +-- .../actions/GovernanceProposal/Component.tsx | 4 +- .../core/actions/UpdateInfo/Component.tsx | 4 +- packages/stateful/actions/providers/dao.tsx | 4 +- .../stateful/command/contexts/generic/dao.tsx | 6 +- packages/stateful/components/ProposalLine.tsx | 8 +-- packages/stateful/components/ProposalList.tsx | 4 +- .../stateful/components/dao/CreateDaoForm.tsx | 6 +- .../components/dao/CreateDaoProposal.tsx | 32 +++++------ .../stateful/components/dao/CreateSubDao.tsx | 7 +-- .../components/dao/DaoFiatDepositModal.tsx | 4 +- ...eProposeApprovalProposalContentDisplay.tsx | 4 +- .../stateful/components/dao/DaoProposal.tsx | 14 ++--- .../dao/DaoProposalContentDisplay.tsx | 4 +- .../dao/DaoRewardsDistributorClaimCard.tsx | 4 +- .../stateful/components/dao/DaoTokenCard.tsx | 4 +- .../components/dao/DaoTokenDepositModal.tsx | 4 +- .../components/dao/DaoTxTreasuryHistory.tsx | 4 +- .../dao/DiscordNotifierConfigureModal.tsx | 4 +- .../components/dao/MainDaoInfoCards.tsx | 4 +- .../dao/ProposalDaoInfoCardSections.tsx | 4 +- .../stateful/components/dao/tabs/AppsTab.tsx | 8 +-- .../stateful/components/dao/tabs/HomeTab.tsx | 6 +- .../components/dao/tabs/ProposalsTab.tsx | 6 +- .../components/dao/tabs/SubDaosTab.tsx | 8 ++- .../components/dao/tabs/TreasuryTab.tsx | 16 +++--- .../components/gov/CreateGovProposal.tsx | 9 +-- .../stateful/components/gov/GovInfoBar.tsx | 8 +-- .../stateful/components/gov/GovProposal.tsx | 6 +- .../stateful/components/pages/DaoDappHome.tsx | 34 ++++++----- .../stateful/components/pages/DaoSdaHome.tsx | 18 +++--- packages/stateful/hooks/useDaoClient.ts | 4 +- .../stateful/hooks/useDaoGovernanceToken.ts | 4 +- .../useDaoProposalSinglePublishProposal.ts | 6 +- .../hooks/useOnSecretNetworkPermitUpdate.ts | 4 +- .../stateful/hooks/useProposalActionState.tsx | 7 ++- .../stateful/hooks/useProposalRelayState.ts | 4 +- .../stateful/hooks/useProposalVetoState.tsx | 4 +- packages/stateful/hooks/useWebSocket.ts | 4 +- .../proposal-module-adapter/README.md | 3 +- .../common/components/NewProposal.tsx | 7 +-- .../components/ProposalStatusAndInfo.tsx | 6 +- .../common/components/NewProposal.tsx | 7 +-- ...reProposeApprovalProposalStatusAndInfo.tsx | 4 +- .../ProposalInnerContentDisplay.tsx | 4 +- .../components/ProposalStatusAndInfo.tsx | 4 +- .../ProposalStatusAndInfoLoader.tsx | 4 +- .../react/provider.tsx | 6 +- .../stateful/voting-module-adapter/README.md | 49 ---------------- .../components/MembersTab.tsx | 8 ++- .../components/ProfileCardMemberInfo.tsx | 8 +-- .../components/StakingModal.tsx | 16 +++--- .../hooks/useGovernanceTokenInfo.ts | 4 +- .../hooks/useMainDaoInfoCards.tsx | 5 +- .../hooks/useStakingInfo.ts | 8 +-- .../actions/ManageMembers/index.tsx | 13 ++--- .../components/MainDaoInfoCardsLoader.tsx | 4 +- .../DaoVotingCw4/components/MembersTab.tsx | 44 +++++++------- .../ProfileCardMemberInfo/index.tsx | 4 +- .../adapters/DaoVotingCw4/hooks/index.ts | 2 +- ...odule.ts => useLoadingVotingModuleInfo.ts} | 57 ++++++------------- .../DaoVotingCw4/hooks/useMainDaoInfoCards.ts | 17 +++--- .../hooks/useVotingModuleRelevantAddresses.ts | 12 ++-- .../components/MembersTab.tsx | 8 ++- .../components/ProfileCardMemberInfo.tsx | 8 +-- .../components/StakingModal.tsx | 17 +++--- .../hooks/useGovernanceCollectionInfo.ts | 4 +- .../hooks/useMainDaoInfoCards.tsx | 5 +- .../hooks/useStakingInfo.ts | 4 +- .../components/MembersTab.tsx | 8 ++- .../components/ProfileCardMemberInfo.tsx | 8 +-- .../components/StakingModal.tsx | 8 +-- .../hooks/useGovernanceCollectionInfo.ts | 6 +- .../hooks/useMainDaoInfoCards.tsx | 5 +- .../hooks/useStakingInfo.ts | 4 +- .../components/MembersTab.tsx | 8 ++- .../components/ProfileCardMemberInfo.tsx | 4 +- .../hooks/useMainDaoInfoCards.ts | 4 +- .../components/MembersTab.tsx | 8 ++- .../components/ProfileCardMemberInfo.tsx | 7 +-- .../components/StakingModal.tsx | 6 +- .../hooks/useGovernanceTokenInfo.ts | 25 ++++---- .../hooks/useMainDaoInfoCards.tsx | 9 ++- .../hooks/useStakingInfo.ts | 9 ++- .../adapters/DaoVotingTokenStaked/index.ts | 8 +-- .../components/MainDaoInfoCardsLoader.tsx | 4 +- .../components/ProfileCardMemberInfo.tsx | 11 ++-- .../components/StakingModal.tsx | 12 ++-- .../components/VaultsTab.tsx | 4 +- .../NeutronVotingRegistry/hooks/index.ts | 2 +- .../hooks/useMainDaoInfoCards.tsx | 4 +- ...VotingModule.ts => useVotingModuleInfo.ts} | 8 +-- .../stateful/voting-module-adapter/core.ts | 10 +--- .../voting-module-adapter/react/context.ts | 15 ----- .../voting-module-adapter/react/provider.tsx | 4 +- .../stateful/widgets/react/useWidgets.tsx | 4 +- .../widgets/widgets/Press/PressEditor.tsx | 7 ++- .../widgets/Press/Renderer/Renderer.tsx | 9 +-- .../widgets/widgets/Press/Renderer/index.tsx | 4 +- .../Press/actions/DeletePost/Component.tsx | 4 +- .../components/stateful/SurveyRow.tsx | 4 +- .../stateful/pages/CreateSurvey.tsx | 4 +- .../components/stateful/pages/Home.tsx | 4 +- .../stateful/pages/ViewSurvey/Complete.tsx | 4 +- .../stateful/pages/ViewSurvey/Info.tsx | 4 +- .../stateful/pages/ViewSurvey/Rate.tsx | 9 +-- .../stateful/pages/ViewSurvey/Submit.tsx | 4 +- .../stateful/pages/ViewSurvey/ViewSurvey.tsx | 4 +- .../components/stateless/SurveyRow.tsx | 4 +- .../components/stateless/TabRenderer.tsx | 8 +-- .../components/stateless/pages/Home.tsx | 4 +- .../stateless/pages/ViewSurvey/Info.tsx | 9 +-- .../Renderer/TabRenderer/TabRenderer.tsx | 4 +- .../Renderer/TabRenderer/index.tsx | 4 +- .../components/actions/ActionsEditor.tsx | 4 +- .../dao/DaoSplashHeader.stories.tsx | 4 +- .../components/dao/DaoSplashHeader.tsx | 38 ++++++------- .../dao/MainDaoInfoCardsTokenLoader.tsx | 4 +- .../dao/tabs/ProposalsTab.stories.tsx | 4 +- .../components/dao/tabs/ProposalsTab.tsx | 12 ++-- .../components/dao/tabs/SubDaosTab.tsx | 10 +++- .../components/dao/tabs/TreasuryTab.tsx | 4 +- .../components/inputs/AccountSelector.tsx | 2 +- .../inputs/DaoSupportedChainPickerInput.tsx | 10 ++-- .../components/layout/Breadcrumbs.tsx | 19 ++++--- .../components/layout/SdaNavigation.tsx | 4 +- .../modals/DiscordNotifierConfigureModal.tsx | 6 +- .../components/not_found/ProposalNotFound.tsx | 4 +- .../ProposalModuleSelector.stories.tsx | 6 +- .../proposal/ProposalModuleSelector.tsx | 12 ++-- .../stateless/components/token/TokenCard.tsx | 4 +- packages/stateless/contexts/Dao.ts | 7 ++- .../stateless/pages/DaoDappTabbedHome.tsx | 6 +- packages/types/components/Breadcrumbs.ts | 9 +-- .../types/components/DaoDappTabbedHome.ts | 2 +- packages/types/components/DaoSplashHeader.ts | 4 +- packages/types/voting-module-adapter.ts | 11 +--- packages/utils/dao.ts | 2 +- 138 files changed, 492 insertions(+), 631 deletions(-) rename packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/{useLoadingVotingModule.ts => useLoadingVotingModuleInfo.ts} (53%) rename packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/{useVotingModule.ts => useVotingModuleInfo.ts} (76%) diff --git a/packages/stateful/actions/core/actions/BecomeApprover/Component.tsx b/packages/stateful/actions/core/actions/BecomeApprover/Component.tsx index 7da238479..2494b4858 100644 --- a/packages/stateful/actions/core/actions/BecomeApprover/Component.tsx +++ b/packages/stateful/actions/core/actions/BecomeApprover/Component.tsx @@ -2,12 +2,7 @@ import { ComponentType } from 'react' import { useFormContext } from 'react-hook-form' import { useTranslation } from 'react-i18next' -import { - ErrorPage, - Loader, - RadioInput, - useDaoInfoContext, -} from '@dao-dao/stateless' +import { ErrorPage, Loader, RadioInput, useDao } from '@dao-dao/stateless' import { LoadingDataWithError, StatefulEntityDisplayProps, @@ -34,7 +29,7 @@ export const BecomeApproverComponent: ActionComponent< BecomeApproverOptions > = ({ fieldNamePrefix, isCreating, options: { options, EntityDisplay } }) => { const { t } = useTranslation() - const { name: daoName } = useDaoInfoContext() + const { name: daoName } = useDao() const { watch, setValue } = useFormContext<BecomeApproverData>() const dao = watch((fieldNamePrefix + 'dao') as 'dao') diff --git a/packages/stateful/actions/core/actions/GovernanceProposal/Component.tsx b/packages/stateful/actions/core/actions/GovernanceProposal/Component.tsx index 876a7c3a4..7670de517 100644 --- a/packages/stateful/actions/core/actions/GovernanceProposal/Component.tsx +++ b/packages/stateful/actions/core/actions/GovernanceProposal/Component.tsx @@ -22,7 +22,7 @@ import { TokenInput, useActionOptions, useChainContext, - useDaoInfoContextIfAvailable, + useDaoIfAvailable, } from '@dao-dao/stateless' import { AddressInputProps, @@ -110,7 +110,7 @@ export const GovernanceProposalComponent: ActionComponent< // Whether or not this action is being used directly on a governance page (as // opposed to in a DAO proposal). const onGovernancePage = - useDaoInfoContextIfAvailable()?.coreVersion === ContractVersion.Gov + useDaoIfAvailable()?.coreVersion === ContractVersion.Gov const { chainId, diff --git a/packages/stateful/actions/core/actions/UpdateInfo/Component.tsx b/packages/stateful/actions/core/actions/UpdateInfo/Component.tsx index c830dcc93..3e59ec2de 100644 --- a/packages/stateful/actions/core/actions/UpdateInfo/Component.tsx +++ b/packages/stateful/actions/core/actions/UpdateInfo/Component.tsx @@ -10,7 +10,7 @@ import { TextAreaInput, TextInput, useActionOptions, - useDaoInfoContext, + useDao, } from '@dao-dao/stateless' import { ChainId, ContractVersion } from '@dao-dao/types' import { ActionComponent, ActionContextType } from '@dao-dao/types/actions' @@ -29,7 +29,7 @@ export const UpdateInfoComponent: ActionComponent< undefined, UpdateInfoData > = ({ fieldNamePrefix, errors, isCreating, data }) => { - const { name } = useDaoInfoContext() + const { name } = useDao() const { address, context } = useActionOptions() const { t } = useTranslation() const { register, watch, setValue } = useFormContext() diff --git a/packages/stateful/actions/providers/dao.tsx b/packages/stateful/actions/providers/dao.tsx index 9b1c4dd36..5ff7d61fb 100644 --- a/packages/stateful/actions/providers/dao.tsx +++ b/packages/stateful/actions/providers/dao.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next' import { processMessage } from '@dao-dao/state' import { ActionsContext, - useDaoContext, + useDao, useSupportedChainContext, } from '@dao-dao/stateless' import { @@ -33,7 +33,7 @@ import { export const DaoActionsProvider = ({ children }: ActionsProviderProps) => { const { t } = useTranslation() const chainContext = useSupportedChainContext() - const { dao } = useDaoContext() + const dao = useDao() const queryClient = useQueryClient() // Get the action category makers for a DAO from its various sources: diff --git a/packages/stateful/command/contexts/generic/dao.tsx b/packages/stateful/command/contexts/generic/dao.tsx index 292b68b54..2ac4e3897 100644 --- a/packages/stateful/command/contexts/generic/dao.tsx +++ b/packages/stateful/command/contexts/generic/dao.tsx @@ -12,7 +12,7 @@ import { useRecoilState } from 'recoil' import useDeepCompareEffect from 'use-deep-compare-effect' import { navigatingToHrefAtom } from '@dao-dao/state' -import { useDaoInfoContext, useDaoNavHelpers } from '@dao-dao/stateless' +import { useDao, useDaoNavHelpers } from '@dao-dao/stateless' import { AccountType, ContractVersion, Feature } from '@dao-dao/types' import { CommandModalContextMaker, @@ -48,7 +48,9 @@ export const makeGenericDaoContext: CommandModalContextMaker<{ const useSections = () => { const { t } = useTranslation() const { getDaoPath, getDaoProposalPath, router } = useDaoNavHelpers() - const { accounts, supportedFeatures } = useDaoInfoContext() + const { + info: { accounts, supportedFeatures }, + } = useDao() const loadingTabs = useLoadingTabs() const { isFollowing, setFollowing, setUnfollowing, updatingFollowing } = diff --git a/packages/stateful/components/ProposalLine.tsx b/packages/stateful/components/ProposalLine.tsx index dc553b56e..9a12a0e46 100644 --- a/packages/stateful/components/ProposalLine.tsx +++ b/packages/stateful/components/ProposalLine.tsx @@ -1,10 +1,6 @@ import { useTranslation } from 'react-i18next' -import { - LineLoader, - StatusCard, - useDaoContextIfAvailable, -} from '@dao-dao/stateless' +import { LineLoader, StatusCard, useDaoIfAvailable } from '@dao-dao/stateless' import { StatefulProposalLineProps } from '@dao-dao/types' import { @@ -20,7 +16,7 @@ export const ProposalLine = ({ coreAddress, ...props }: StatefulProposalLineProps) => { - const existingDao = useDaoContextIfAvailable()?.dao + const existingDao = useDaoIfAvailable() const content = ( <ProposalModuleAdapterProvider proposalId={props.proposalId}> diff --git a/packages/stateful/components/ProposalList.tsx b/packages/stateful/components/ProposalList.tsx index 0ef37542b..bf5414d3d 100644 --- a/packages/stateful/components/ProposalList.tsx +++ b/packages/stateful/components/ProposalList.tsx @@ -11,7 +11,7 @@ import { ProposalList as StatelessProposalList, useAppContext, useCachedLoadingWithError, - useDaoContext, + useDao, useDaoNavHelpers, useLoadingPromise, useUpdatingRef, @@ -66,7 +66,7 @@ export const ProposalList = ({ ...props }: StatefulProposalListProps) => { const { t } = useTranslation() - const { dao } = useDaoContext() + const dao = useDao() const { getDaoProposalPath } = useDaoNavHelpers() const { mode } = useAppContext() const { isMember = false } = useMembership() diff --git a/packages/stateful/components/dao/CreateDaoForm.tsx b/packages/stateful/components/dao/CreateDaoForm.tsx index 3b8175222..09612fdc3 100644 --- a/packages/stateful/components/dao/CreateDaoForm.tsx +++ b/packages/stateful/components/dao/CreateDaoForm.tsx @@ -34,7 +34,7 @@ import { TooltipInfoIcon, useAppContext, useCachedLoadable, - useDaoInfoContextIfAvailable, + useDaoIfAvailable, useDaoNavHelpers, useSupportedChainContext, useThemeContext, @@ -170,7 +170,7 @@ export const InnerCreateDaoForm = ({ initialPageIndex = 0, }: CreateDaoFormProps) => { const { t } = useTranslation() - const daoInfo = useDaoInfoContextIfAvailable() + const dao = useDaoIfAvailable() const queryClient = useQueryClient() const chainContext = useSupportedChainContext() @@ -977,7 +977,7 @@ export const InnerCreateDaoForm = ({ } : undefined, current: makingSubDao ? t('title.newSubDao') : t('title.newDao'), - daoInfo, + dao, }} centerNode={ !makingSubDao && ( diff --git a/packages/stateful/components/dao/CreateDaoProposal.tsx b/packages/stateful/components/dao/CreateDaoProposal.tsx index ed59dad35..a07d7f54b 100644 --- a/packages/stateful/components/dao/CreateDaoProposal.tsx +++ b/packages/stateful/components/dao/CreateDaoProposal.tsx @@ -27,15 +27,15 @@ import { CreateProposal, PageLoader, ProposalModuleSelector, - useDaoInfoContext, + useDao, useDaoNavHelpers, useUpdatingRef, } from '@dao-dao/stateless' import { BaseNewProposalProps, DaoTabId, + IProposalModuleBase, ProposalDraft, - ProposalModuleInfo, ProposalPrefill, } from '@dao-dao/types' import { @@ -58,11 +58,11 @@ import { SuspenseLoader } from '../SuspenseLoader' import { ProposalDaoInfoCards } from './ProposalDaoInfoCards' export const CreateDaoProposal = () => { - const daoInfo = useDaoInfoContext() + const dao = useDao() const [selectedProposalModule, setSelectedProposalModule] = useState(() => { // Ignore proposals with an approver pre-propose since those are // automatically managed by a pre-propose-approval contract in another DAO. - const validProposalModules = daoInfo.proposalModules.filter( + const validProposalModules = dao.proposalModules.filter( ({ prePropose }) => prePropose?.contractName !== ContractName.PreProposeApprover ) @@ -85,9 +85,9 @@ export const CreateDaoProposal = () => { ({ snapshot }) => async () => setLatestProposalSave( - await snapshot.getPromise(latestProposalSaveAtom(daoInfo.coreAddress)) + await snapshot.getPromise(latestProposalSaveAtom(dao.coreAddress)) ), - [daoInfo.coreAddress] + [dao.coreAddress] ) useEffect(() => { loadLatestProposalSave() @@ -109,8 +109,8 @@ export const CreateDaoProposal = () => { } type InnerCreateDaoProposalProps = { - selectedProposalModule: ProposalModuleInfo - setSelectedProposalModule: Dispatch<SetStateAction<ProposalModuleInfo>> + selectedProposalModule: IProposalModuleBase + setSelectedProposalModule: Dispatch<SetStateAction<IProposalModuleBase>> latestProposalSave: any } @@ -121,7 +121,7 @@ const InnerCreateDaoProposal = ({ }: InnerCreateDaoProposalProps) => { const { t } = useTranslation() const { goToDaoProposal, router, getDaoProposalPath } = useDaoNavHelpers() - const daoInfo = useDaoInfoContext() + const dao = useDao() // Set once prefill has been assessed, indicating NewProposal can load now. const [prefillChecked, setPrefillChecked] = useState(false) @@ -147,7 +147,7 @@ const InnerCreateDaoProposal = ({ const { getValues, reset } = formMethods const setLatestProposalSave = useSetRecoilState( - latestProposalSaveAtom(daoInfo.coreAddress) + latestProposalSaveAtom(dao.coreAddress) ) // Reset form to defaults and clear latest proposal save. @@ -163,7 +163,7 @@ const InnerCreateDaoProposal = ({ const loadPrefill = useCallback( ({ id, data }: ProposalPrefill<any>) => { // Attempt to find proposal module to prefill and set if found. - const matchingProposalModule = daoInfo.proposalModules.find( + const matchingProposalModule = dao.proposalModules.find( ({ contractName }) => matchProposalModuleAdapter(contractName)?.id === id ) @@ -173,7 +173,7 @@ const InnerCreateDaoProposal = ({ reset(data) } }, - [daoInfo.proposalModules, reset, setSelectedProposalModule] + [dao.proposalModules, reset, setSelectedProposalModule] ) // Prefill form with data from parameter once ready. @@ -248,7 +248,7 @@ const InnerCreateDaoProposal = ({ ]) const [drafts, setDrafts] = useRecoilState( - proposalDraftsAtom(daoInfo.coreAddress) + proposalDraftsAtom(dao.coreAddress) ) const [draftIndex, setDraftIndex] = useState<number>() const draft = @@ -389,7 +389,7 @@ const InnerCreateDaoProposal = ({ // Copy link to clipboard. navigator.clipboard.writeText( SITE_URL + - getDaoProposalPath(daoInfo.coreAddress, 'create', { + getDaoProposalPath(dao.coreAddress, 'create', { pi: cid, }) ) @@ -405,7 +405,7 @@ const InnerCreateDaoProposal = ({ sdaLabel: t('title.proposals'), }, current: t('title.createProposal'), - daoInfo, + dao, }} /> @@ -451,7 +451,7 @@ const InnerCreateDaoProposal = ({ // storage periodically. const FormSaver = () => { const { watch, getValues } = useFormContext() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const proposalCreatedCardProps = useRecoilValue(proposalCreatedCardPropsAtom) const setLatestProposalSave = useSetRecoilState( diff --git a/packages/stateful/components/dao/CreateSubDao.tsx b/packages/stateful/components/dao/CreateSubDao.tsx index 609e85e2f..822b1c79e 100644 --- a/packages/stateful/components/dao/CreateSubDao.tsx +++ b/packages/stateful/components/dao/CreateSubDao.tsx @@ -1,4 +1,4 @@ -import { useDaoInfoContext } from '@dao-dao/stateless' +import { useDao } from '@dao-dao/stateless' import { ContractVersion } from '@dao-dao/types' import { getFallbackImage } from '@dao-dao/utils' @@ -12,10 +12,9 @@ export const CreateSubDao = () => { coreVersion, name, imageUrl, - parentDao, - admin, accounts, - } = useDaoInfoContext() + info: { parentDao, admin }, + } = useDao() // Chain x/gov DAO infos have coreAddress set to their name for URL // resolution, so retrieve their gov module address from their accounts list diff --git a/packages/stateful/components/dao/DaoFiatDepositModal.tsx b/packages/stateful/components/dao/DaoFiatDepositModal.tsx index 521085e81..38eb2d35c 100644 --- a/packages/stateful/components/dao/DaoFiatDepositModal.tsx +++ b/packages/stateful/components/dao/DaoFiatDepositModal.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next' -import { KadoModal, useDaoInfoContext } from '@dao-dao/stateless' +import { KadoModal, useDao } from '@dao-dao/stateless' import { DaoFiatDepositModalProps } from '@dao-dao/types' export const DaoFiatDepositModal = ({ @@ -10,7 +10,7 @@ export const DaoFiatDepositModal = ({ }: DaoFiatDepositModalProps) => { const { t } = useTranslation() - const { chainId: daoChainId, coreAddress, accounts } = useDaoInfoContext() + const { chainId: daoChainId, coreAddress, accounts } = useDao() // Deposit address depends on the account type. let depositAddress = accounts.find( (account) => account.chainId === chainId && account.type === accountType diff --git a/packages/stateful/components/dao/DaoPreProposeApprovalProposalContentDisplay.tsx b/packages/stateful/components/dao/DaoPreProposeApprovalProposalContentDisplay.tsx index 3552d45d0..826e0a99e 100644 --- a/packages/stateful/components/dao/DaoPreProposeApprovalProposalContentDisplay.tsx +++ b/packages/stateful/components/dao/DaoPreProposeApprovalProposalContentDisplay.tsx @@ -5,7 +5,7 @@ import { Loader, ProposalContentDisplay, StatusCard, - useDaoInfoContext, + useDao, useDaoNavHelpers, } from '@dao-dao/stateless' import { @@ -28,7 +28,7 @@ export const DaoPreProposeApprovalProposalContentDisplay = ({ proposalInfo, }: DaoPreProposeApprovalProposalContentDisplayProps) => { const { t } = useTranslation() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { getDaoProposalPath } = useDaoNavHelpers() const { id, diff --git a/packages/stateful/components/dao/DaoProposal.tsx b/packages/stateful/components/dao/DaoProposal.tsx index 0efde5d17..7adfcb389 100644 --- a/packages/stateful/components/dao/DaoProposal.tsx +++ b/packages/stateful/components/dao/DaoProposal.tsx @@ -21,7 +21,7 @@ import { ProposalNotFound, ProposalProps, ProposalVotesPrivate, - useDaoInfoContext, + useDao, } from '@dao-dao/stateless' import { CommonProposalInfo, @@ -47,7 +47,7 @@ interface InnerDaoProposalProps { const InnerDaoProposal = ({ proposalInfo }: InnerDaoProposalProps) => { const { t } = useTranslation() - const daoInfo = useDaoInfoContext() + const dao = useDao() const { address } = useWallet() const proposalModuleAdapterContext = useProposalModuleAdapterContext() @@ -138,7 +138,7 @@ const InnerDaoProposal = ({ proposalInfo }: InnerDaoProposalProps) => { // Manually revalidate DAO static props. await fetch( - `/api/revalidate?d=${daoInfo.coreAddress}&p=${proposalInfo.id}` + `/api/revalidate?d=${dao.coreAddress}&p=${proposalInfo.id}` ) // Refresh entire app since any DAO config may have changed. @@ -235,7 +235,7 @@ const InnerDaoProposal = ({ proposalInfo }: InnerDaoProposalProps) => { sdaLabel: t('title.proposals'), }, current: `${t('title.proposal')} ${proposalInfo.id}`, - daoInfo, + dao, }} rightNode={ canVote ? ( @@ -277,7 +277,7 @@ const InnerDaoProposal = ({ proposalInfo }: InnerDaoProposalProps) => { VotesCast={ isPreProposeApprovalProposal ? undefined - : isSecretNetwork(daoInfo.chainId) + : isSecretNetwork(dao.chainId) ? ProposalVotesPrivate : ProposalVotes } @@ -328,7 +328,7 @@ const InnerDaoProposal = ({ proposalInfo }: InnerDaoProposalProps) => { } export const DaoProposal = ({ proposalInfo }: DaoProposalProps) => { - const { coreAddress } = useDaoInfoContext() + const dao = useDao() return proposalInfo ? ( <ProposalModuleAdapterProvider @@ -336,7 +336,7 @@ export const DaoProposal = ({ proposalInfo }: DaoProposalProps) => { // Make sure to refresh when the DAO or proposal ID changes. In case we // redirect to a proposal in the same DAO, this is necessary to refresh // for some reason. - coreAddress + proposalInfo.id + dao.coreAddress + proposalInfo.id } proposalId={proposalInfo.id} > diff --git a/packages/stateful/components/dao/DaoProposalContentDisplay.tsx b/packages/stateful/components/dao/DaoProposalContentDisplay.tsx index 7101dc2c1..ed3cf4e49 100644 --- a/packages/stateful/components/dao/DaoProposalContentDisplay.tsx +++ b/packages/stateful/components/dao/DaoProposalContentDisplay.tsx @@ -2,7 +2,7 @@ import { useState } from 'react' import { ProposalContentDisplay, - useDaoInfoContext, + useDao, useDaoNavHelpers, } from '@dao-dao/stateless' import { @@ -24,7 +24,7 @@ export type DaoProposalContentDisplayProps = { export const DaoProposalContentDisplay = ({ proposalInfo, }: DaoProposalContentDisplayProps) => { - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { getDaoProposalPath } = useDaoNavHelpers() const { id, diff --git a/packages/stateful/components/dao/DaoRewardsDistributorClaimCard.tsx b/packages/stateful/components/dao/DaoRewardsDistributorClaimCard.tsx index 79fe7dff1..145548037 100644 --- a/packages/stateful/components/dao/DaoRewardsDistributorClaimCard.tsx +++ b/packages/stateful/components/dao/DaoRewardsDistributorClaimCard.tsx @@ -9,7 +9,7 @@ import { } from '@dao-dao/state/query' import { DaoRewardsDistributorClaimCard as StatelessDaoRewardsDistributorClaimCard, - useDaoContext, + useDao, } from '@dao-dao/stateless' import { StatefulDaoRewardsDistributorClaimCardProps } from '@dao-dao/types' import { executeSmartContracts, processError } from '@dao-dao/utils' @@ -20,7 +20,7 @@ export const DaoRewardsDistributorClaimCard = ({ ...props }: StatefulDaoRewardsDistributorClaimCardProps) => { const { t } = useTranslation() - const { dao } = useDaoContext() + const dao = useDao() const { address, isWalletConnected, getSigningClient } = useWallet() const queryClient = useQueryClient() diff --git a/packages/stateful/components/dao/DaoTokenCard.tsx b/packages/stateful/components/dao/DaoTokenCard.tsx index cb106cc2a..3e3a2d1f8 100644 --- a/packages/stateful/components/dao/DaoTokenCard.tsx +++ b/packages/stateful/components/dao/DaoTokenCard.tsx @@ -18,7 +18,7 @@ import { ChainProvider, TokenCard as StatelessTokenCard, useCachedLoading, - useDaoInfoContext, + useDao, useDaoNavHelpers, } from '@dao-dao/stateless' import { @@ -49,7 +49,7 @@ export const DaoTokenCard = ({ const { t } = useTranslation() const router = useRouter() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { getDaoProposalPath } = useDaoNavHelpers() const lazyInfo = useCachedLoading( diff --git a/packages/stateful/components/dao/DaoTokenDepositModal.tsx b/packages/stateful/components/dao/DaoTokenDepositModal.tsx index 8947e5026..87e564b5c 100644 --- a/packages/stateful/components/dao/DaoTokenDepositModal.tsx +++ b/packages/stateful/components/dao/DaoTokenDepositModal.tsx @@ -14,7 +14,7 @@ import { TokenDepositModal, TokenDepositModalProps, useCachedLoading, - useDaoInfoContext, + useDao, } from '@dao-dao/stateless' import { Account } from '@dao-dao/types' import { CHAIN_GAS_MULTIPLIER, processError } from '@dao-dao/utils' @@ -36,7 +36,7 @@ export const DaoTokenDepositModal = ({ ...props }: DaoTokenDepositModalProps) => { const { t } = useTranslation() - const { name: daoName } = useDaoInfoContext() + const { name: daoName } = useDao() const { isWalletConnected, address, diff --git a/packages/stateful/components/dao/DaoTxTreasuryHistory.tsx b/packages/stateful/components/dao/DaoTxTreasuryHistory.tsx index 545455cbf..7febf33ce 100644 --- a/packages/stateful/components/dao/DaoTxTreasuryHistory.tsx +++ b/packages/stateful/components/dao/DaoTxTreasuryHistory.tsx @@ -21,7 +21,7 @@ import { TokenAmountDisplay, useChainContext, useConfiguredChainContext, - useDaoInfoContext, + useDao, } from '@dao-dao/stateless' import { processError, tokensEqual } from '@dao-dao/utils' @@ -63,7 +63,7 @@ export const InnerDaoTxTreasuryHistory = ({ chain: { chain_id: chainId }, nativeToken, } = useChainContext() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() // Initialization. const latestBlockHeight = useRecoilValue( diff --git a/packages/stateful/components/dao/DiscordNotifierConfigureModal.tsx b/packages/stateful/components/dao/DiscordNotifierConfigureModal.tsx index a4c4b36ea..440ace508 100644 --- a/packages/stateful/components/dao/DiscordNotifierConfigureModal.tsx +++ b/packages/stateful/components/dao/DiscordNotifierConfigureModal.tsx @@ -17,7 +17,7 @@ import { Tooltip, useCachedLoadable, useChain, - useDaoInfoContext, + useDao, useDaoNavHelpers, } from '@dao-dao/stateless' import { DaoTabId } from '@dao-dao/types' @@ -37,8 +37,8 @@ export const DiscordNotifierConfigureModal = () => { const { t } = useTranslation() const router = useRouter() const { chain_id: chainId } = useChain() - const { coreAddress } = useDaoInfoContext() const { getDaoPath } = useDaoNavHelpers() + const { coreAddress } = useDao() const { isWalletConnected, hexPublicKey } = useWallet({ loadAccount: true, }) diff --git a/packages/stateful/components/dao/MainDaoInfoCards.tsx b/packages/stateful/components/dao/MainDaoInfoCards.tsx index 4860d1db0..6648807a9 100644 --- a/packages/stateful/components/dao/MainDaoInfoCards.tsx +++ b/packages/stateful/components/dao/MainDaoInfoCards.tsx @@ -8,7 +8,7 @@ import { DaoInfoCards as StatelessDaoInfoCards, TokenAmountDisplay, useChain, - useDaoContext, + useDao, } from '@dao-dao/stateless' import { PreProposeModuleType } from '@dao-dao/types' import { formatDate, formatPercentOf100 } from '@dao-dao/utils' @@ -39,7 +39,7 @@ const InnerMainDaoInfoCards = () => { const votingModuleCards = useMainDaoInfoCards() const tokenInfo = useDaoGovernanceToken() - const { dao } = useDaoContext() + const dao = useDao() const { activeThreshold, created, proposalModules } = dao.info const tvlLoading = useQueryLoadingData(dao.tvlQuery, { diff --git a/packages/stateful/components/dao/ProposalDaoInfoCardSections.tsx b/packages/stateful/components/dao/ProposalDaoInfoCardSections.tsx index 5f3b5bc85..f09a92580 100644 --- a/packages/stateful/components/dao/ProposalDaoInfoCardSections.tsx +++ b/packages/stateful/components/dao/ProposalDaoInfoCardSections.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next' -import { Loader, TooltipInfoIcon, useDaoContext } from '@dao-dao/stateless' +import { Loader, TooltipInfoIcon, useDao } from '@dao-dao/stateless' import { PreProposeModuleType } from '@dao-dao/types' import { ProposalModuleAdapterCommonProvider } from '../../proposal-module-adapter' @@ -11,7 +11,7 @@ import { Trans } from '../Trans' import { ProposalDaoInfoCards } from './ProposalDaoInfoCards' export const ProposalDaoInfoCardSections = () => { - const { dao } = useDaoContext() + const dao = useDao() return ( <> diff --git a/packages/stateful/components/dao/tabs/AppsTab.tsx b/packages/stateful/components/dao/tabs/AppsTab.tsx index f2bf01653..0a2a29a5b 100644 --- a/packages/stateful/components/dao/tabs/AppsTab.tsx +++ b/packages/stateful/components/dao/tabs/AppsTab.tsx @@ -27,7 +27,7 @@ import { AppsTab as StatelessAppsTab, StatusCard, useActionMatcher, - useDaoInfoContext, + useDao, useLoadingPromise, } from '@dao-dao/stateless' import { @@ -75,10 +75,10 @@ export const AppsTab = () => { name, chainId: currentChainId, coreAddress, - polytoneProxies, proposalModules, accounts, - } = useDaoInfoContext() + info: { polytoneProxies }, + } = useDao() // Select the single choice proposal module to use for proposals. const singleChoiceProposalModule = proposalModules.find( @@ -396,7 +396,7 @@ const InnerActionMatcherAndProposer = ({ actionKeysAndData, }: ActionMatcherAndProposerProps) => { const { t } = useTranslation() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { connected, profile } = useProfile() const { diff --git a/packages/stateful/components/dao/tabs/HomeTab.tsx b/packages/stateful/components/dao/tabs/HomeTab.tsx index bc9535295..48840ea8b 100644 --- a/packages/stateful/components/dao/tabs/HomeTab.tsx +++ b/packages/stateful/components/dao/tabs/HomeTab.tsx @@ -7,7 +7,7 @@ import { DaoSplashHeader, useAppContext, useCachedLoadable, - useDaoContext, + useDao, } from '@dao-dao/stateless' import { CheckedDepositInfo, DaoPageMode } from '@dao-dao/types' import { getDaoRewardDistributors } from '@dao-dao/utils' @@ -28,7 +28,7 @@ import { MainDaoInfoCards } from '../MainDaoInfoCards' export const HomeTab = () => { const { t } = useTranslation() - const { dao } = useDaoContext() + const dao = useDao() const { mode } = useAppContext() const { isWalletConnected, isSecretNetworkPermitNeeded } = useDaoWithWalletSecretNetworkPermit() @@ -80,7 +80,7 @@ export const HomeTab = () => { <DaoSplashHeader ButtonLink={ButtonLink} LinkWrapper={LinkWrapper} - daoInfo={dao.info} + dao={dao} /> )} diff --git a/packages/stateful/components/dao/tabs/ProposalsTab.tsx b/packages/stateful/components/dao/tabs/ProposalsTab.tsx index c0c4414ff..2224bdd5c 100644 --- a/packages/stateful/components/dao/tabs/ProposalsTab.tsx +++ b/packages/stateful/components/dao/tabs/ProposalsTab.tsx @@ -1,19 +1,19 @@ import { ProposalsTab as StatelessProposalsTab, - useDaoInfoContext, + useDao, } from '@dao-dao/stateless' import { ButtonLink } from '../../ButtonLink' import { ProposalList } from '../../ProposalList' export const ProposalsTab = () => { - const daoInfo = useDaoInfoContext() + const dao = useDao() return ( <StatelessProposalsTab ButtonLink={ButtonLink} ProposalList={ProposalList} - daoInfo={daoInfo} + dao={dao} /> ) } diff --git a/packages/stateful/components/dao/tabs/SubDaosTab.tsx b/packages/stateful/components/dao/tabs/SubDaosTab.tsx index e0c5edf51..082fe1892 100644 --- a/packages/stateful/components/dao/tabs/SubDaosTab.tsx +++ b/packages/stateful/components/dao/tabs/SubDaosTab.tsx @@ -2,7 +2,7 @@ import { useQueryClient } from '@tanstack/react-query' import { SubDaosTab as StatelessSubDaosTab, - useDaoInfoContext, + useDao, useDaoNavHelpers, useInitializedActionForKey, } from '@dao-dao/stateless' @@ -15,7 +15,11 @@ import { ButtonLink } from '../../ButtonLink' import { DaoCard } from '../DaoCard' export const SubDaosTab = () => { - const { chainId, coreAddress, supportedFeatures } = useDaoInfoContext() + const { + chainId, + coreAddress, + info: { supportedFeatures }, + } = useDao() const { getDaoPath, getDaoProposalPath } = useDaoNavHelpers() const { isMember = false } = useMembership() diff --git a/packages/stateful/components/dao/tabs/TreasuryTab.tsx b/packages/stateful/components/dao/tabs/TreasuryTab.tsx index d449396b4..55df4337f 100644 --- a/packages/stateful/components/dao/tabs/TreasuryTab.tsx +++ b/packages/stateful/components/dao/tabs/TreasuryTab.tsx @@ -5,7 +5,7 @@ import { import { TreasuryTab as StatelessTreasuryTab, useCachedLoading, - useDaoInfoContext, + useDao, useDaoNavHelpers, useInitializedActionForKey, } from '@dao-dao/stateless' @@ -26,7 +26,7 @@ import { DaoFiatDepositModal } from '../DaoFiatDepositModal' import { DaoTokenLine } from '../DaoTokenLine' export const TreasuryTab = () => { - const daoInfo = useDaoInfoContext() + const dao = useDao() const { isWalletConnected } = useWallet() const { getDaoProposalPath } = useDaoNavHelpers() @@ -39,8 +39,8 @@ export const TreasuryTab = () => { const tokens = useCachedLoading( treasuryTokenCardInfosForDaoSelector({ - chainId: daoInfo.chainId, - coreAddress: daoInfo.coreAddress, + chainId: dao.chainId, + coreAddress: dao.coreAddress, cw20GovernanceTokenAddress, nativeGovernanceTokenDenom, }), @@ -48,8 +48,8 @@ export const TreasuryTab = () => { ) const nfts = useCachedLoading( lazyNftCardInfosForDaoSelector({ - chainId: daoInfo.chainId, - coreAddress: daoInfo.coreAddress, + chainId: dao.chainId, + coreAddress: dao.coreAddress, governanceCollectionAddress: cw721GovernanceCollectionAddress, }), {} @@ -97,7 +97,7 @@ export const TreasuryTab = () => { configureRebalancerHref={ // Prefill URL only valid if action exists. configureRebalancerAction - ? getDaoProposalPath(daoInfo.coreAddress, 'create', { + ? getDaoProposalPath(dao.coreAddress, 'create', { prefill: configureRebalancerPrefill, }) : undefined @@ -110,7 +110,7 @@ export const TreasuryTab = () => { !createCrossChainAccountAction.loading && !createCrossChainAccountAction.errored && !createCrossChainAccountAction.data.metadata.hideFromPicker - ? getDaoProposalPath(daoInfo.coreAddress, 'create', { + ? getDaoProposalPath(dao.coreAddress, 'create', { prefill: createCrossChainAccountPrefill, }) : undefined diff --git a/packages/stateful/components/gov/CreateGovProposal.tsx b/packages/stateful/components/gov/CreateGovProposal.tsx index e777451a1..ce0c1af44 100644 --- a/packages/stateful/components/gov/CreateGovProposal.tsx +++ b/packages/stateful/components/gov/CreateGovProposal.tsx @@ -1,10 +1,7 @@ import { useRef } from 'react' import { useTranslation } from 'react-i18next' -import { - CreateProposal, - useDaoInfoContextIfAvailable, -} from '@dao-dao/stateless' +import { CreateProposal, useDaoIfAvailable } from '@dao-dao/stateless' import { DaoTabId } from '@dao-dao/types' import { GovActionsProvider } from '../../actions' @@ -13,7 +10,7 @@ import { NewGovProposal } from './NewGovProposal' export const CreateGovProposal = () => { const { t } = useTranslation() - const daoInfo = useDaoInfoContextIfAvailable() + const dao = useDaoIfAvailable() const clearRef = useRef(() => {}) const copyDraftLinkRef = useRef(async () => {}) @@ -27,7 +24,7 @@ export const CreateGovProposal = () => { sdaLabel: t('title.proposals'), }, current: t('title.createProposal'), - daoInfo, + dao, }} /> diff --git a/packages/stateful/components/gov/GovInfoBar.tsx b/packages/stateful/components/gov/GovInfoBar.tsx index ca1d2fe10..52d8ee8ae 100644 --- a/packages/stateful/components/gov/GovInfoBar.tsx +++ b/packages/stateful/components/gov/GovInfoBar.tsx @@ -1,17 +1,13 @@ import { useTranslation } from 'react-i18next' -import { - DaoInfoCards, - TokenAmountDisplay, - useDaoContext, -} from '@dao-dao/stateless' +import { DaoInfoCards, TokenAmountDisplay, useDao } from '@dao-dao/stateless' import { useQueryLoadingData } from '../../hooks' export const GovInfoBar = () => { const { t } = useTranslation() - const { dao } = useDaoContext() + const dao = useDao() const tvlLoading = useQueryLoadingData(dao.tvlQuery, { amount: -1, timestamp: Date.now(), diff --git a/packages/stateful/components/gov/GovProposal.tsx b/packages/stateful/components/gov/GovProposal.tsx index 8d667072c..53375d9bc 100644 --- a/packages/stateful/components/gov/GovProposal.tsx +++ b/packages/stateful/components/gov/GovProposal.tsx @@ -10,7 +10,7 @@ import { Proposal, ProposalNotFound, useChain, - useDaoInfoContextIfAvailable, + useDaoIfAvailable, } from '@dao-dao/stateless' import { BaseProposalVotesProps, @@ -46,7 +46,7 @@ type InnerGovProposalProps = { const InnerGovProposal = ({ proposal }: InnerGovProposalProps) => { const { t } = useTranslation() const { chain_id: chainId } = useChain() - const daoInfo = useDaoInfoContextIfAvailable() + const dao = useDaoIfAvailable() const proposalId = proposal.id.toString() const loadingProposal = useLoadingGovProposal(proposalId) @@ -113,7 +113,7 @@ const InnerGovProposal = ({ proposal }: InnerGovProposalProps) => { sdaLabel: t('title.proposals'), }, current: `${t('title.proposal')} ${proposalId}`, - daoInfo, + dao, }} rightNode={ proposal.proposal.status === diff --git a/packages/stateful/components/pages/DaoDappHome.tsx b/packages/stateful/components/pages/DaoDappHome.tsx index 1665681b3..21ae21605 100644 --- a/packages/stateful/components/pages/DaoDappHome.tsx +++ b/packages/stateful/components/pages/DaoDappHome.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next' import { DaoDappTabbedHome, FollowingToggle, - useDaoInfoContext, + useDao, useDaoNavHelpers, } from '@dao-dao/stateless' import { @@ -42,14 +42,14 @@ export const InnerDaoDappHome = ({ ...props }: InnerDaoDappHomeProps) => { const { t } = useTranslation() - const daoInfo = useDaoInfoContext() + const dao = useDao() const { getDaoPath, getDaoProposalPath, router } = useDaoNavHelpers() const { isFollowing, setFollowing, setUnfollowing, updatingFollowing } = useFollowingDaos() const following = isFollowing({ - chainId: daoInfo.chainId, - coreAddress: daoInfo.coreAddress, + chainId: dao.chainId, + coreAddress: dao.coreAddress, }) // Just a type-check because some tabs are loaded at the beginning. @@ -61,9 +61,9 @@ export const InnerDaoDappHome = ({ // Pre-fetch tabs. useEffect(() => { tabs?.forEach((tab) => { - router.prefetch(getDaoPath(daoInfo.coreAddress, tab.id)) + router.prefetch(getDaoPath(dao.coreAddress, tab.id)) }) - }, [daoInfo.coreAddress, getDaoPath, router, tabs]) + }, [dao.coreAddress, getDaoPath, router, tabs]) const slug = (router.query.slug || []) as string[] const checkedSlug = useRef(false) @@ -78,11 +78,11 @@ export const InnerDaoDappHome = ({ // If no slug and on current DAO, redirect to first tab. if (slug.length === 0) { - router.replace(getDaoPath(daoInfo.coreAddress, firstTabId), undefined, { + router.replace(getDaoPath(dao.coreAddress, firstTabId), undefined, { shallow: true, }) } - }, [daoInfo.coreAddress, getDaoPath, router, slug.length, firstTabId]) + }, [dao.coreAddress, getDaoPath, router, slug.length, firstTabId]) const tabId = slug.length > 0 && tabs?.some(({ id }) => id === slug[0]) @@ -90,7 +90,7 @@ export const InnerDaoDappHome = ({ : // If tab is invalid, default to first tab. firstTabId const onSelectTabId = (tabId: string) => - router.replace(getDaoPath(daoInfo.coreAddress, tabId), undefined, { + router.replace(getDaoPath(dao.coreAddress, tabId), undefined, { shallow: true, }) @@ -98,8 +98,8 @@ export const InnerDaoDappHome = ({ following, onFollow: () => (following ? setUnfollowing : setFollowing)({ - chainId: daoInfo.chainId, - coreAddress: daoInfo.coreAddress, + chainId: dao.chainId, + coreAddress: dao.coreAddress, }), updatingFollowing, } @@ -109,8 +109,8 @@ export const InnerDaoDappHome = ({ <PageHeaderContent breadcrumbs={{ home: true, - current: daoInfo.name, - daoInfo, + current: dao.name, + dao, }} rightNode={ <> @@ -118,7 +118,7 @@ export const InnerDaoDappHome = ({ <ButtonLink className="hidden md:block" contentContainerClassName="text-text-body text-base !gap-1.5" - href={getDaoProposalPath(daoInfo.coreAddress, 'create')} + href={getDaoProposalPath(dao.coreAddress, 'create')} variant="ghost" > <Add className="!h-5 !w-5 !text-icon-primary" /> @@ -158,10 +158,8 @@ export const DaoDappHome = () => { chainId, coreAddress, name, - contractAdmin, - supportedFeatures, - parentDao, - } = useDaoInfoContext() + info: { contractAdmin, supportedFeatures, parentDao }, + } = useDao() const { isMember = false } = useMembership() // We won't use this value unless there's a parent, so the undefined DAO diff --git a/packages/stateful/components/pages/DaoSdaHome.tsx b/packages/stateful/components/pages/DaoSdaHome.tsx index 75135eb5e..1d4521791 100644 --- a/packages/stateful/components/pages/DaoSdaHome.tsx +++ b/packages/stateful/components/pages/DaoSdaHome.tsx @@ -1,11 +1,7 @@ import { useRouter } from 'next/router' import { useEffect, useRef } from 'react' -import { - DaoSdaWrappedTab, - useDaoInfoContext, - useDaoNavHelpers, -} from '@dao-dao/stateless' +import { DaoSdaWrappedTab, useDao, useDaoNavHelpers } from '@dao-dao/stateless' import { DaoTabId } from '@dao-dao/types' import { useDaoTabs } from '../../hooks' @@ -14,7 +10,7 @@ import { SuspenseLoader } from '../SuspenseLoader' export const DaoSdaHome = () => { const router = useRouter() - const daoInfo = useDaoInfoContext() + const dao = useDao() const { getDaoPath } = useDaoNavHelpers() const loadingTabs = useDaoTabs() @@ -27,9 +23,9 @@ export const DaoSdaHome = () => { // Pre-fetch tabs. useEffect(() => { tabs?.forEach((tab) => { - router.prefetch(getDaoPath(daoInfo.coreAddress, tab.id)) + router.prefetch(getDaoPath(dao.coreAddress, tab.id)) }) - }, [daoInfo.coreAddress, getDaoPath, router, tabs]) + }, [dao.coreAddress, getDaoPath, router, tabs]) const slug = (router.query.slug || []) as string[] const checkedSlug = useRef(false) @@ -44,11 +40,11 @@ export const DaoSdaHome = () => { // If no slug, redirect to first tab. if (slug.length === 0) { - router.replace(getDaoPath(daoInfo.coreAddress, firstTabId), undefined, { + router.replace(getDaoPath(dao.coreAddress, firstTabId), undefined, { shallow: true, }) } - }, [daoInfo.coreAddress, getDaoPath, router, slug.length, firstTabId]) + }, [dao.coreAddress, getDaoPath, router, slug.length, firstTabId]) const selectedTabId = slug.length > 0 && tabs?.some(({ id }) => id === slug[0]) @@ -65,7 +61,7 @@ export const DaoSdaHome = () => { breadcrumbs={{ home: true, current: activeTab?.label, - daoInfo, + dao, }} /> diff --git a/packages/stateful/hooks/useDaoClient.ts b/packages/stateful/hooks/useDaoClient.ts index 8331436a5..97cca280b 100644 --- a/packages/stateful/hooks/useDaoClient.ts +++ b/packages/stateful/hooks/useDaoClient.ts @@ -1,7 +1,7 @@ import { useQueryClient } from '@tanstack/react-query' import { useMemo } from 'react' -import { useDaoContextIfAvailable } from '@dao-dao/stateless' +import { useDaoIfAvailable } from '@dao-dao/stateless' import { DaoSource, IDaoBase } from '@dao-dao/types' import { getDao } from '../clients/dao' @@ -28,7 +28,7 @@ export const useDaoClient = ({ dao: daoSource, }: UseDaoClientOptions): UseDaoClientReturn => { const queryClient = useQueryClient() - const currentDao = useDaoContextIfAvailable()?.dao + const currentDao = useDaoIfAvailable() // Get DAO client. If matches current DAO context, use that one instead. const dao = useMemo( diff --git a/packages/stateful/hooks/useDaoGovernanceToken.ts b/packages/stateful/hooks/useDaoGovernanceToken.ts index 215ae4745..35f44a24c 100644 --- a/packages/stateful/hooks/useDaoGovernanceToken.ts +++ b/packages/stateful/hooks/useDaoGovernanceToken.ts @@ -1,6 +1,6 @@ import { useSuspenseQuery } from '@tanstack/react-query' -import { useDaoContextIfAvailable } from '@dao-dao/stateless' +import { useDaoIfAvailable } from '@dao-dao/stateless' import { GenericToken } from '@dao-dao/types' /** @@ -9,7 +9,7 @@ import { GenericToken } from '@dao-dao/types' * context. Should never error. */ export const useDaoGovernanceToken = () => { - const dao = useDaoContextIfAvailable()?.dao + const dao = useDaoIfAvailable() return useSuspenseQuery<GenericToken | null>( dao?.maybeVotingModule?.getGovernanceTokenQuery?.() || { queryKey: ['null'], diff --git a/packages/stateful/hooks/useDaoProposalSinglePublishProposal.ts b/packages/stateful/hooks/useDaoProposalSinglePublishProposal.ts index da8ec7b7b..59ab9867d 100644 --- a/packages/stateful/hooks/useDaoProposalSinglePublishProposal.ts +++ b/packages/stateful/hooks/useDaoProposalSinglePublishProposal.ts @@ -1,6 +1,6 @@ import { useMemo } from 'react' -import { useDaoContext } from '@dao-dao/stateless' +import { useDao } from '@dao-dao/stateless' import { DaoProposalSingleAdapterId } from '@dao-dao/utils' import { @@ -16,10 +16,10 @@ import { PublishProposal } from '../proposal-module-adapter/adapters/DaoProposal export const useDaoProposalSinglePublishProposal = (): | PublishProposal | undefined => { - const { dao } = useDaoContext() + const dao = useDao() // Memoize hook getter since we don't want to create the hook more than once. - // `useDaoInfoContext` always returns the same instances of the data, so no + // `useDao` always returns the same instances of the data, so no // hook rules are violated here. const useProposalModule = useMemo(() => { const daoProposalSingleModule = dao.proposalModules.find( diff --git a/packages/stateful/hooks/useOnSecretNetworkPermitUpdate.ts b/packages/stateful/hooks/useOnSecretNetworkPermitUpdate.ts index 74bd105b4..329ae20f9 100644 --- a/packages/stateful/hooks/useOnSecretNetworkPermitUpdate.ts +++ b/packages/stateful/hooks/useOnSecretNetworkPermitUpdate.ts @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react' -import { useDaoContextIfAvailable, useUpdatingRef } from '@dao-dao/stateless' +import { useDaoIfAvailable, useUpdatingRef } from '@dao-dao/stateless' import { DaoSource, PermitForPermitData } from '@dao-dao/types' import { serializeDaoSource } from '@dao-dao/utils' @@ -37,7 +37,7 @@ export const useOnSecretNetworkPermitUpdate = ({ reRender = true, callback, }: OnSecretNetworkPermitUpdateOptions = {}) => { - const currentDaoSource = useDaoContextIfAvailable()?.dao.source + const currentDaoSource = useDaoIfAvailable()?.source // Memoize callback into a ref. const callbackRef = useUpdatingRef(callback) diff --git a/packages/stateful/hooks/useProposalActionState.tsx b/packages/stateful/hooks/useProposalActionState.tsx index 18602f6fa..6b3c69452 100644 --- a/packages/stateful/hooks/useProposalActionState.tsx +++ b/packages/stateful/hooks/useProposalActionState.tsx @@ -10,7 +10,7 @@ import { ProposalStatusAndInfoProps, TextInput, useConfiguredChainContext, - useDaoInfoContext, + useDao, } from '@dao-dao/stateless' import { ChainId, @@ -60,7 +60,10 @@ export const useProposalActionState = ({ const { chain: { chain_id: chainId }, } = useConfiguredChainContext() - const { coreAddress, items } = useDaoInfoContext() + const { + coreAddress, + info: { items }, + } = useDao() const { options: { proposalNumber }, proposalModule, diff --git a/packages/stateful/hooks/useProposalRelayState.ts b/packages/stateful/hooks/useProposalRelayState.ts index 4a85cb7a4..d2275e20b 100644 --- a/packages/stateful/hooks/useProposalRelayState.ts +++ b/packages/stateful/hooks/useProposalRelayState.ts @@ -20,7 +20,7 @@ import { import { useCachedLoading, useCachedLoadingWithError, - useDaoInfoContext, + useDao, useSupportedChainContext, } from '@dao-dao/stateless' import { @@ -66,7 +66,7 @@ export const useProposalRelayState = ({ openSelfRelayExecute, loadingTxHash, }: UseProposalRelayStateOptions): UseProposalRelayStateReturn => { - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { chain: { chain_id: srcChainId }, } = useSupportedChainContext() diff --git a/packages/stateful/hooks/useProposalVetoState.tsx b/packages/stateful/hooks/useProposalVetoState.tsx index d81e40155..0a124d0f4 100644 --- a/packages/stateful/hooks/useProposalVetoState.tsx +++ b/packages/stateful/hooks/useProposalVetoState.tsx @@ -9,7 +9,7 @@ import { ProposalStatusAndInfoProps, Tooltip, useConfiguredChainContext, - useDaoInfoContext, + useDao, useDaoNavHelpers, } from '@dao-dao/stateless' import { @@ -70,7 +70,7 @@ export const useProposalVetoState = ({ const { chain: { chain_id: chainId }, } = useConfiguredChainContext() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { getDaoProposalPath } = useDaoNavHelpers() const { proposalModule, proposalNumber } = useProposalModuleAdapterOptions() const { address: walletAddress = '', getSigningClient } = useWallet() diff --git a/packages/stateful/hooks/useWebSocket.ts b/packages/stateful/hooks/useWebSocket.ts index dbbc572f3..96fce4766 100644 --- a/packages/stateful/hooks/useWebSocket.ts +++ b/packages/stateful/hooks/useWebSocket.ts @@ -11,7 +11,7 @@ import { } from '@dao-dao/state/recoil' import { useCachedLoadingWithError, - useDaoInfoContext, + useDao, useUpdatingRef, } from '@dao-dao/stateless' import { ParametersExceptFirst } from '@dao-dao/types' @@ -243,6 +243,6 @@ export const useOnDaoWebSocketMessage = ( export const useOnCurrentDaoWebSocketMessage = ( ...args: ParametersExceptFirst<typeof useOnWebSocketMessage> ) => { - const { chainId, coreAddress } = useDaoInfoContext() + const { chainId, coreAddress } = useDao() return useOnDaoWebSocketMessage(chainId, coreAddress, ...args) } diff --git a/packages/stateful/proposal-module-adapter/README.md b/packages/stateful/proposal-module-adapter/README.md index a58b69b79..f8323c78f 100644 --- a/packages/stateful/proposal-module-adapter/README.md +++ b/packages/stateful/proposal-module-adapter/README.md @@ -101,9 +101,10 @@ view the voting configuration for each one: ```tsx import { matchAndLoadCommon } from '@dao-dao/stateful/proposal-module-adapter' +import { useDao } from '@dao-dao/stateless' export const DaoInfo = () => { - const { coreAddress, proposalModules } = useDaoInfoContext() + const { chainId, coreAddress, proposalModules } = useDao() const components = useMemo( () => diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/components/NewProposal.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/components/NewProposal.tsx index e8822a291..373e38229 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/components/NewProposal.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/common/components/NewProposal.tsx @@ -16,7 +16,7 @@ import { useActionsContext, useCachedLoadable, useChain, - useDaoInfoContext, + useDao, } from '@dao-dao/stateless' import { BaseNewProposalProps, IProposalModuleBase } from '@dao-dao/types' import { @@ -56,9 +56,8 @@ export const NewProposal = ({ name: daoName, imageUrl: daoImageUrl, coreAddress, - isActive, - activeThreshold, - } = useDaoInfoContext() + info: { isActive, activeThreshold }, + } = useDao() const { isWalletConnecting, isWalletConnected, getStargateClient } = useWallet() diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/components/ProposalStatusAndInfo.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/components/ProposalStatusAndInfo.tsx index 9686b8d6e..06921b23e 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/components/ProposalStatusAndInfo.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/components/ProposalStatusAndInfo.tsx @@ -23,7 +23,7 @@ import { Tooltip, TooltipTruncatedText, useConfiguredChainContext, - useDaoInfoContext, + useDao, useExecuteAt, useTranslatedTimeDeltaFormatter, } from '@dao-dao/stateless' @@ -109,7 +109,7 @@ const InnerProposalStatusAndInfo = ({ chain: { chain_id: chainId }, config: { explorerUrlTemplates }, } = useConfiguredChainContext() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { proposalModule, proposalNumber } = useProposalModuleAdapterOptions() const config = useRecoilValue( @@ -389,7 +389,7 @@ const InnerProposalStatusAndInfoLoader = ( props: BaseProposalStatusAndInfoProps ) => { const { t } = useTranslation() - const { name: daoName, coreAddress } = useDaoInfoContext() + const { name: daoName, coreAddress } = useDao() const LoaderP: ComponentType<{ className: string }> = ({ className }) => ( <p className={clsx('animate-pulse', className)}>...</p> diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/components/NewProposal.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/components/NewProposal.tsx index 2a565a7cf..35c58d039 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/components/NewProposal.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/common/components/NewProposal.tsx @@ -16,7 +16,7 @@ import { useActionsContext, useCachedLoadable, useChain, - useDaoInfoContext, + useDao, useProcessTQ, } from '@dao-dao/stateless' import { BaseNewProposalProps, IProposalModuleBase } from '@dao-dao/types' @@ -55,9 +55,8 @@ export const NewProposal = ({ name: daoName, imageUrl: daoImageUrl, coreAddress, - isActive, - activeThreshold, - } = useDaoInfoContext() + info: { isActive, activeThreshold }, + } = useDao() const { isWalletConnecting, isWalletConnected, getStargateClient } = useWallet() diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/PreProposeApprovalProposalStatusAndInfo.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/PreProposeApprovalProposalStatusAndInfo.tsx index 3ad6a845a..914a40fa0 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/PreProposeApprovalProposalStatusAndInfo.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/PreProposeApprovalProposalStatusAndInfo.tsx @@ -18,7 +18,7 @@ import { ProposalStatusAndInfoProps, ProposalStatusAndInfo as StatelessProposalStatusAndInfo, Tooltip, - useDaoInfoContext, + useDao, useDaoNavHelpers, } from '@dao-dao/stateless' import { @@ -69,7 +69,7 @@ const InnerPreProposeApprovalProposalStatusAndInfo = ({ proposal: PreProposeApprovalProposalWithMeteadata }) => { const { t } = useTranslation() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { getDaoProposalPath } = useDaoNavHelpers() const { proposalModule: { prefix, prePropose }, diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalInnerContentDisplay.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalInnerContentDisplay.tsx index b16c2ac60..b23be1498 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalInnerContentDisplay.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalInnerContentDisplay.tsx @@ -8,7 +8,7 @@ import { ActionsMatchAndRender, Button, RawActionsRenderer, - useDaoInfoContext, + useDao, } from '@dao-dao/stateless' import { ActionKeyAndData, @@ -52,7 +52,7 @@ const InnerProposalInnerContentDisplay = ({ }) => { const { t } = useTranslation() const [showRaw, setShowRaw] = useState(false) - const { chainId, coreVersion } = useDaoInfoContext() + const { chainId, coreVersion } = useDao() const actionMessagesToDisplay = useMemo(() => { let messages = proposal.msgs diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalStatusAndInfo.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalStatusAndInfo.tsx index a5290e495..4d27cbe2a 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalStatusAndInfo.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalStatusAndInfo.tsx @@ -22,7 +22,7 @@ import { ProposalStatusAndInfo as StatelessProposalStatusAndInfo, Tooltip, useConfiguredChainContext, - useDaoInfoContext, + useDao, useDaoNavHelpers, useExecuteAt, useTranslatedTimeDeltaFormatter, @@ -123,7 +123,7 @@ const InnerProposalStatusAndInfo = ({ chain: { chain_id: chainId }, config: { explorerUrlTemplates }, } = useConfiguredChainContext() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { getDaoProposalPath } = useDaoNavHelpers() const { proposalModule, proposalNumber } = useProposalModuleAdapterOptions() diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalStatusAndInfoLoader.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalStatusAndInfoLoader.tsx index 4b5051d8c..0a0e411be 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalStatusAndInfoLoader.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalStatusAndInfoLoader.tsx @@ -11,7 +11,7 @@ import { Logo, ProposalStatusAndInfoProps, ProposalStatusAndInfo as StatelessProposalStatusAndInfo, - useDaoInfoContext, + useDao, useDaoNavHelpers, } from '@dao-dao/stateless' import { BaseProposalStatusAndInfoProps } from '@dao-dao/types' @@ -22,7 +22,7 @@ export const ProposalStatusAndInfoLoader = ( props: Pick<BaseProposalStatusAndInfoProps, 'inline'> ) => { const { t } = useTranslation() - const { name: daoName, coreAddress } = useDaoInfoContext() + const { name: daoName, coreAddress } = useDao() const { getDaoPath } = useDaoNavHelpers() const LoaderP: ComponentType<{ className: string }> = ({ className }) => ( diff --git a/packages/stateful/proposal-module-adapter/react/provider.tsx b/packages/stateful/proposal-module-adapter/react/provider.tsx index 5d545bc1c..da5100a55 100644 --- a/packages/stateful/proposal-module-adapter/react/provider.tsx +++ b/packages/stateful/proposal-module-adapter/react/provider.tsx @@ -1,6 +1,6 @@ import { ReactNode, useMemo } from 'react' -import { useDaoContext } from '@dao-dao/stateless' +import { useDao } from '@dao-dao/stateless' import { IProposalModuleCommonContext, IProposalModuleContext, @@ -25,7 +25,7 @@ export const ProposalModuleAdapterProvider = ({ proposalId, children, }: ProposalModuleAdapterProviderProps) => { - const { dao } = useDaoContext() + const dao = useDao() const { context, commonContext } = useMemo(() => { const context = matchAndLoadAdapter(dao, proposalId) const commonContext = commonContextFromAdapterContext(context) @@ -54,7 +54,7 @@ export const ProposalModuleAdapterCommonProvider = ({ proposalModuleAddress, children, }: ProposalModuleAdapterCommonProviderProps) => { - const { dao } = useDaoContext() + const dao = useDao() const context = useMemo( () => matchAndLoadCommonContext(dao, proposalModuleAddress), [dao, proposalModuleAddress] diff --git a/packages/stateful/voting-module-adapter/README.md b/packages/stateful/voting-module-adapter/README.md index 4a2df1085..8a169977a 100644 --- a/packages/stateful/voting-module-adapter/README.md +++ b/packages/stateful/voting-module-adapter/README.md @@ -141,52 +141,3 @@ const MyVotingModuleAdapter: VotingModuleAdapter = { }), } ``` - -There's one more thing to be aware of when writing adapters... the -`useVotingModuleAdapterOptions` hook! - -### **useVotingModuleAdapterOptions** - -This hook simply provides the `options` passed to the -`VotingModuleAdapterProvider`, so you can easily access the `coreAddress` as -well as other common info instead of needing to manually pass them around. - -Example: - -<details> -<summary>`DaoVotingCw4/hooks/useMainDaoInfoCards.ts`</summary> - -```tsx -import { PeopleAltOutlined } from '@mui/icons-material' -import { useTranslation } from 'react-i18next' - -import { DaoInfoBarItem } from '@dao-dao/stateless' - -// IMPORT HOOK: -import { useVotingModuleAdapterOptions } from '../../../react/context' -// OR: -// import { useVotingModuleAdapterOptions } from '@dao-dao/stateful/voting-module-adapter/react/context' - -import { useVotingModule } from './useVotingModule' - -export const useMainDaoInfoCards = (): DaoInfoBarItem[] => { - const { t } = useTranslation() - // USE HOOK TO GET `coreAddress` FROM OPTIONS: - const { coreAddress } = useVotingModuleAdapterOptions() - const { members } = useVotingModule(coreAddress, { fetchMembers: true }) - - if (!members) { - throw new Error(t('error.loadingData')) - } - - return [ - { - Icon: PeopleAltOutlined, - label: t('title.members'), - value: members.length, - }, - ] -} -``` - -</details> diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/MembersTab.tsx index 45ef66a56..fe5b2267d 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/MembersTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/MembersTab.tsx @@ -3,7 +3,10 @@ import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { indexerQueries } from '@dao-dao/state/query' -import { MembersTab as StatelessMembersTab } from '@dao-dao/stateless' +import { + MembersTab as StatelessMembersTab, + useVotingModule, +} from '@dao-dao/stateless' import { StatefulDaoMemberCardProps } from '@dao-dao/types' import { @@ -12,12 +15,11 @@ import { EntityDisplay, } from '../../../../components' import { useQueryLoadingDataWithError } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { useGovernanceTokenInfo } from '../hooks/useGovernanceTokenInfo' export const MembersTab = () => { const { t } = useTranslation() - const { votingModule } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const { governanceToken } = useGovernanceTokenInfo() const queryClient = useQueryClient() diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx index 3bc754910..b9cb93c25 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/ProfileCardMemberInfo.tsx @@ -10,11 +10,7 @@ import { blocksPerYearSelector, stakingLoadingAtom, } from '@dao-dao/state' -import { - useCachedLoadable, - useChain, - useDaoInfoContext, -} from '@dao-dao/stateless' +import { useCachedLoadable, useChain, useDao } from '@dao-dao/stateless' import { BaseProfileCardMemberInfoProps, UnstakingTask, @@ -42,7 +38,7 @@ export const ProfileCardMemberInfo = ({ }: BaseProfileCardMemberInfoProps) => { const { t } = useTranslation() const { chain_id: chainId } = useChain() - const { name: daoName } = useDaoInfoContext() + const { name: daoName } = useDao() const { address: walletAddress, isWalletConnected, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx index b091f8ea0..e601b3c92 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/components/StakingModal.tsx @@ -20,6 +20,7 @@ import { ModalLoader, StakingModal as StatelessStakingModal, useCachedLoadable, + useVotingModule, } from '@dao-dao/stateless' import { BaseStakingModalProps, StakingMode } from '@dao-dao/types' import { encodeJsonToBase64, processError } from '@dao-dao/utils' @@ -32,7 +33,6 @@ import { useAwaitNextBlock, useWallet, } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { useGovernanceTokenInfo, useStakingInfo } from '../hooks' export const StakingModal = (props: BaseStakingModalProps) => ( @@ -55,7 +55,7 @@ const InnerStakingModal = ({ isWalletConnected, refreshBalances, } = useWallet() - const { chainId, coreAddress } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const [stakingLoading, setStakingLoading] = useRecoilState(stakingLoadingAtom) @@ -79,16 +79,16 @@ const InnerStakingModal = ({ useRecoilValue( waitForAll([ Cw20StakeSelectors.isOraichainProxySnapshotContractSelector({ - chainId, + chainId: votingModule.chainId, contractAddress: stakingContractAddress, }), Cw20StakeSelectors.totalStakedAtHeightSelector({ - chainId, + chainId: votingModule.chainId, contractAddress: stakingContractAddress, params: [{}], }), Cw20StakeSelectors.totalValueSelector({ - chainId, + chainId: votingModule.chainId, contractAddress: stakingContractAddress, params: [], }), @@ -98,7 +98,7 @@ const InnerStakingModal = ({ const oraichainCw20StakingConfig = useRecoilValue( isOraichainCustomStaking ? Cw20StakeSelectors.oraichainProxySnapshotConfigSelector({ - chainId, + chainId: votingModule.chainId, contractAddress: stakingContractAddress, }) : constSelector(undefined) @@ -114,7 +114,7 @@ const InnerStakingModal = ({ const walletStakedBalanceLoadable = useCachedLoadable( walletAddress ? Cw20StakeSelectors.stakedBalanceAtHeightSelector({ - chainId, + chainId: votingModule.chainId, contractAddress: stakingContractAddress, params: [{ address: walletAddress }], }) @@ -146,7 +146,7 @@ const InnerStakingModal = ({ }) const setRefreshDaoVotingPower = useSetRecoilState( - refreshDaoVotingPowerAtom(coreAddress) + refreshDaoVotingPowerAtom(votingModule.dao.coreAddress) ) const setRefreshFollowedDaos = useSetRecoilState(refreshFollowingDaosAtom) const refreshDaoVotingPower = () => { diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useGovernanceTokenInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useGovernanceTokenInfo.ts index ce9b64859..854f61ba1 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useGovernanceTokenInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useGovernanceTokenInfo.ts @@ -9,7 +9,7 @@ import { tokenQueries, usdPriceSelector, } from '@dao-dao/state' -import { useCachedLoading, useDaoContext } from '@dao-dao/stateless' +import { useCachedLoading, useDao } from '@dao-dao/stateless' import { TokenType } from '@dao-dao/types' import { useWallet } from '../../../../hooks/useWallet' @@ -23,7 +23,7 @@ export const useGovernanceTokenInfo = ({ fetchTreasuryBalance = false, fetchUsdcPrice = false, }: UseGovernanceTokenInfoOptions = {}): UseGovernanceTokenInfoResponse => { - const { dao } = useDaoContext() + const dao = useDao() const { address: walletAddress } = useWallet() const queryClient = useQueryClient() diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useMainDaoInfoCards.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useMainDaoInfoCards.tsx index 6b6c0714b..084070575 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useMainDaoInfoCards.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useMainDaoInfoCards.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { indexerQueries } from '@dao-dao/state' -import { TokenAmountDisplay } from '@dao-dao/stateless' +import { TokenAmountDisplay, useVotingModule } from '@dao-dao/stateless' import { DaoInfoCard } from '@dao-dao/types' import { convertDurationToHumanReadableString, @@ -12,13 +12,12 @@ import { } from '@dao-dao/utils' import { useMembership, useQueryLoadingDataWithError } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { useGovernanceTokenInfo } from './useGovernanceTokenInfo' import { useStakingInfo } from './useStakingInfo' export const useMainDaoInfoCards = (): DaoInfoCard[] => { const { t } = useTranslation() - const { votingModule } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const { totalVotingWeight } = useMembership() const { unstakingDuration } = useStakingInfo() diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useStakingInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useStakingInfo.ts index facddfbb2..755e4eadc 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useStakingInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw20Staked/hooks/useStakingInfo.ts @@ -11,11 +11,7 @@ import { refreshClaimsIdAtom, refreshWalletBalancesIdAtom, } from '@dao-dao/state' -import { - useCachedLoadable, - useCachedLoading, - useDaoContext, -} from '@dao-dao/stateless' +import { useCachedLoadable, useCachedLoading, useDao } from '@dao-dao/stateless' import { claimAvailable } from '@dao-dao/utils' import { useWallet } from '../../../../hooks/useWallet' @@ -26,7 +22,7 @@ export const useStakingInfo = ({ fetchTotalStakedValue = false, fetchWalletStakedValue = false, }: UseStakingInfoOptions = {}): UseStakingInfoResponse => { - const { dao } = useDaoContext() + const dao = useDao() const { address: walletAddress } = useWallet() const queryClient = useQueryClient() diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/index.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/index.tsx index d657fe9b3..26dfe34d9 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/index.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/actions/ManageMembers/index.tsx @@ -1,5 +1,5 @@ import { daoVotingCw4Queries } from '@dao-dao/state/query' -import { ActionBase, PeopleEmoji, useActionOptions } from '@dao-dao/stateless' +import { ActionBase, PeopleEmoji } from '@dao-dao/stateless' import { UnifiedCosmosMsg } from '@dao-dao/types' import { ActionComponent, @@ -16,16 +16,14 @@ import { import { Cw4VotingModule } from '../../../../../clients' import { AddressInput, EntityDisplay } from '../../../../../components' -import { useLoadingVotingModule } from '../../hooks/useLoadingVotingModule' +import { useLoadingVotingModuleInfo } from '../../hooks/useLoadingVotingModuleInfo' import { ManageMembersData, ManageMembersComponent as StatelessManageMembersComponent, } from './Component' const Component: ActionComponent = (props) => { - const { address } = useActionOptions() - - const votingModule = useLoadingVotingModule(address, { + const loadingMembers = useLoadingVotingModuleInfo({ fetchMembers: true, }) @@ -34,11 +32,12 @@ const Component: ActionComponent = (props) => { {...props} options={{ currentMembers: - votingModule.loading || votingModule.errored + loadingMembers.loading || loadingMembers.errored ? { loading: true } : { loading: false, - data: votingModule.data.members?.map(({ addr }) => addr) || [], + data: + loadingMembers.data.members?.map(({ addr }) => addr) || [], }, AddressInput, EntityDisplay, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/components/MainDaoInfoCardsLoader.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/components/MainDaoInfoCardsLoader.tsx index 251f0d407..46bd12ba6 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/components/MainDaoInfoCardsLoader.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/components/MainDaoInfoCardsLoader.tsx @@ -1,11 +1,11 @@ import { useTranslation } from 'react-i18next' -import { DaoInfoCards, useDaoInfoContext } from '@dao-dao/stateless' +import { DaoInfoCards, useDao } from '@dao-dao/stateless' import { formatDate } from '@dao-dao/utils' export const MainDaoInfoCardsLoader = () => { const { t } = useTranslation() - const { activeThreshold, created } = useDaoInfoContext() + const { activeThreshold, created } = useDao().info return ( <DaoInfoCards diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/components/MembersTab.tsx index 4fc8b5a9b..74a8ee7af 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/components/MembersTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/components/MembersTab.tsx @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next' import { MembersTab as StatelessMembersTab, useDaoNavHelpers, + useVotingModule, } from '@dao-dao/stateless' import { ActionKey, @@ -13,29 +14,28 @@ import { getDaoProposalSinglePrefill } from '@dao-dao/utils' import { ButtonLink, DaoMemberCard } from '../../../../components' import { useMembership } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' -import { useLoadingVotingModule } from '../hooks/useLoadingVotingModule' +import { useLoadingVotingModuleInfo } from '../hooks/useLoadingVotingModuleInfo' export const MembersTab = () => { const { t } = useTranslation() - const { coreAddress } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const { getDaoProposalPath } = useDaoNavHelpers() const { isMember = false, totalVotingWeight } = useMembership() - const votingModule = useLoadingVotingModule(coreAddress, { + const loadingMembers = useLoadingVotingModuleInfo({ fetchMembers: true, }) const members: LoadingDataWithError<StatefulDaoMemberCardProps[]> = - votingModule.loading + loadingMembers.loading ? { loading: true, errored: false } - : votingModule.errored - ? { loading: false, errored: true, error: votingModule.error } + : loadingMembers.errored + ? { loading: false, errored: true, error: loadingMembers.error } : { loading: false, errored: false, data: - votingModule.data.members?.map(({ addr, weight }) => ({ + loadingMembers.data.members?.map(({ addr, weight }) => ({ address: addr, balanceLabel: t('title.votingWeight'), balance: { @@ -58,19 +58,23 @@ export const MembersTab = () => { <StatelessMembersTab ButtonLink={ButtonLink} DaoMemberCard={DaoMemberCard} - addMemberHref={getDaoProposalPath(coreAddress, 'create', { - prefill: getDaoProposalSinglePrefill({ - actions: [ - { - actionKey: ActionKey.ManageMembers, - data: { - toAdd: [{ addr: '', weight: NaN }], - toRemove: [], + addMemberHref={getDaoProposalPath( + votingModule.dao.coreAddress, + 'create', + { + prefill: getDaoProposalSinglePrefill({ + actions: [ + { + actionKey: ActionKey.ManageMembers, + data: { + toAdd: [{ addr: '', weight: NaN }], + toRemove: [], + }, }, - }, - ], - }), - })} + ], + }), + } + )} isMember={isMember} members={members} topVoters={{ diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/components/ProfileCardMemberInfo/index.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/components/ProfileCardMemberInfo/index.tsx index dc80113d1..3ec0852b2 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/components/ProfileCardMemberInfo/index.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/components/ProfileCardMemberInfo/index.tsx @@ -1,4 +1,4 @@ -import { useDaoInfoContext } from '@dao-dao/stateless' +import { useDao } from '@dao-dao/stateless' import { BaseProfileCardMemberInfoProps } from '@dao-dao/types' import { useMembership } from '../../../../../hooks' @@ -8,7 +8,7 @@ export const ProfileCardMemberInfo = ({ maxGovernanceTokenDeposit: _, ...props }: BaseProfileCardMemberInfoProps) => { - const { name: daoName } = useDaoInfoContext() + const { name: daoName } = useDao() const { walletVotingWeight, totalVotingWeight } = useMembership() diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/index.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/index.ts index 6138de210..ef775edc7 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/index.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/index.ts @@ -1,3 +1,3 @@ -export * from './useLoadingVotingModule' +export * from './useLoadingVotingModuleInfo' export * from './useMainDaoInfoCards' export * from './useVotingModuleRelevantAddresses' diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/useLoadingVotingModule.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/useLoadingVotingModuleInfo.ts similarity index 53% rename from packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/useLoadingVotingModule.ts rename to packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/useLoadingVotingModuleInfo.ts index 4faf8a8b1..09fcc1f9a 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/useLoadingVotingModule.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/useLoadingVotingModuleInfo.ts @@ -1,48 +1,33 @@ import { useQueryClient } from '@tanstack/react-query' import { useMemo } from 'react' -import { - cw4GroupExtraQueries, - daoDaoCoreQueries, - daoVotingCw4Queries, -} from '@dao-dao/state' -import { useChain } from '@dao-dao/stateless' +import { cw4GroupExtraQueries, daoVotingCw4Queries } from '@dao-dao/state' +import { useVotingModule } from '@dao-dao/stateless' import { LoadingDataWithError } from '@dao-dao/types' import { Member } from '@dao-dao/types/contracts/DaoVotingCw4' import { useQueryLoadingDataWithError } from '../../../../hooks' -interface UseVotingModuleOptions { +type UseVotingModuleInfoOptions = { fetchMembers?: boolean } -interface UseVotingModuleReturn { - votingModuleAddress: string +type UseVotingModuleInfoReturn = { cw4GroupAddress: string members: Member[] | undefined } -export const useLoadingVotingModule = ( - coreAddress: string, - { fetchMembers }: UseVotingModuleOptions = {} -): LoadingDataWithError<UseVotingModuleReturn> => { - const { chain_id: chainId } = useChain() +export const useLoadingVotingModuleInfo = ({ + fetchMembers, +}: UseVotingModuleInfoOptions = {}): LoadingDataWithError<UseVotingModuleInfoReturn> => { + const votingModule = useVotingModule() const queryClient = useQueryClient() - const votingModuleAddress = useQueryLoadingDataWithError( - daoDaoCoreQueries.votingModule(queryClient, { - chainId, - contractAddress: coreAddress, - }) - ) - const cw4GroupAddress = useQueryLoadingDataWithError( - votingModuleAddress.loading || votingModuleAddress.errored - ? undefined - : daoVotingCw4Queries.groupContract(queryClient, { - chainId, - contractAddress: votingModuleAddress.data, - }) + daoVotingCw4Queries.groupContract(queryClient, { + chainId: votingModule.chainId, + contractAddress: votingModule.address, + }) ) const members = useQueryLoadingDataWithError( @@ -50,7 +35,7 @@ export const useLoadingVotingModule = ( ? cw4GroupAddress.loading || cw4GroupAddress.errored ? undefined : cw4GroupExtraQueries.listAllMembers(queryClient, { - chainId, + chainId: votingModule.chainId, address: cw4GroupAddress.data, }) : undefined @@ -58,22 +43,16 @@ export const useLoadingVotingModule = ( return useMemo( () => - votingModuleAddress.loading || - cw4GroupAddress.loading || - (fetchMembers && members.loading) + cw4GroupAddress.loading || (fetchMembers && members.loading) ? { loading: true, errored: false, } - : votingModuleAddress.errored || - cw4GroupAddress.errored || - (fetchMembers && members.errored) + : cw4GroupAddress.errored || (fetchMembers && members.errored) ? { loading: false, errored: true, - error: votingModuleAddress.errored - ? votingModuleAddress.error - : cw4GroupAddress.errored + error: cw4GroupAddress.errored ? cw4GroupAddress.error : fetchMembers && members.errored ? members.error @@ -83,11 +62,9 @@ export const useLoadingVotingModule = ( loading: false, errored: false, updating: - votingModuleAddress.updating || cw4GroupAddress.updating || (fetchMembers && !members.loading && members.updating), data: { - votingModuleAddress: votingModuleAddress.data, cw4GroupAddress: cw4GroupAddress.data, members: fetchMembers && !members.loading && !members.errored @@ -95,6 +72,6 @@ export const useLoadingVotingModule = ( : undefined, }, }, - [cw4GroupAddress, members, votingModuleAddress, fetchMembers] + [cw4GroupAddress, members, fetchMembers] ) } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/useMainDaoInfoCards.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/useMainDaoInfoCards.ts index 9459eaac2..40ae7b290 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/useMainDaoInfoCards.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/useMainDaoInfoCards.ts @@ -1,15 +1,16 @@ import { useTranslation } from 'react-i18next' +import { useChain } from '@dao-dao/stateless' import { DaoInfoCard } from '@dao-dao/types' import { isSecretNetwork } from '@dao-dao/utils' -import { useVotingModuleAdapterOptions } from '../../../react/context' -import { useLoadingVotingModule } from './useLoadingVotingModule' +import { useLoadingVotingModuleInfo } from './useLoadingVotingModuleInfo' export const useMainDaoInfoCards = (): DaoInfoCard[] => { const { t } = useTranslation() - const { chainId, coreAddress } = useVotingModuleAdapterOptions() - const votingModule = useLoadingVotingModule(coreAddress, { + const { chain_id: chainId } = useChain() + + const loadingMembers = useLoadingVotingModuleInfo({ fetchMembers: true, }) @@ -20,12 +21,12 @@ export const useMainDaoInfoCards = (): DaoInfoCard[] => { { label: t('title.members'), tooltip: t('info.membersTooltip'), - loading: votingModule.loading, - value: votingModule.loading + loading: loadingMembers.loading, + value: loadingMembers.loading ? undefined - : votingModule.errored + : loadingMembers.errored ? '<error>' - : votingModule.data.members?.length ?? '<error>', + : loadingMembers.data.members?.length ?? '<error>', }, ] } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/useVotingModuleRelevantAddresses.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/useVotingModuleRelevantAddresses.ts index 0a35302de..bfd94a4e2 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/useVotingModuleRelevantAddresses.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw4/hooks/useVotingModuleRelevantAddresses.ts @@ -2,23 +2,21 @@ import { useTranslation } from 'react-i18next' import { VotingModuleRelevantAddress } from '@dao-dao/types' -import { useVotingModuleAdapterOptions } from '../../../react/context' -import { useLoadingVotingModule } from './useLoadingVotingModule' +import { useLoadingVotingModuleInfo } from './useLoadingVotingModuleInfo' export const useVotingModuleRelevantAddresses = (): VotingModuleRelevantAddress[] => { const { t } = useTranslation() - const { coreAddress } = useVotingModuleAdapterOptions() - const votingModule = useLoadingVotingModule(coreAddress) + const loadingInfo = useLoadingVotingModuleInfo() return [ { label: t('info.groupAddress'), - address: votingModule.loading + address: loadingInfo.loading ? '...' - : votingModule.errored + : loadingInfo.errored ? '<error>' - : votingModule.data.cw4GroupAddress, + : loadingInfo.data.cw4GroupAddress, }, ] } diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/MembersTab.tsx index 0e6043c88..3072fd95f 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/MembersTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/MembersTab.tsx @@ -2,7 +2,10 @@ import { useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' import { daoVotingCw721StakedExtraQueries } from '@dao-dao/state/query' -import { MembersTab as StatelessMembersTab } from '@dao-dao/stateless' +import { + MembersTab as StatelessMembersTab, + useVotingModule, +} from '@dao-dao/stateless' import { StatefulDaoMemberCardProps } from '@dao-dao/types' import { @@ -14,11 +17,10 @@ import { useDaoGovernanceToken, useQueryLoadingDataWithError, } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' export const MembersTab = () => { const { t } = useTranslation() - const { votingModule } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const token = useDaoGovernanceToken() ?? undefined const queryClient = useQueryClient() diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx index 14f59886a..ecd5d28b3 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/ProfileCardMemberInfo.tsx @@ -9,11 +9,7 @@ import { blocksPerYearSelector, stakingLoadingAtom, } from '@dao-dao/state' -import { - useCachedLoadable, - useChain, - useDaoInfoContext, -} from '@dao-dao/stateless' +import { useCachedLoadable, useChain, useDao } from '@dao-dao/stateless' import { BaseProfileCardMemberInfoProps, UnstakingTask, @@ -40,7 +36,7 @@ export const ProfileCardMemberInfo = ({ }: BaseProfileCardMemberInfoProps) => { const { t } = useTranslation() const { chain_id: chainId } = useChain() - const { name: daoName } = useDaoInfoContext() + const { name: daoName } = useDao() const { address: walletAddress, isWalletConnected, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/StakingModal.tsx index cbda5d03e..a5f1b3b36 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/components/StakingModal.tsx @@ -8,7 +8,11 @@ import { refreshWalletBalancesIdAtom, stakingLoadingAtom, } from '@dao-dao/state' -import { ModalLoader, SegmentedControls } from '@dao-dao/stateless' +import { + ModalLoader, + SegmentedControls, + useVotingModule, +} from '@dao-dao/stateless' import { BaseStakingModalProps, LazyNftCardInfo, @@ -24,7 +28,6 @@ import { useAwaitNextBlock, useWallet, } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { useGovernanceCollectionInfo, useStakingInfo } from '../hooks' export const StakingModal = (props: BaseStakingModalProps) => ( @@ -41,10 +44,8 @@ const InnerStakingModal = ({ initialMode = StakingMode.Stake, }: BaseStakingModalProps) => { const { t } = useTranslation() - const { chainId, coreAddress } = useVotingModuleAdapterOptions() - const { address: walletAddress, isWalletConnected } = useWallet({ - chainId, - }) + const votingModule = useVotingModule() + const { address: walletAddress, isWalletConnected } = useWallet() const setRefreshWalletNftsId = useSetRecoilState( refreshWalletBalancesIdAtom(walletAddress) @@ -91,7 +92,7 @@ const InnerStakingModal = ({ }) const setRefreshDaoVotingPower = useSetRecoilState( - refreshDaoVotingPowerAtom(coreAddress) + refreshDaoVotingPowerAtom(votingModule.dao.coreAddress) ) const refreshDaoVotingPower = () => setRefreshDaoVotingPower((id) => id + 1) @@ -264,7 +265,7 @@ const InnerStakingModal = ({ onNftClick={onNftClick} onSelectAll={onSelectAll} selectedKeys={currentTokenIds.map((tokenId) => - getNftKey(chainId, collectionAddress, tokenId) + getNftKey(votingModule.chainId, collectionAddress, tokenId) )} unstakingDuration={unstakingDuration} visible={visible} diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useGovernanceCollectionInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useGovernanceCollectionInfo.ts index 76791e327..5963be3f5 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useGovernanceCollectionInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useGovernanceCollectionInfo.ts @@ -3,7 +3,7 @@ import { constSelector, useRecoilValue, waitForAll } from 'recoil' import { HugeDecimal } from '@dao-dao/math' import { CommonNftSelectors, daoVotingCw721StakedQueries } from '@dao-dao/state' -import { useCachedLoading, useDaoContext } from '@dao-dao/stateless' +import { useCachedLoading, useDao } from '@dao-dao/stateless' import { TokenType } from '@dao-dao/types' import { useWallet } from '../../../../hooks/useWallet' @@ -16,7 +16,7 @@ export const useGovernanceCollectionInfo = ({ fetchWalletBalance = false, fetchTreasuryBalance = false, }: UseGovernanceCollectionInfoOptions = {}): UseGovernanceCollectionInfoResponse => { - const { dao } = useDaoContext() + const dao = useDao() const { address: walletAddress } = useWallet() const queryClient = useQueryClient() diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useMainDaoInfoCards.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useMainDaoInfoCards.tsx index 2ffef1e89..b8f08060b 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useMainDaoInfoCards.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useMainDaoInfoCards.tsx @@ -2,7 +2,7 @@ import { useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' import { daoVotingCw721StakedExtraQueries } from '@dao-dao/state' -import { TokenAmountDisplay } from '@dao-dao/stateless' +import { TokenAmountDisplay, useVotingModule } from '@dao-dao/stateless' import { DaoInfoCard } from '@dao-dao/types' import { convertDurationToHumanReadableString, @@ -11,13 +11,12 @@ import { } from '@dao-dao/utils' import { useQueryLoadingDataWithError } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { useGovernanceCollectionInfo } from './useGovernanceCollectionInfo' import { useStakingInfo } from './useStakingInfo' export const useMainDaoInfoCards = (): DaoInfoCard[] => { const { t } = useTranslation() - const { votingModule } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const { loadingTotalStakedValue, unstakingDuration } = useStakingInfo({ fetchTotalStakedValue: true, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useStakingInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useStakingInfo.ts index 3bd53b998..93f79ff6f 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useStakingInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingCw721Staked/hooks/useStakingInfo.ts @@ -20,7 +20,7 @@ import { useCachedLoadable, useCachedLoading, useCachedLoadingWithError, - useDaoContext, + useDao, } from '@dao-dao/stateless' import { NftClaim } from '@dao-dao/types/contracts/DaoVotingCw721Staked' import { claimAvailable } from '@dao-dao/utils' @@ -35,7 +35,7 @@ export const useStakingInfo = ({ fetchWalletStakedValue = false, fetchWalletUnstakedNfts = false, }: UseStakingInfoOptions = {}): UseStakingInfoResponse => { - const { dao } = useDaoContext() + const dao = useDao() const { address: walletAddress } = useWallet() const { collectionAddress: governanceTokenAddress } = diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/MembersTab.tsx index d3b3a8f43..ed3ae5be6 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/MembersTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/MembersTab.tsx @@ -2,7 +2,10 @@ import { useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' import { daoVotingOnftStakedExtraQueries } from '@dao-dao/state' -import { MembersTab as StatelessMembersTab } from '@dao-dao/stateless' +import { + MembersTab as StatelessMembersTab, + useVotingModule, +} from '@dao-dao/stateless' import { StatefulDaoMemberCardProps } from '@dao-dao/types' import { @@ -14,11 +17,10 @@ import { useDaoGovernanceToken, useQueryLoadingDataWithError, } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' export const MembersTab = () => { const { t } = useTranslation() - const { votingModule } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const token = useDaoGovernanceToken() ?? undefined const queryClient = useQueryClient() diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx index ba8d2f7fe..db0cd5f85 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/ProfileCardMemberInfo.tsx @@ -9,11 +9,7 @@ import { blocksPerYearSelector, stakingLoadingAtom, } from '@dao-dao/state' -import { - useCachedLoadable, - useChain, - useDaoInfoContext, -} from '@dao-dao/stateless' +import { useCachedLoadable, useChain, useDao } from '@dao-dao/stateless' import { BaseProfileCardMemberInfoProps, UnstakingTask, @@ -38,7 +34,7 @@ export const ProfileCardMemberInfo = ({ }: BaseProfileCardMemberInfoProps) => { const { t } = useTranslation() const { chain_id: chainId } = useChain() - const { name: daoName } = useDaoInfoContext() + const { name: daoName } = useDao() const { address: walletAddress, isWalletConnected, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/StakingModal.tsx index 57885399c..56e89cf29 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/components/StakingModal.tsx @@ -5,11 +5,7 @@ import { useTranslation } from 'react-i18next' import { useRecoilState } from 'recoil' import { stakingLoadingAtom } from '@dao-dao/state' -import { - ModalLoader, - SegmentedControls, - useDaoContext, -} from '@dao-dao/stateless' +import { ModalLoader, SegmentedControls, useDao } from '@dao-dao/stateless' import { BaseStakingModalProps, LazyNftCardInfo, @@ -47,7 +43,7 @@ const InnerStakingModal = ({ initialMode = StakingMode.Stake, }: BaseStakingModalProps) => { const { t } = useTranslation() - const { dao } = useDaoContext() + const dao = useDao() const { address: walletAddress, isWalletConnected, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useGovernanceCollectionInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useGovernanceCollectionInfo.ts index 2835f3868..398c3ce23 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useGovernanceCollectionInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useGovernanceCollectionInfo.ts @@ -6,11 +6,11 @@ import { import { HugeDecimal } from '@dao-dao/math' import { daoVotingOnftStakedQueries, omniflixQueries } from '@dao-dao/state' +import { useVotingModule } from '@dao-dao/stateless' import { TokenType } from '@dao-dao/types' import { useQueryLoadingDataWithError } from '../../../../hooks' import { useWallet } from '../../../../hooks/useWallet' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { UseGovernanceCollectionInfoOptions, UseGovernanceCollectionInfoResponse, @@ -20,7 +20,7 @@ export const useGovernanceCollectionInfo = ({ fetchWalletBalance = false, fetchTreasuryBalance = false, }: UseGovernanceCollectionInfoOptions = {}): UseGovernanceCollectionInfoResponse => { - const { coreAddress, votingModule } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const { address: walletAddress } = useWallet() const queryClient = useQueryClient() @@ -73,7 +73,7 @@ export const useGovernanceCollectionInfo = ({ ? { chainId: votingModule.chainId, id: onft_collection_id, - owner: coreAddress, + owner: votingModule.dao.coreAddress, } : undefined ) diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useMainDaoInfoCards.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useMainDaoInfoCards.tsx index 7c9d00759..65e00afb6 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useMainDaoInfoCards.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useMainDaoInfoCards.tsx @@ -2,7 +2,7 @@ import { useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' import { daoVotingOnftStakedExtraQueries } from '@dao-dao/state' -import { TokenAmountDisplay } from '@dao-dao/stateless' +import { TokenAmountDisplay, useVotingModule } from '@dao-dao/stateless' import { DaoInfoCard } from '@dao-dao/types' import { convertDurationToHumanReadableString, @@ -10,13 +10,12 @@ import { } from '@dao-dao/utils' import { useQueryLoadingDataWithError } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { useGovernanceCollectionInfo } from './useGovernanceCollectionInfo' import { useStakingInfo } from './useStakingInfo' export const useMainDaoInfoCards = (): DaoInfoCard[] => { const { t } = useTranslation() - const { votingModule } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const { loadingTotalStakedValue, unstakingDuration } = useStakingInfo({ fetchTotalStakedValue: true, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useStakingInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useStakingInfo.ts index 9e2600640..1fb26790d 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useStakingInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingOnftStaked/hooks/useStakingInfo.ts @@ -15,7 +15,7 @@ import { import { useCachedLoadable, useCachedLoadingWithError, - useDaoContext, + useDao, } from '@dao-dao/stateless' import { NftClaim } from '@dao-dao/types/contracts/DaoVotingOnftStaked' import { claimAvailable, parseContractVersion } from '@dao-dao/utils' @@ -34,7 +34,7 @@ export const useStakingInfo = ({ fetchWalletStakedValue = false, fetchWalletUnstakedNfts = false, }: UseStakingInfoOptions = {}): UseStakingInfoResponse => { - const { dao } = useDaoContext() + const dao = useDao() const { address: walletAddress } = useWallet() const { collectionAddress } = useGovernanceCollectionInfo() const queryClient = useQueryClient() diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/components/MembersTab.tsx index 747bd0d2f..10fc4ef4a 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/components/MembersTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/components/MembersTab.tsx @@ -2,7 +2,10 @@ import { useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' import { daoVotingSgCommunityNftExtraQueries } from '@dao-dao/state' -import { MembersTab as StatelessMembersTab } from '@dao-dao/stateless' +import { + MembersTab as StatelessMembersTab, + useVotingModule, +} from '@dao-dao/stateless' import { StatefulDaoMemberCardProps } from '@dao-dao/types' import { @@ -11,11 +14,10 @@ import { EntityDisplay, } from '../../../../components' import { useQueryLoadingDataWithError } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' export const MembersTab = () => { const { t } = useTranslation() - const { votingModule } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const queryClient = useQueryClient() const members = useQueryLoadingDataWithError( diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/components/ProfileCardMemberInfo.tsx index 00291b00e..143925dd8 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/components/ProfileCardMemberInfo.tsx @@ -10,7 +10,7 @@ import { daoVotingSgCommunityNftQueries, indexerQueries, } from '@dao-dao/state/query' -import { Button, useDaoContext } from '@dao-dao/stateless' +import { Button, useDao } from '@dao-dao/stateless' import { BaseProfileCardMemberInfoProps, LoadingDataWithError, @@ -33,7 +33,7 @@ export const ProfileCardMemberInfo = ({ cantVoteOnProposal, }: BaseProfileCardMemberInfoProps) => { const { t } = useTranslation() - const { dao } = useDaoContext() + const dao = useDao() const { address: walletAddress, isWalletConnected, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/hooks/useMainDaoInfoCards.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/hooks/useMainDaoInfoCards.ts index 5ddb7c0fd..b6e1ff79b 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/hooks/useMainDaoInfoCards.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingSgCommunityNft/hooks/useMainDaoInfoCards.ts @@ -2,14 +2,14 @@ import { useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' import { daoVotingSgCommunityNftExtraQueries } from '@dao-dao/state' +import { useVotingModule } from '@dao-dao/stateless' import { DaoInfoCard } from '@dao-dao/types' import { useQueryLoadingDataWithError } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' export const useMainDaoInfoCards = (): DaoInfoCard[] => { const { t } = useTranslation() - const { votingModule } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const queryClient = useQueryClient() const loadingMembers = useQueryLoadingDataWithError( diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/MembersTab.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/MembersTab.tsx index 48df76de1..ce8d60f40 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/MembersTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/MembersTab.tsx @@ -3,7 +3,10 @@ import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { indexerQueries } from '@dao-dao/state/query' -import { MembersTab as StatelessMembersTab } from '@dao-dao/stateless' +import { + MembersTab as StatelessMembersTab, + useVotingModule, +} from '@dao-dao/stateless' import { StatefulDaoMemberCardProps } from '@dao-dao/types' import { TokenStakedVotingModule } from '../../../../clients' @@ -13,12 +16,11 @@ import { EntityDisplay, } from '../../../../components' import { useQueryLoadingDataWithError } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { useGovernanceTokenInfo } from '../hooks/useGovernanceTokenInfo' export const MembersTab = () => { const { t } = useTranslation() - const { votingModule } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const { governanceToken } = useGovernanceTokenInfo() const queryClient = useQueryClient() diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx index ef9d8b190..560c3574f 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/ProfileCardMemberInfo.tsx @@ -9,7 +9,7 @@ import { blocksPerYearSelector, stakingLoadingAtom, } from '@dao-dao/state' -import { useCachedLoadable, useDaoInfoContext } from '@dao-dao/stateless' +import { useCachedLoadable, useDao, useVotingModule } from '@dao-dao/stateless' import { BaseProfileCardMemberInfoProps, UnstakingTask, @@ -27,7 +27,6 @@ import { useWallet, } from '../../../../hooks' import { ProfileCardMemberInfoTokens } from '../../../components' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { useGovernanceTokenInfo, useStakingInfo } from '../hooks' import { StakingModal } from './StakingModal' @@ -36,8 +35,8 @@ export const ProfileCardMemberInfo = ({ ...props }: BaseProfileCardMemberInfoProps) => { const { t } = useTranslation() - const { name: daoName } = useDaoInfoContext() - const { votingModule } = useVotingModuleAdapterOptions() + const { name: daoName } = useDao() + const votingModule = useVotingModule() const { address: walletAddress, isWalletConnected, diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx index 148768d90..a6cee4f04 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/components/StakingModal.tsx @@ -12,6 +12,7 @@ import { import { ModalLoader, StakingModal as StatelessStakingModal, + useVotingModule, } from '@dao-dao/stateless' import { BaseStakingModalProps, StakingMode } from '@dao-dao/types' import { CHAIN_GAS_MULTIPLIER, processError } from '@dao-dao/utils' @@ -22,7 +23,6 @@ import { useAwaitNextBlock, useWallet, } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { useGovernanceTokenInfo, useStakingInfo } from '../hooks' export const StakingModal = (props: BaseStakingModalProps) => ( @@ -45,7 +45,7 @@ const InnerStakingModal = ({ isWalletConnected, refreshBalances, } = useWallet() - const { coreAddress, votingModule } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const [stakingLoading, setStakingLoading] = useRecoilState(stakingLoadingAtom) @@ -80,7 +80,7 @@ const InnerStakingModal = ({ }) const setRefreshDaoVotingPower = useSetRecoilState( - refreshDaoVotingPowerAtom(coreAddress) + refreshDaoVotingPowerAtom(votingModule.dao.coreAddress) ) const setRefreshFollowedDaos = useSetRecoilState(refreshFollowingDaosAtom) const refreshDaoVotingPower = () => { diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useGovernanceTokenInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useGovernanceTokenInfo.ts index 87ca3b6f0..443d9429e 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useGovernanceTokenInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useGovernanceTokenInfo.ts @@ -9,12 +9,11 @@ import { nativeSupplySelector, usdPriceSelector, } from '@dao-dao/state' -import { useCachedLoading } from '@dao-dao/stateless' +import { useCachedLoading, useVotingModule } from '@dao-dao/stateless' import { TokenType } from '@dao-dao/types' import { TokenStakedVotingModule } from '../../../../clients' import { useWallet } from '../../../../hooks/useWallet' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { UseGovernanceTokenInfoOptions, UseGovernanceTokenInfoResponse, @@ -25,9 +24,9 @@ export const useGovernanceTokenInfo = ({ fetchTreasuryBalance = false, fetchUsdcPrice = false, }: UseGovernanceTokenInfoOptions = {}): UseGovernanceTokenInfoResponse => { - const { chainId, coreAddress, votingModule } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const { address: walletAddress } = useWallet({ - chainId, + chainId: votingModule.chainId, }) const isTokenStaked = votingModule instanceof TokenStakedVotingModule @@ -35,12 +34,12 @@ export const useGovernanceTokenInfo = ({ const { denom } = useRecoilValue( isTokenStaked ? DaoVotingTokenStakedSelectors.denomSelector({ - chainId, + chainId: votingModule.chainId, contractAddress: votingModule.address, params: [], }) : DaoVotingNativeStakedSelectors.getConfigSelector({ - chainId, + chainId: votingModule.chainId, contractAddress: votingModule.address, params: [], }) @@ -49,18 +48,18 @@ export const useGovernanceTokenInfo = ({ const [governanceToken, supply, tokenFactoryIssuerAddress] = useRecoilValue( waitForAll([ genericTokenSelector({ - chainId, + chainId: votingModule.chainId, type: TokenType.Native, denomOrAddress: denom, }), nativeSupplySelector({ - chainId, + chainId: votingModule.chainId, denom, }), isTokenStaked ? DaoVotingTokenStakedSelectors.validatedTokenfactoryIssuerContractSelector( { - chainId, + chainId: votingModule.chainId, contractAddress: votingModule.address, } ) @@ -74,7 +73,7 @@ export const useGovernanceTokenInfo = ({ const loadingWalletBalance = useCachedLoading( fetchWalletBalance && walletAddress ? nativeDenomBalanceSelector({ - chainId, + chainId: votingModule.chainId, walletAddress, denom, }) @@ -86,8 +85,8 @@ export const useGovernanceTokenInfo = ({ const loadingTreasuryBalance = useCachedLoading( fetchTreasuryBalance ? nativeDenomBalanceSelector({ - chainId, - walletAddress: coreAddress, + chainId: votingModule.chainId, + walletAddress: votingModule.dao.coreAddress, denom, }) : constSelector(undefined), @@ -99,7 +98,7 @@ export const useGovernanceTokenInfo = ({ fetchUsdcPrice ? usdPriceSelector({ type: TokenType.Native, - chainId, + chainId: votingModule.chainId, denomOrAddress: denom, }) : constSelector(undefined), diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useMainDaoInfoCards.tsx b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useMainDaoInfoCards.tsx index 165cd571c..4e3d22b57 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useMainDaoInfoCards.tsx +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useMainDaoInfoCards.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import { HugeDecimal } from '@dao-dao/math' import { indexerQueries } from '@dao-dao/state' -import { TokenAmountDisplay } from '@dao-dao/stateless' +import { TokenAmountDisplay, useVotingModule } from '@dao-dao/stateless' import { DaoInfoCard } from '@dao-dao/types' import { convertDurationToHumanReadableString, @@ -12,13 +12,12 @@ import { import { TokenStakedVotingModule } from '../../../../clients' import { useQueryLoadingDataWithError } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { useGovernanceTokenInfo } from './useGovernanceTokenInfo' import { useStakingInfo } from './useStakingInfo' export const useMainDaoInfoCards = (): DaoInfoCard[] => { const { t } = useTranslation() - const { chainId, votingModule } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const { loadingTotalStakedValue, unstakingDuration } = useStakingInfo({ fetchTotalStakedValue: true, }) @@ -35,7 +34,7 @@ export const useMainDaoInfoCards = (): DaoInfoCard[] => { const queryClient = useQueryClient() const loadingMembers = useQueryLoadingDataWithError( indexerQueries.queryContract(queryClient, { - chainId, + chainId: votingModule.chainId, contractAddress: votingModule.address, formula: votingModule instanceof TokenStakedVotingModule @@ -47,7 +46,7 @@ export const useMainDaoInfoCards = (): DaoInfoCard[] => { return [ // Can't view members on Secret Network. - ...(isSecretNetwork(chainId) + ...(isSecretNetwork(votingModule.chainId) ? [] : [ { diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useStakingInfo.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useStakingInfo.ts index c38d47604..ca174be0c 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useStakingInfo.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/hooks/useStakingInfo.ts @@ -11,12 +11,15 @@ import { refreshClaimsIdAtom, refreshWalletBalancesIdAtom, } from '@dao-dao/state' -import { useCachedLoadable, useCachedLoading } from '@dao-dao/stateless' +import { + useCachedLoadable, + useCachedLoading, + useVotingModule, +} from '@dao-dao/stateless' import { claimAvailable } from '@dao-dao/utils' import { TokenStakedVotingModule } from '../../../../clients' import { useWallet } from '../../../../hooks/useWallet' -import { useVotingModuleAdapterOptions } from '../../../react/context' import { UseStakingInfoOptions, UseStakingInfoResponse } from '../types' export const useStakingInfo = ({ @@ -24,7 +27,7 @@ export const useStakingInfo = ({ fetchTotalStakedValue = false, fetchWalletStakedValue = false, }: UseStakingInfoOptions = {}): UseStakingInfoResponse => { - const { votingModule } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() const { address: walletAddress } = useWallet() const queryClient = useQueryClient() diff --git a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/index.ts b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/index.ts index 801b140c9..5e6fac34c 100644 --- a/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/index.ts +++ b/packages/stateful/voting-module-adapter/adapters/DaoVotingTokenStaked/index.ts @@ -32,7 +32,7 @@ export const DaoVotingTokenStakedAdapter: VotingModuleAdapter = { ...DAO_VOTING_NATIVE_STAKED_CONTRACT_NAMES, ], - load: ({ chainId, votingModule }) => ({ + load: (votingModule) => ({ // Hooks hooks: { useMainDaoInfoCards, @@ -46,7 +46,7 @@ export const DaoVotingTokenStakedAdapter: VotingModuleAdapter = { StakingModal, // Can't view members on Secret Network. - extraTabs: isSecretNetwork(chainId) + extraTabs: isSecretNetwork(votingModule.chainId) ? undefined : [ { @@ -63,8 +63,8 @@ export const DaoVotingTokenStakedAdapter: VotingModuleAdapter = { fields: { actions: { actions: [ - ...(chainId === ChainId.BitsongMainnet || - chainId === ChainId.BitsongTestnet + ...(votingModule.chainId === ChainId.BitsongMainnet || + votingModule.chainId === ChainId.BitsongTestnet ? [BitSongFantokenMintAction] : [ votingModule instanceof TokenStakedVotingModule diff --git a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/MainDaoInfoCardsLoader.tsx b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/MainDaoInfoCardsLoader.tsx index fdd265aa0..4c713561c 100644 --- a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/MainDaoInfoCardsLoader.tsx +++ b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/MainDaoInfoCardsLoader.tsx @@ -1,11 +1,11 @@ import { useTranslation } from 'react-i18next' -import { DaoInfoCards, useDaoInfoContext } from '@dao-dao/stateless' +import { DaoInfoCards, useDao } from '@dao-dao/stateless' import { formatDate } from '@dao-dao/utils' export const MainDaoInfoCardsLoader = () => { const { t } = useTranslation() - const { activeThreshold, created } = useDaoInfoContext() + const { activeThreshold, created } = useDao().info return ( <DaoInfoCards diff --git a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/ProfileCardMemberInfo.tsx b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/ProfileCardMemberInfo.tsx index d3ebc7cdb..69c75a9c7 100644 --- a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/ProfileCardMemberInfo.tsx +++ b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/ProfileCardMemberInfo.tsx @@ -9,29 +9,26 @@ import { neutronVotingRegistryQueries, stakingLoadingAtom, } from '@dao-dao/state' -import { - useCachedLoadingWithError, - useDaoInfoContext, -} from '@dao-dao/stateless' +import { useCachedLoadingWithError, useDao } from '@dao-dao/stateless' import { BaseProfileCardMemberInfoProps } from '@dao-dao/types' import { makeCombineQueryResultsIntoLoadingDataWithError } from '@dao-dao/utils' import { useQueryLoadingDataWithError, useWallet } from '../../../../hooks' import { ProfileCardMemberInfoTokens } from '../../../components' -import { useVotingModule } from '../hooks' +import { useVotingModuleInfo } from '../hooks' import { StakingModal } from './StakingModal' export const ProfileCardMemberInfo = ({ maxGovernanceTokenDeposit, ...props }: BaseProfileCardMemberInfoProps) => { - const { name: daoName, chainId } = useDaoInfoContext() + const { name: daoName, chainId } = useDao() const { address } = useWallet() const [showStakingModal, setShowStakingModal] = useState(false) const stakingLoading = useRecoilValue(stakingLoadingAtom) - const { votingRegistryAddress, loadingVaults } = useVotingModule() + const { votingRegistryAddress, loadingVaults } = useVotingModuleInfo() const realVaults = loadingVaults.loading || loadingVaults.errored ? [] diff --git a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx index 4c19cf0c4..5f3d0986d 100644 --- a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx +++ b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/StakingModal.tsx @@ -17,6 +17,7 @@ import { ModalLoader, StakingModal as StatelessStakingModal, useCachedLoadingWithError, + useVotingModule, } from '@dao-dao/stateless' import { BaseStakingModalProps, @@ -36,8 +37,7 @@ import { useAwaitNextBlock, useWallet, } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' -import { useVotingModule } from '../hooks' +import { useVotingModuleInfo } from '../hooks' export const StakingModal = (props: BaseStakingModalProps) => ( <SuspenseLoader @@ -54,9 +54,9 @@ const InnerStakingModal = ({ }: BaseStakingModalProps) => { const { t } = useTranslation() const { address = '', isWalletConnected, refreshBalances } = useWallet() - const { coreAddress, chainId } = useVotingModuleAdapterOptions() + const votingModule = useVotingModule() - const { loadingVaults } = useVotingModule() + const { loadingVaults } = useVotingModuleInfo() const realVaults = loadingVaults.loading || loadingVaults.errored ? [] @@ -75,7 +75,7 @@ const InnerStakingModal = ({ ? [] : realVaults.map(({ address: contractAddress }) => neutronVaultQueries.bondingStatus({ - chainId, + chainId: votingModule.chainId, contractAddress, args: { address, @@ -138,7 +138,7 @@ const InnerStakingModal = ({ }) const setRefreshDaoVotingPower = useSetRecoilState( - refreshDaoVotingPowerAtom(coreAddress) + refreshDaoVotingPowerAtom(votingModule.dao.coreAddress) ) const setRefreshFollowedDaos = useSetRecoilState(refreshFollowingDaosAtom) const refreshDaoVotingPower = () => { diff --git a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/VaultsTab.tsx b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/VaultsTab.tsx index eb8fc945d..1e067fe49 100644 --- a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/VaultsTab.tsx +++ b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/components/VaultsTab.tsx @@ -3,11 +3,11 @@ import { NeutronVotingVaultsTab, useChain } from '@dao-dao/stateless' import { DaoVotingVaultCard } from '../../../../components' import { useQueryLoadingDataWithError } from '../../../../hooks' -import { useVotingModule } from '../hooks' +import { useVotingModuleInfo } from '../hooks' export const VaultsTab = () => { const { chain_id: chainId } = useChain() - const { votingRegistryAddress, loadingVaults } = useVotingModule() + const { votingRegistryAddress, loadingVaults } = useVotingModuleInfo() const loadingTotalVotingPower = useQueryLoadingDataWithError( neutronVotingRegistryQueries.totalPowerAtHeight({ chainId, diff --git a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/index.ts b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/index.ts index 3055dca9b..bcabdf232 100644 --- a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/index.ts +++ b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/index.ts @@ -1,2 +1,2 @@ export * from './useMainDaoInfoCards' -export * from './useVotingModule' +export * from './useVotingModuleInfo' diff --git a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useMainDaoInfoCards.tsx b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useMainDaoInfoCards.tsx index 16935b205..96bae5a04 100644 --- a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useMainDaoInfoCards.tsx +++ b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useMainDaoInfoCards.tsx @@ -3,12 +3,12 @@ import { useTranslation } from 'react-i18next' import { TokenAmountDisplay } from '@dao-dao/stateless' import { DaoInfoCard } from '@dao-dao/types' -import { useVotingModule } from './useVotingModule' +import { useVotingModuleInfo } from './useVotingModuleInfo' export const useMainDaoInfoCards = (): DaoInfoCard[] => { const { t } = useTranslation() - const { loadingVaults } = useVotingModule() + const { loadingVaults } = useVotingModuleInfo() return loadingVaults.loading || loadingVaults.errored ? [] diff --git a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useVotingModule.ts b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useVotingModuleInfo.ts similarity index 76% rename from packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useVotingModule.ts rename to packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useVotingModuleInfo.ts index d18c6702c..fee8423f1 100644 --- a/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useVotingModule.ts +++ b/packages/stateful/voting-module-adapter/adapters/NeutronVotingRegistry/hooks/useVotingModuleInfo.ts @@ -1,20 +1,20 @@ import { useQueryClient } from '@tanstack/react-query' import { neutronVotingRegistryExtraQueries } from '@dao-dao/state' +import { useVotingModule } from '@dao-dao/stateless' import { LoadingDataWithError, VotingVaultWithInfo } from '@dao-dao/types' import { useQueryLoadingDataWithError } from '../../../../hooks' -import { useVotingModuleAdapterOptions } from '../../../react/context' export type LoadingVaults = LoadingDataWithError<VotingVaultWithInfo[]> -export type UseVotingModuleReturn = { +export type UseVotingModuleInfoReturn = { votingRegistryAddress: string loadingVaults: LoadingVaults } -export const useVotingModule = (): UseVotingModuleReturn => { - const { votingModule } = useVotingModuleAdapterOptions() +export const useVotingModuleInfo = (): UseVotingModuleInfoReturn => { + const votingModule = useVotingModule() const queryClient = useQueryClient() const loadingVaults = useQueryLoadingDataWithError( diff --git a/packages/stateful/voting-module-adapter/core.ts b/packages/stateful/voting-module-adapter/core.ts index 746eeb977..2330d9234 100644 --- a/packages/stateful/voting-module-adapter/core.ts +++ b/packages/stateful/voting-module-adapter/core.ts @@ -1,7 +1,6 @@ import { IDaoBase, IVotingModuleAdapterContext, - IVotingModuleAdapterOptions, VotingModuleAdapter, } from '@dao-dao/types' @@ -58,16 +57,9 @@ export const matchAndLoadAdapter = ( ) } - const options: IVotingModuleAdapterOptions = { - chainId: dao.chainId, - coreAddress: dao.coreAddress, - votingModule: dao.votingModule, - } - return { id: adapter.id, - adapter: adapter.load(options), - options, + adapter: adapter.load(dao.votingModule), votingModule: dao.votingModule, } } diff --git a/packages/stateful/voting-module-adapter/react/context.ts b/packages/stateful/voting-module-adapter/react/context.ts index 0f3b58686..cddbdebb8 100644 --- a/packages/stateful/voting-module-adapter/react/context.ts +++ b/packages/stateful/voting-module-adapter/react/context.ts @@ -3,7 +3,6 @@ import { createContext, useContext } from 'react' import { IVotingModuleAdapter, IVotingModuleAdapterContext, - IVotingModuleAdapterOptions, } from '@dao-dao/types' // External API @@ -30,17 +29,3 @@ export const useVotingModuleAdapterContextIfAvailable = (): export const useVotingModuleAdapter = (): IVotingModuleAdapter => useVotingModuleAdapterContext().adapter - -// For internal use to pass around options. -export const useVotingModuleAdapterOptions = - (): IVotingModuleAdapterOptions => { - const context = useContext(VotingModuleAdapterContext) - - if (!context) { - throw new Error( - 'useVotingModuleAdapterOptions can only be used in a descendant of VotingModuleAdapterProvider.' - ) - } - - return context.options - } diff --git a/packages/stateful/voting-module-adapter/react/provider.tsx b/packages/stateful/voting-module-adapter/react/provider.tsx index 800da2bd0..3e7c11cad 100644 --- a/packages/stateful/voting-module-adapter/react/provider.tsx +++ b/packages/stateful/voting-module-adapter/react/provider.tsx @@ -1,6 +1,6 @@ import { ReactNode, useState } from 'react' -import { useDaoContext } from '@dao-dao/stateless' +import { useDao } from '@dao-dao/stateless' import { IVotingModuleAdapterContext } from '@dao-dao/types' import { matchAndLoadAdapter } from '../core' @@ -14,7 +14,7 @@ export const VotingModuleAdapterProvider = ({ }: { children: ReactNode }) => { - const { dao } = useDaoContext() + const dao = useDao() const [context] = useState<IVotingModuleAdapterContext>(() => matchAndLoadAdapter(dao) ) diff --git a/packages/stateful/widgets/react/useWidgets.tsx b/packages/stateful/widgets/react/useWidgets.tsx index e19891de2..b1200acb5 100644 --- a/packages/stateful/widgets/react/useWidgets.tsx +++ b/packages/stateful/widgets/react/useWidgets.tsx @@ -3,7 +3,7 @@ import { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { useChain, useDaoInfoContext } from '@dao-dao/stateless' +import { useChain, useDao } from '@dao-dao/stateless' import { LoadedWidget, LoadingData, @@ -28,7 +28,7 @@ export const useWidgets = ({ }: UseWidgetsOptions = {}): UseWidgetsResult => { const { t } = useTranslation() const { chain_id: chainId } = useChain() - const { items } = useDaoInfoContext() + const { items } = useDao().info const { isMember = false } = useMembership() const loadingWidgets = useMemo((): LoadingData<LoadedWidget[]> => { diff --git a/packages/stateful/widgets/widgets/Press/PressEditor.tsx b/packages/stateful/widgets/widgets/Press/PressEditor.tsx index b988493fa..01943ca54 100644 --- a/packages/stateful/widgets/widgets/Press/PressEditor.tsx +++ b/packages/stateful/widgets/widgets/Press/PressEditor.tsx @@ -9,7 +9,7 @@ import { CopyableAddress, DaoSupportedChainPickerInput, useChain, - useDaoInfoContext, + useDao, useSupportedChainContext, } from '@dao-dao/stateless' import { ActionKey, ChainId, WidgetEditorProps } from '@dao-dao/types' @@ -36,7 +36,10 @@ export const PressEditor = ({ config: { polytone }, } = useSupportedChainContext() const { chain_id: nativeChainId } = useChain() - const { name: daoName, polytoneProxies } = useDaoInfoContext() + const { + name: daoName, + info: { polytoneProxies }, + } = useDao() const { setValue, setError, clearErrors, watch } = useFormContext<PressData>() const chainId = watch((fieldNamePrefix + 'chainId') as 'chainId') diff --git a/packages/stateful/widgets/widgets/Press/Renderer/Renderer.tsx b/packages/stateful/widgets/widgets/Press/Renderer/Renderer.tsx index 1b558cca7..a7ed059b2 100644 --- a/packages/stateful/widgets/widgets/Press/Renderer/Renderer.tsx +++ b/packages/stateful/widgets/widgets/Press/Renderer/Renderer.tsx @@ -7,12 +7,7 @@ import { import { ComponentType, useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { - Button, - Tooltip, - useDaoInfoContext, - useDaoNavHelpers, -} from '@dao-dao/stateless' +import { Button, Tooltip, useDao, useDaoNavHelpers } from '@dao-dao/stateless' import { ButtonLinkProps, IconButtonLinkProps, @@ -46,7 +41,7 @@ export const Renderer = ({ IconButtonLink, }: RendererProps) => { const { t } = useTranslation() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { daoSubpathComponents, goToDao } = useDaoNavHelpers() const openPostId = diff --git a/packages/stateful/widgets/widgets/Press/Renderer/index.tsx b/packages/stateful/widgets/widgets/Press/Renderer/index.tsx index 1dbf4c638..e78a44d88 100644 --- a/packages/stateful/widgets/widgets/Press/Renderer/index.tsx +++ b/packages/stateful/widgets/widgets/Press/Renderer/index.tsx @@ -1,6 +1,6 @@ import { useCachedLoading, - useDaoInfoContext, + useDao, useDaoNavHelpers, useInitializedActionForKey, } from '@dao-dao/stateless' @@ -16,7 +16,7 @@ import { Renderer as StatelessRenderer } from './Renderer' export const Renderer = ({ variables: { chainId: configuredChainId, contract }, }: WidgetRendererProps<PressData>) => { - const { chainId: daoChainId, coreAddress } = useDaoInfoContext() + const { chainId: daoChainId, coreAddress } = useDao() const { getDaoProposalPath } = useDaoNavHelpers() const { isMember = false } = useMembership() diff --git a/packages/stateful/widgets/widgets/Press/actions/DeletePost/Component.tsx b/packages/stateful/widgets/widgets/Press/actions/DeletePost/Component.tsx index c44030624..afbc424a4 100644 --- a/packages/stateful/widgets/widgets/Press/actions/DeletePost/Component.tsx +++ b/packages/stateful/widgets/widgets/Press/actions/DeletePost/Component.tsx @@ -8,7 +8,7 @@ import { InputLabel, Loader, SelectInput, - useDaoInfoContext, + useDao, useDaoNavHelpers, } from '@dao-dao/stateless' import { ActionComponent, LoadingData } from '@dao-dao/types' @@ -35,7 +35,7 @@ export const DeletePostComponent: ActionComponent<DeletePostOptions> = ({ const { register, watch } = useFormContext<DeletePostData>() const id = watch((fieldNamePrefix + 'id') as 'id') - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { getDaoPath } = useDaoNavHelpers() return isCreating ? ( diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/SurveyRow.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/SurveyRow.tsx index 64b923329..cb8a278b1 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/SurveyRow.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/SurveyRow.tsx @@ -1,4 +1,4 @@ -import { useDaoContext } from '@dao-dao/stateless' +import { useDao } from '@dao-dao/stateless' import { LinkWrapper } from '../../../../../../components' import { @@ -16,7 +16,7 @@ export const SurveyRow = ({ const { isWalletConnected, hexPublicKey } = useWallet({ loadAccount: true, }) - const { dao } = useDaoContext() + const dao = useDao() // Load survey from query in case list is out of date. const loadingSurvey = useQueryLoadingDataWithError( diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/CreateSurvey.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/CreateSurvey.tsx index 158d747fd..b1637c663 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/CreateSurvey.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/CreateSurvey.tsx @@ -10,7 +10,7 @@ import { Loader, useCachedLoading, useChain, - useDaoInfoContext, + useDao, useDaoNavHelpers, } from '@dao-dao/stateless' import { TokenType, WidgetId } from '@dao-dao/types' @@ -32,7 +32,7 @@ import { CreateSurvey as StatelessCreateSurvey } from '../../stateless/pages/Cre export const CreateSurvey = () => { const { t } = useTranslation() const { chain_id: chainId } = useChain() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { getDaoPath } = useDaoNavHelpers() const router = useRouter() diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/Home.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/Home.tsx index 2a72d4804..5442c26fe 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/Home.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/Home.tsx @@ -1,6 +1,6 @@ import { useQueryClient } from '@tanstack/react-query' -import { useDaoContext } from '@dao-dao/stateless' +import { useDao } from '@dao-dao/stateless' import { IconButtonLink } from '../../../../../../../components' import { @@ -13,7 +13,7 @@ import { Home as StatelessHome } from '../../stateless/pages/Home' import { SurveyRow } from '../SurveyRow' export const Home = () => { - const { dao } = useDaoContext() + const dao = useDao() const { hexPublicKey } = useWallet({ loadAccount: true, }) diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Complete.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Complete.tsx index f33d9e6de..788d98216 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Complete.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Complete.tsx @@ -9,7 +9,7 @@ import { Loader, useCachedLoadable, useChain, - useDaoContext, + useDao, useDaoNavHelpers, } from '@dao-dao/stateless' import { TokenType, UnifiedCosmosMsg } from '@dao-dao/types' @@ -53,7 +53,7 @@ export const Complete = ({ isMember, }: ViewSurveyPageProps) => { const { t } = useTranslation() - const { dao } = useDaoContext() + const dao = useDao() const { goToDaoProposal } = useDaoNavHelpers() const { chain_id: chainId, bech32_prefix: bech32Prefix } = useChain() const { address: walletAddress = '' } = useWallet() diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Info.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Info.tsx index 45ba48bc2..20fa4b357 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Info.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Info.tsx @@ -3,7 +3,7 @@ import { unparse as jsonToCsv } from 'papaparse' import { useState } from 'react' import toast from 'react-hot-toast' -import { useChain, useDaoContext } from '@dao-dao/stateless' +import { useChain, useDao } from '@dao-dao/stateless' import { secp256k1PublicKeyToBech32Address } from '@dao-dao/utils' import { ButtonLink } from '../../../../../../../../components' @@ -13,7 +13,7 @@ import { Info as StatelessInfo } from '../../../stateless/pages/ViewSurvey/Info' import { ViewSurveyPageProps } from './types' export const Info = ({ status, isMember }: ViewSurveyPageProps) => { - const { dao } = useDaoContext() + const dao = useDao() const { bech32_prefix: bech32Prefix } = useChain() const postRequest = usePostRequest() diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Rate.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Rate.tsx index 08dd7bade..1049ef07a 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Rate.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Rate.tsx @@ -8,12 +8,7 @@ import { cosmWasmClientForChainSelector, genericTokenWithUsdPriceSelector, } from '@dao-dao/state/recoil' -import { - Loader, - useCachedLoadable, - useChain, - useDaoInfoContext, -} from '@dao-dao/stateless' +import { Loader, useCachedLoadable, useChain, useDao } from '@dao-dao/stateless' import { TokenType } from '@dao-dao/types' import { secp256k1PublicKeyToBech32Address } from '@dao-dao/utils' @@ -41,7 +36,7 @@ import { ViewSurveyPageProps } from './types' export const Rate = ({ status, refreshRef, isMember }: ViewSurveyPageProps) => { const { t } = useTranslation() const { chain_id: chainId, bech32_prefix: bech32Prefix } = useChain() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const client = useRecoilValue(cosmWasmClientForChainSelector(chainId)) const postRequest = usePostRequest() diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Submit.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Submit.tsx index c7ac8c14f..a0c475afa 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Submit.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/Submit.tsx @@ -2,7 +2,7 @@ import { useState } from 'react' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' -import { useDaoContext } from '@dao-dao/stateless' +import { useDao } from '@dao-dao/stateless' import { ConnectWallet, @@ -22,7 +22,7 @@ export const Submit = ({ connected, }: ViewSurveyPageProps) => { const { t } = useTranslation() - const { dao } = useDaoContext() + const dao = useDao() const { address: walletAddress = '' } = useWallet() const { entity: walletEntity } = useEntity(walletAddress) const postRequest = usePostRequest() diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/ViewSurvey.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/ViewSurvey.tsx index 7a417a7c3..15eb1fe42 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/ViewSurvey.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateful/pages/ViewSurvey/ViewSurvey.tsx @@ -4,7 +4,7 @@ import { ComponentType } from 'react' import { ErrorPage, Loader, - useDaoContext, + useDao, useDaoNavHelpers, useUpdatingRef, } from '@dao-dao/stateless' @@ -24,7 +24,7 @@ import { Submit } from './Submit' import { ViewSurveyPageProps } from './types' export const ViewSurvey = () => { - const { dao } = useDaoContext() + const dao = useDao() const { daoSubpathComponents } = useDaoNavHelpers() const { hexPublicKey } = useWallet({ diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/SurveyRow.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/SurveyRow.tsx index ff409e856..f87c64768 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/SurveyRow.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/SurveyRow.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next' import { ProposalWalletVote, Tooltip, - useDaoContext, + useDao, useDaoNavHelpers, } from '@dao-dao/stateless' import { LinkWrapperProps, WidgetId } from '@dao-dao/types' @@ -51,7 +51,7 @@ export const SurveyRow = ({ LinkWrapper, }: SurveyRowProps) => { const { t } = useTranslation() - const { dao } = useDaoContext() + const dao = useDao() const { getDaoPath } = useDaoNavHelpers() // Display upcoming date first, then date when contributions close. Even diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/TabRenderer.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/TabRenderer.tsx index eb54641c7..a09034229 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/TabRenderer.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/TabRenderer.tsx @@ -2,11 +2,7 @@ import { Add, ArrowBackRounded, Remove } from '@mui/icons-material' import { ComponentType } from 'react' import { useTranslation } from 'react-i18next' -import { - Tooltip, - useDaoInfoContext, - useDaoNavHelpers, -} from '@dao-dao/stateless' +import { Tooltip, useDao, useDaoNavHelpers } from '@dao-dao/stateless' import { ButtonLinkProps, WidgetId } from '@dao-dao/types' import { PagePath } from '../../types' @@ -23,7 +19,7 @@ export const TabRenderer = ({ ButtonLink, }: TabRendererProps) => { const { t } = useTranslation() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { daoSubpathComponents, getDaoPath } = useDaoNavHelpers() const pagePath = daoSubpathComponents[1] || '' diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/Home.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/Home.tsx index 3d615d431..cfbb322c5 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/Home.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/Home.tsx @@ -6,7 +6,7 @@ import { ErrorPage, LineLoader, NoContent, - useDaoInfoContext, + useDao, useDaoNavHelpers, } from '@dao-dao/stateless' import { LoadingDataWithError, WidgetId } from '@dao-dao/types' @@ -28,7 +28,7 @@ export type HomeProps = { export const Home = ({ loadingSurveys, isMember, SurveyRow }: HomeProps) => { const { t } = useTranslation() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { getDaoPath } = useDaoNavHelpers() return ( diff --git a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Info.tsx b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Info.tsx index d3147bdc8..2e149f28d 100644 --- a/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Info.tsx +++ b/packages/stateful/widgets/widgets/RetroactiveCompensation/Renderer/components/stateless/pages/ViewSurvey/Info.tsx @@ -2,12 +2,7 @@ import { ArrowOutward, Download } from '@mui/icons-material' import { ComponentType } from 'react' import { useTranslation } from 'react-i18next' -import { - Button, - Tooltip, - useDaoContext, - useDaoNavHelpers, -} from '@dao-dao/stateless' +import { Button, Tooltip, useDao, useDaoNavHelpers } from '@dao-dao/stateless' import { ButtonLinkProps, LoadingDataWithError } from '@dao-dao/types' import { formatDateTimeTz } from '@dao-dao/utils' @@ -46,7 +41,7 @@ export const Info = ({ ButtonLink, }: InfoProps) => { const { t } = useTranslation() - const { dao } = useDaoContext() + const dao = useDao() const { getDaoProposalPath } = useDaoNavHelpers() return ( diff --git a/packages/stateful/widgets/widgets/VestingPayments/Renderer/TabRenderer/TabRenderer.tsx b/packages/stateful/widgets/widgets/VestingPayments/Renderer/TabRenderer/TabRenderer.tsx index 17dbf6f4b..44f7d2c36 100644 --- a/packages/stateful/widgets/widgets/VestingPayments/Renderer/TabRenderer/TabRenderer.tsx +++ b/packages/stateful/widgets/widgets/VestingPayments/Renderer/TabRenderer/TabRenderer.tsx @@ -11,7 +11,7 @@ import { Modal, NoContent, Tooltip, - useDaoInfoContext, + useDao, useDaoNavHelpers, } from '@dao-dao/stateless' import { @@ -48,7 +48,7 @@ export const TabRenderer = ({ Trans, }: TabRendererProps) => { const { t } = useTranslation() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { daoSubpathComponents, goToDao } = useDaoNavHelpers() const { address: walletAddress } = useWallet() diff --git a/packages/stateful/widgets/widgets/VestingPayments/Renderer/TabRenderer/index.tsx b/packages/stateful/widgets/widgets/VestingPayments/Renderer/TabRenderer/index.tsx index d32f296fa..870a34633 100644 --- a/packages/stateful/widgets/widgets/VestingPayments/Renderer/TabRenderer/index.tsx +++ b/packages/stateful/widgets/widgets/VestingPayments/Renderer/TabRenderer/index.tsx @@ -7,7 +7,7 @@ import { cwVestingExtraQueries, } from '@dao-dao/state/query' import { - useDaoInfoContext, + useDao, useDaoNavHelpers, useInitializedActionForKey, } from '@dao-dao/stateless' @@ -33,7 +33,7 @@ import { TabRenderer as StatelessTabRenderer } from './TabRenderer' export const TabRenderer = ({ variables: { factories, factory, oldFactories }, }: WidgetRendererProps<VestingPaymentsWidgetData>) => { - const { chainId: defaultChainId, coreAddress, accounts } = useDaoInfoContext() + const { chainId: defaultChainId, coreAddress, accounts } = useDao() const { getDaoProposalPath } = useDaoNavHelpers() const { isMember = false } = useMembership() diff --git a/packages/stateless/components/actions/ActionsEditor.tsx b/packages/stateless/components/actions/ActionsEditor.tsx index 7350ec0a4..21af8fc47 100644 --- a/packages/stateless/components/actions/ActionsEditor.tsx +++ b/packages/stateless/components/actions/ActionsEditor.tsx @@ -24,7 +24,7 @@ import { ActionMap, } from '@dao-dao/types/actions' -import { useActionsContext, useDaoInfoContextIfAvailable } from '../../contexts' +import { useActionsContext, useDaoIfAvailable } from '../../contexts' import { useLoadingPromise } from '../../hooks' import { Loader } from '../logo' import { ActionCard } from './ActionCard' @@ -52,7 +52,7 @@ export const ActionsEditor = ({ }>() const { actionMap } = useActionsContext() - const isDao = !!useDaoInfoContextIfAvailable() + const isDao = !!useDaoIfAvailable() // Type assertion assumes the passed in field name is correct. const actionDataFieldName = _actionDataFieldName as 'actionData' diff --git a/packages/stateless/components/dao/DaoSplashHeader.stories.tsx b/packages/stateless/components/dao/DaoSplashHeader.stories.tsx index 4dd8928b6..85c8d5bbb 100644 --- a/packages/stateless/components/dao/DaoSplashHeader.stories.tsx +++ b/packages/stateless/components/dao/DaoSplashHeader.stories.tsx @@ -2,7 +2,7 @@ import { ComponentMeta, ComponentStory } from '@storybook/react' import { DaoPageWrapperDecorator } from '@dao-dao/storybook' -import { useDaoInfoContext } from '../../contexts' +import { useDao } from '../../contexts' import { ButtonLink } from '../buttons' import { LinkWrapper } from '../LinkWrapper' import { DaoSplashHeader } from './DaoSplashHeader' @@ -14,7 +14,7 @@ export default { } as ComponentMeta<typeof DaoSplashHeader> const Template: ComponentStory<typeof DaoSplashHeader> = (args) => ( - <DaoSplashHeader {...args} daoInfo={useDaoInfoContext()} /> + <DaoSplashHeader {...args} dao={useDao()} /> ) export const Default = Template.bind({}) diff --git a/packages/stateless/components/dao/DaoSplashHeader.tsx b/packages/stateless/components/dao/DaoSplashHeader.tsx index 6f77047b3..71661a61b 100644 --- a/packages/stateless/components/dao/DaoSplashHeader.tsx +++ b/packages/stateless/components/dao/DaoSplashHeader.tsx @@ -7,7 +7,7 @@ import { formatPercentOf100 } from '@dao-dao/utils' import { DaoHeader } from './DaoHeader' export const DaoSplashHeader = ({ - daoInfo, + dao, follow, ButtonLink, LinkWrapper, @@ -18,32 +18,32 @@ export const DaoSplashHeader = ({ return ( <> - {!daoInfo.isActive && daoInfo.activeThreshold && ( + {!dao.info.isActive && dao.info.activeThreshold && ( <div className="mb-10 -mt-4 flex flex-row items-center justify-center gap-3 rounded-md bg-background-interactive-warning p-3 md:gap-2"> <WarningRounded className="!h-10 !w-10 text-icon-interactive-warning md:!h-6 md:!w-6" /> <p className="text-text-interactive-warning-body"> {t('error.daoIsInactive', { context: - 'percentage' in daoInfo.activeThreshold + 'percentage' in dao.info.activeThreshold ? 'percent' : 'absolute', percent: - 'percentage' in daoInfo.activeThreshold + 'percentage' in dao.info.activeThreshold ? formatPercentOf100( - Number(daoInfo.activeThreshold.percentage.percent) * 100 + Number(dao.info.activeThreshold.percentage.percent) * 100 ) : undefined, count: - 'percentage' in daoInfo.activeThreshold + 'percentage' in dao.info.activeThreshold ? undefined - : Number(daoInfo.activeThreshold.absolute_count.count), + : Number(dao.info.activeThreshold.absolute_count.count), })} </p> </div> )} - {daoInfo.parentDao && !daoInfo.parentDao.registeredSubDao && ( + {dao.info.parentDao && !dao.info.parentDao.registeredSubDao && ( <ButtonLink center className="mb-10 -mt-4 bg-background-interactive-warning" @@ -56,8 +56,8 @@ export const DaoSplashHeader = ({ <p className="text-text-interactive-warning-body"> {t('info.subDaoNotYetRecognized', { - parent: daoInfo.parentDao.name, - child: daoInfo.name, + parent: dao.info.parentDao.name, + child: dao.name, })} {!!parentProposalRecognizeSubDaoHref && ( @@ -69,7 +69,7 @@ export const DaoSplashHeader = ({ </ButtonLink> )} - {daoInfo.parentDao && daoInfo.contractAdmin === daoInfo.coreAddress && ( + {dao.info.parentDao && dao.info.contractAdmin === dao.coreAddress && ( <ButtonLink center className="mb-10 -mt-4 bg-background-interactive-warning" @@ -82,15 +82,15 @@ export const DaoSplashHeader = ({ <p className="text-text-interactive-warning-body"> {t('info.parentDaoNotAdmin', { - parent: daoInfo.parentDao.name, - child: daoInfo.name, + parent: dao.info.parentDao.name, + child: dao.name, })} {!!proposeUpdateAdminToParentHref && ( <span className="font-bold"> {' ' + t('button.clickHereToProposeSettingAdminToParent', { - parent: daoInfo.parentDao.name, + parent: dao.info.parentDao.name, })} </span> )} @@ -100,12 +100,12 @@ export const DaoSplashHeader = ({ <DaoHeader LinkWrapper={LinkWrapper} - coreAddress={daoInfo.coreAddress} - description={daoInfo.description} + coreAddress={dao.coreAddress} + description={dao.description} follow={follow} - imageUrl={daoInfo.imageUrl} - name={daoInfo.name} - parentDao={daoInfo.parentDao} + imageUrl={dao.imageUrl} + name={dao.name} + parentDao={dao.info.parentDao} /> </> ) diff --git a/packages/stateless/components/dao/MainDaoInfoCardsTokenLoader.tsx b/packages/stateless/components/dao/MainDaoInfoCardsTokenLoader.tsx index 0824f39af..c14f72dd9 100644 --- a/packages/stateless/components/dao/MainDaoInfoCardsTokenLoader.tsx +++ b/packages/stateless/components/dao/MainDaoInfoCardsTokenLoader.tsx @@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next' import { formatDate } from '@dao-dao/utils' -import { useDaoInfoContext } from '../../contexts' +import { useDao } from '../../contexts' import { DaoInfoCards } from './DaoInfoCards' /** @@ -11,7 +11,7 @@ import { DaoInfoCards } from './DaoInfoCards' */ export const MainDaoInfoCardsTokenLoader = () => { const { t } = useTranslation() - const { activeThreshold, created } = useDaoInfoContext() + const { activeThreshold, created } = useDao().info return ( <DaoInfoCards diff --git a/packages/stateless/components/dao/tabs/ProposalsTab.stories.tsx b/packages/stateless/components/dao/tabs/ProposalsTab.stories.tsx index ab3d64c71..62d879f29 100644 --- a/packages/stateless/components/dao/tabs/ProposalsTab.stories.tsx +++ b/packages/stateless/components/dao/tabs/ProposalsTab.stories.tsx @@ -3,7 +3,7 @@ import { ComponentMeta, ComponentStory } from '@storybook/react' import { DaoPageWrapperDecorator } from '@dao-dao/storybook/decorators' import { ProposalListProps } from '@dao-dao/types' -import { useDaoInfoContext } from '../../../contexts/Dao' +import { useDao } from '../../../contexts/Dao' import { ButtonLink } from '../../buttons' import { ProposalLineProps, ProposalList } from '../../proposal' import * as ProposalListStories from '../../proposal/ProposalList.stories' @@ -17,7 +17,7 @@ export default { } as ComponentMeta<typeof ProposalsTab> const Template: ComponentStory<typeof ProposalsTab> = (args) => ( - <ProposalsTab {...args} daoInfo={useDaoInfoContext()} /> + <ProposalsTab {...args} dao={useDao()} /> ) export const Default = Template.bind({}) diff --git a/packages/stateless/components/dao/tabs/ProposalsTab.tsx b/packages/stateless/components/dao/tabs/ProposalsTab.tsx index bfa69a40d..6c00391cc 100644 --- a/packages/stateless/components/dao/tabs/ProposalsTab.tsx +++ b/packages/stateless/components/dao/tabs/ProposalsTab.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next' import { ButtonLinkProps, - DaoInfo, + IDaoBase, StatefulProposalListProps, } from '@dao-dao/types' @@ -13,13 +13,13 @@ import { useDaoNavHelpers, usePlatform } from '../../../hooks' import { Tooltip } from '../../tooltip/Tooltip' export interface ProposalsTabProps { - daoInfo: DaoInfo + dao: IDaoBase ProposalList: ComponentType<StatefulProposalListProps> ButtonLink: ComponentType<ButtonLinkProps> } export const ProposalsTab = ({ - daoInfo, + dao, ProposalList, ButtonLink, }: ProposalsTabProps) => { @@ -35,14 +35,14 @@ export const ProposalsTab = ({ if (((!isMac && event.ctrlKey) || event.metaKey) && event.shiftKey) { if (event.key === 'p') { event.preventDefault() - goToDaoProposal(daoInfo.coreAddress, 'create') + goToDaoProposal(dao.coreAddress, 'create') } } } document.addEventListener('keydown', handleKeyPress) return () => document.removeEventListener('keydown', handleKeyPress) - }, [isMac, daoInfo.coreAddress, goToDaoProposal]) + }, [isMac, dao.coreAddress, goToDaoProposal]) return ( <> @@ -64,7 +64,7 @@ export const ProposalsTab = ({ > <ButtonLink className="shrink-0" - href={getDaoProposalPath(daoInfo.coreAddress, 'create')} + href={getDaoProposalPath(dao.coreAddress, 'create')} > <Add className="!h-4 !w-4" /> <span className="hidden md:inline">{t('button.newProposal')}</span> diff --git a/packages/stateless/components/dao/tabs/SubDaosTab.tsx b/packages/stateless/components/dao/tabs/SubDaosTab.tsx index 2577f06d4..e30a1e2a8 100644 --- a/packages/stateless/components/dao/tabs/SubDaosTab.tsx +++ b/packages/stateless/components/dao/tabs/SubDaosTab.tsx @@ -11,7 +11,7 @@ import { StatefulDaoCardProps, } from '@dao-dao/types' -import { useDaoInfoContext } from '../../../contexts' +import { useDao } from '../../../contexts' import { useDaoNavHelpers } from '../../../hooks' import { ErrorPage } from '../../error' import { GridCardContainer } from '../../GridCardContainer' @@ -37,8 +37,12 @@ export const SubDaosTab = ({ ButtonLink, }: SubDaosTabProps) => { const { t } = useTranslation() - const { coreAddress, coreVersion, name, supportedFeatures } = - useDaoInfoContext() + const { + coreAddress, + coreVersion, + name, + info: { supportedFeatures }, + } = useDao() const { getDaoPath } = useDaoNavHelpers() const subDaosSupported = diff --git a/packages/stateless/components/dao/tabs/TreasuryTab.tsx b/packages/stateless/components/dao/tabs/TreasuryTab.tsx index 65507a4d7..df28dab85 100644 --- a/packages/stateless/components/dao/tabs/TreasuryTab.tsx +++ b/packages/stateless/components/dao/tabs/TreasuryTab.tsx @@ -23,7 +23,7 @@ import { serializeTokenSource, } from '@dao-dao/utils' -import { useDaoInfoContext, useSupportedChainContext } from '../../../contexts' +import { useDao, useSupportedChainContext } from '../../../contexts' import { useButtonPopupSorter, useTokenSortOptions } from '../../../hooks' import { ErrorPage } from '../../error' import { AccountSelector } from '../../inputs' @@ -76,7 +76,7 @@ export const TreasuryTab = <T extends TokenCardInfo, N extends object>({ chain: { chain_id: currentChainId }, config: { noIndexer }, } = useSupportedChainContext() - const { chainId: daoChainId, coreAddress, accounts } = useDaoInfoContext() + const { chainId: daoChainId, coreAddress, accounts } = useDao() // Combine chain tokens into loadable, lazily. Load all that are ready. const { nonValenceTokens, valenceTokens } = useMemo((): { diff --git a/packages/stateless/components/inputs/AccountSelector.tsx b/packages/stateless/components/inputs/AccountSelector.tsx index d19df689d..090b116b4 100644 --- a/packages/stateless/components/inputs/AccountSelector.tsx +++ b/packages/stateless/components/inputs/AccountSelector.tsx @@ -21,7 +21,7 @@ export type AccountSelectorProps = { /** * The list of accounts. */ - accounts: Account[] + accounts: readonly Account[] /** * Account selection callback function. */ diff --git a/packages/stateless/components/inputs/DaoSupportedChainPickerInput.tsx b/packages/stateless/components/inputs/DaoSupportedChainPickerInput.tsx index 564154afe..941d707de 100644 --- a/packages/stateless/components/inputs/DaoSupportedChainPickerInput.tsx +++ b/packages/stateless/components/inputs/DaoSupportedChainPickerInput.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next' import { AccountType, ChainPickerPopupProps } from '@dao-dao/types' import { getIbcTransferChainIdsForChain } from '@dao-dao/utils' -import { useChainContext, useDaoInfoContextIfAvailable } from '../../contexts' +import { useChainContext, useDaoIfAvailable } from '../../contexts' import { ChainPickerPopup } from '../popup' import { InputLabel } from './InputLabel' @@ -77,13 +77,13 @@ export const DaoSupportedChainPickerInput = ({ config, } = useChainContext() const { watch, setValue } = useFormContext() - const daoInfo = useDaoInfoContextIfAvailable() + const dao = useDaoIfAvailable() const includeChainIds = - onlyDaoChainIds && daoInfo + onlyDaoChainIds && dao ? [ - daoInfo.chainId, - ...daoInfo.accounts.flatMap(({ type, chainId }) => + dao.chainId, + ...dao.accounts.flatMap(({ type, chainId }) => accountTypes.includes(type as any) ? chainId : [] ), ] diff --git a/packages/stateless/components/layout/Breadcrumbs.tsx b/packages/stateless/components/layout/Breadcrumbs.tsx index 49ff985f5..be07aef2b 100644 --- a/packages/stateless/components/layout/Breadcrumbs.tsx +++ b/packages/stateless/components/layout/Breadcrumbs.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next' import { BreadcrumbsProps, DaoPageMode } from '@dao-dao/types' -import { useDaoInfoContextIfAvailable } from '../../contexts' +import { useDaoIfAvailable } from '../../contexts' import { useDaoNavHelpers } from '../../hooks' import { Button } from '../buttons/Button' import { IconButton } from '../icon_buttons/IconButton' @@ -18,37 +18,38 @@ export const Breadcrumbs = ({ override = false, homeTab, current, - daoInfo: _daoInfo, + dao: _dao, className, }: BreadcrumbsProps) => { const { t } = useTranslation() - // Allow using Breadcrumbs outside of DaoPageWrapper. - const daoInfo = useDaoInfoContextIfAvailable() || _daoInfo const { mode } = useAppContext() const { getDaoPath } = useDaoNavHelpers() + // Allow using Breadcrumbs outside of DaoPageWrapper. + const dao = useDaoIfAvailable() || _dao + const [responsive, setResponsive] = useState(false) const crumbs = mode === DaoPageMode.Dapp - ? home || !daoInfo + ? home || !dao ? [{ href: '/', label: t('title.home') }] : [ { href: // Link to home tab if available. - getDaoPath(daoInfo.coreAddress, homeTab?.id), - label: daoInfo.name, + getDaoPath(dao.coreAddress, homeTab?.id), + label: dao.name, }, ] : // SDA - home || !daoInfo + home || !dao ? [] : [ { href: // Link to home tab if available. - getDaoPath(daoInfo.coreAddress, homeTab?.id), + getDaoPath(dao.coreAddress, homeTab?.id), label: homeTab?.sdaLabel || t('title.home'), }, ] diff --git a/packages/stateless/components/layout/SdaNavigation.tsx b/packages/stateless/components/layout/SdaNavigation.tsx index 9b1d6b4e5..1a2393f0f 100644 --- a/packages/stateless/components/layout/SdaNavigation.tsx +++ b/packages/stateless/components/layout/SdaNavigation.tsx @@ -13,7 +13,7 @@ import { DaoPageMode, DaoTabId } from '@dao-dao/types' import { SdaNavigationProps } from '@dao-dao/types/components/SdaNavigation' import { MAINNET, getDaoPath as baseGetDaoPath } from '@dao-dao/utils' -import { useDaoInfoContext } from '../../contexts' +import { useDao } from '../../contexts' import { useDaoNavHelpers } from '../../hooks' import { DaoImage } from '../dao/DaoImage' import { IconButton, ThemeToggle } from '../icon_buttons' @@ -53,7 +53,7 @@ export const SdaNavigation = ({ LinkWrapper, SidebarWallet, }: SdaNavigationProps) => { - const daoInfo = useDaoInfoContext() + const daoInfo = useDao() const { t } = useTranslation() const { getDaoPath, diff --git a/packages/stateless/components/modals/DiscordNotifierConfigureModal.tsx b/packages/stateless/components/modals/DiscordNotifierConfigureModal.tsx index ba2cbbe1b..7879895b3 100644 --- a/packages/stateless/components/modals/DiscordNotifierConfigureModal.tsx +++ b/packages/stateless/components/modals/DiscordNotifierConfigureModal.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next' import { DiscordNotifierRegistration, ModalProps } from '@dao-dao/types' -import { useDaoInfoContext } from '../../contexts' +import { useDao } from '../../contexts' import { Button, ButtonLink } from '../buttons' import { CopyToClipboard } from '../CopyToClipboard' import { IconButton, IconButtonLink } from '../icon_buttons' @@ -53,7 +53,7 @@ export const DiscordNotifierConfigureModal = ({ ...props }: DiscordNotifierConfigureModalProps) => { const { t } = useTranslation() - const { name: daoName } = useDaoInfoContext() + const { name: daoName } = useDao() const [registering, setRegistering] = useState(false) @@ -77,7 +77,7 @@ export const DiscordNotifierConfigureModal = ({ {...props} contentContainerClassName="gap-4" header={{ - title: t('title.discordNotifier', { daoName: daoName }), + title: t('title.discordNotifier', { daoName }), subtitle: t('info.discordNotifierSubtitle'), }} onClose={() => { diff --git a/packages/stateless/components/not_found/ProposalNotFound.tsx b/packages/stateless/components/not_found/ProposalNotFound.tsx index 6dfb06b23..aea8ead60 100644 --- a/packages/stateless/components/not_found/ProposalNotFound.tsx +++ b/packages/stateless/components/not_found/ProposalNotFound.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import { DaoTabId, PageHeaderProps } from '@dao-dao/types' -import { useDaoInfoContext } from '../../contexts' +import { useDao } from '../../contexts' import { useDaoNavHelpers } from '../../hooks' import { ButtonLink } from '../buttons' import { ErrorPage } from '../error/ErrorPage' @@ -16,7 +16,7 @@ export const ProposalNotFound = ({ PageHeaderContent, }: ProposalNotFoundProps) => { const { t } = useTranslation() - const { coreAddress } = useDaoInfoContext() + const { coreAddress } = useDao() const { getDaoPath } = useDaoNavHelpers() return ( diff --git a/packages/stateless/components/proposal/ProposalModuleSelector.stories.tsx b/packages/stateless/components/proposal/ProposalModuleSelector.stories.tsx index 46e7d42d1..f3ec89d03 100644 --- a/packages/stateless/components/proposal/ProposalModuleSelector.stories.tsx +++ b/packages/stateless/components/proposal/ProposalModuleSelector.stories.tsx @@ -5,7 +5,7 @@ import { matchAdapter } from '@dao-dao/stateful/proposal-module-adapter' import { DaoPageWrapperDecorator } from '@dao-dao/storybook/decorators' import { DaoProposalSingleAdapterId } from '@dao-dao/utils' -import { useDaoInfoContext } from '../../contexts' +import { useDao } from '../../contexts' import { ProposalModuleSelector } from './ProposalModuleSelector' export default { @@ -16,11 +16,11 @@ export default { } as ComponentMeta<typeof ProposalModuleSelector> const Template: ComponentStory<typeof ProposalModuleSelector> = (args) => { - const daoInfo = useDaoInfoContext() + const dao = useDao() const [selectedProposalModule, setSelectedProposalModule] = useState( // Default to single choice proposal module. - daoInfo.proposalModules.find( + dao.proposalModules.find( ({ contractName }) => matchAdapter(contractName)?.id === DaoProposalSingleAdapterId )! diff --git a/packages/stateless/components/proposal/ProposalModuleSelector.tsx b/packages/stateless/components/proposal/ProposalModuleSelector.tsx index 76d0133e5..72149e7cc 100644 --- a/packages/stateless/components/proposal/ProposalModuleSelector.tsx +++ b/packages/stateless/components/proposal/ProposalModuleSelector.tsx @@ -3,14 +3,14 @@ import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { + IProposalModuleBase, PreProposeModuleType, ProposalModuleAdapter, - ProposalModuleInfo, TypedOption, } from '@dao-dao/types' import { ContractName } from '@dao-dao/utils' -import { useDaoInfoContext } from '../../contexts' +import { useDao } from '../../contexts' import { SegmentedControls } from '../inputs/SegmentedControls' export type ProposalModuleSelectorProps = { @@ -18,7 +18,7 @@ export type ProposalModuleSelectorProps = { * Address of the selected proposal module. */ selected: string - setSelected: (proposalModule: ProposalModuleInfo) => void + setSelected: (proposalModule: IProposalModuleBase) => void matchAdapter: ( contractNameToMatch: string ) => ProposalModuleAdapter | undefined @@ -35,7 +35,7 @@ export const ProposalModuleSelector = ({ className, }: ProposalModuleSelectorProps) => { const { t } = useTranslation() - const { proposalModules } = useDaoInfoContext() + const { proposalModules } = useDao() // List of proposal modules available, using the adapter ID to derive a label // to display in the selector. @@ -48,7 +48,7 @@ export const ProposalModuleSelector = ({ ({ prePropose }) => prePropose?.type !== PreProposeModuleType.NeutronOverruleSingle ) - .map((proposalModule): TypedOption<ProposalModuleInfo> | undefined => { + .map((proposalModule): TypedOption<IProposalModuleBase> | undefined => { const adapter = matchAdapter(proposalModule.contractName) return ( @@ -58,7 +58,7 @@ export const ProposalModuleSelector = ({ } ) }) - .filter((item): item is TypedOption<ProposalModuleInfo> => !!item) + .filter((item): item is TypedOption<IProposalModuleBase> => !!item) // Ignore proposals with an approver pre-propose since those are // automatically managed by a pre-propose-approval contract in another // DAO. diff --git a/packages/stateless/components/token/TokenCard.tsx b/packages/stateless/components/token/TokenCard.tsx index 713854f19..f8f36ac77 100644 --- a/packages/stateless/components/token/TokenCard.tsx +++ b/packages/stateless/components/token/TokenCard.tsx @@ -19,7 +19,7 @@ import { toAccessibleImageUrl, } from '@dao-dao/utils' -import { useDaoInfoContextIfAvailable } from '../../contexts' +import { useDaoIfAvailable } from '../../contexts' import { useAddToken } from '../../hooks' import { Button } from '../buttons/Button' import { CopyToClipboard } from '../CopyToClipboard' @@ -48,7 +48,7 @@ export const TokenCard = ({ // If in a DAO context, don't show the DAOs governed section if the only DAO // this token governs is the current DAO. See the comment where this is used // for more details. - const { coreAddress } = useDaoInfoContextIfAvailable() ?? {} + const { coreAddress } = useDaoIfAvailable() ?? {} const lazyStakes = lazyInfo.loading || !lazyInfo.data.stakingInfo diff --git a/packages/stateless/contexts/Dao.ts b/packages/stateless/contexts/Dao.ts index d77a8e19f..faf44848d 100644 --- a/packages/stateless/contexts/Dao.ts +++ b/packages/stateless/contexts/Dao.ts @@ -21,6 +21,7 @@ export const useDaoContext = () => { export const useDaoContextIfAvailable = () => useContext(DaoContext) -export const useDaoInfoContext = () => useDaoContext().dao.info -export const useDaoInfoContextIfAvailable = () => - useDaoContextIfAvailable()?.dao.info +export const useDao = () => useDaoContext().dao +export const useDaoIfAvailable = () => useDaoContextIfAvailable()?.dao + +export const useVotingModule = () => useDao().votingModule diff --git a/packages/stateless/pages/DaoDappTabbedHome.tsx b/packages/stateless/pages/DaoDappTabbedHome.tsx index 195de9f7d..cbdc3747c 100644 --- a/packages/stateless/pages/DaoDappTabbedHome.tsx +++ b/packages/stateless/pages/DaoDappTabbedHome.tsx @@ -9,7 +9,7 @@ import { import { PageLoader, TabBar } from '../components' import { DaoSplashHeader } from '../components/dao/DaoSplashHeader' -import { useDaoInfoContext } from '../contexts' +import { useDao } from '../contexts' import { useTabBarScrollReset } from '../hooks' export const DaoDappTabbedHome = ({ @@ -21,7 +21,7 @@ export const DaoDappTabbedHome = ({ onSelectTabId, ...headerProps }: DaoDappTabbedHomeProps) => { - const daoInfo = useDaoInfoContext() + const dao = useDao() // Auto scroll to top of tab on change. const { tabBarRef, tabContainerRef } = useTabBarScrollReset({ @@ -34,7 +34,7 @@ export const DaoDappTabbedHome = ({ <DaoSplashHeader ButtonLink={ButtonLink} LinkWrapper={LinkWrapper} - daoInfo={daoInfo} + dao={dao} {...headerProps} /> </div> diff --git a/packages/types/components/Breadcrumbs.ts b/packages/types/components/Breadcrumbs.ts index 94bac8047..25eef4bb7 100644 --- a/packages/types/components/Breadcrumbs.ts +++ b/packages/types/components/Breadcrumbs.ts @@ -1,6 +1,7 @@ import { ReactNode } from 'react' -import { DaoInfo, DaoTabId } from '../dao' +import { IDaoBase } from '../clients' +import { DaoTabId } from '../dao' export type BreadcrumbCrumb = { href: string @@ -32,10 +33,10 @@ export type BreadcrumbsProps = { */ current: ReactNode /** - * DAO info, if this is being rendered outside of the context provider (like - * in the PageHeader), but still needs access to the DAO info. + * DAO, if this is being rendered outside of the context provider (like in the + * PageHeader), but still needs access to the DAO. */ - daoInfo?: DaoInfo | null + dao?: IDaoBase | null /** * Optional container class name. */ diff --git a/packages/types/components/DaoDappTabbedHome.ts b/packages/types/components/DaoDappTabbedHome.ts index d02874a59..376cc337e 100644 --- a/packages/types/components/DaoDappTabbedHome.ts +++ b/packages/types/components/DaoDappTabbedHome.ts @@ -6,7 +6,7 @@ import { DaoSplashHeaderProps } from './DaoSplashHeader' import { LinkWrapperProps } from './LinkWrapper' import { SuspenseLoaderProps } from './SuspenseLoader' -export type DaoDappTabbedHomeProps = Omit<DaoSplashHeaderProps, 'daoInfo'> & { +export type DaoDappTabbedHomeProps = Omit<DaoSplashHeaderProps, 'dao'> & { SuspenseLoader: ComponentType<SuspenseLoaderProps> ButtonLink: ComponentType<ButtonLinkProps> LinkWrapper: ComponentType<LinkWrapperProps> diff --git a/packages/types/components/DaoSplashHeader.ts b/packages/types/components/DaoSplashHeader.ts index 00b8270a2..397721ee3 100644 --- a/packages/types/components/DaoSplashHeader.ts +++ b/packages/types/components/DaoSplashHeader.ts @@ -1,12 +1,12 @@ import { ComponentType } from 'react' -import { DaoInfo } from '../dao' +import { IDaoBase } from '../clients' import { ButtonLinkProps } from './Buttonifier' import { FollowState } from './DaoCard' import { LinkWrapperProps } from './LinkWrapper' export type DaoSplashHeaderProps = { - daoInfo: DaoInfo + dao: IDaoBase follow?: FollowState ButtonLink: ComponentType<ButtonLinkProps> LinkWrapper: ComponentType<LinkWrapperProps> diff --git a/packages/types/voting-module-adapter.ts b/packages/types/voting-module-adapter.ts index df421331b..5f8234459 100644 --- a/packages/types/voting-module-adapter.ts +++ b/packages/types/voting-module-adapter.ts @@ -53,18 +53,11 @@ export type VotingModuleAdapter = { id: string contractNames: string[] - load: (options: IVotingModuleAdapterOptions) => IVotingModuleAdapter + load: (votingModule: IVotingModuleBase) => IVotingModuleAdapter } -export interface IVotingModuleAdapterOptions { - chainId: string - coreAddress: string - votingModule: IVotingModuleBase -} - -export interface IVotingModuleAdapterContext { +export type IVotingModuleAdapterContext = { id: string - options: IVotingModuleAdapterOptions adapter: IVotingModuleAdapter votingModule: IVotingModuleBase } diff --git a/packages/utils/dao.ts b/packages/utils/dao.ts index ab8124242..26590dd3a 100644 --- a/packages/utils/dao.ts +++ b/packages/utils/dao.ts @@ -95,7 +95,7 @@ export const getAccountChainId = ({ accounts, address, }: { - accounts: Account[] + accounts: readonly Account[] address: string }): string | undefined => accounts.find((account) => account.address === address)?.chainId From ce1516943fb5c08d1ef2b7a2d3f1deb0a95238b2 Mon Sep 17 00:00:00 2001 From: Noah Saso <noahsaso@gmail.com> Date: Wed, 2 Oct 2024 21:58:55 -0400 Subject: [PATCH 25/26] convert some more to HugeDecimals --- packages/state/recoil/selectors/wallet.ts | 4 ++-- .../actions/ConfigureRebalancer/Component.tsx | 8 ++++---- .../components/ProposalVotes/index.tsx | 18 ++++++++++++------ .../components/ProposalVotes/index.tsx | 18 ++++++++++++------ 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/packages/state/recoil/selectors/wallet.ts b/packages/state/recoil/selectors/wallet.ts index 2a35384bd..19dd680f2 100644 --- a/packages/state/recoil/selectors/wallet.ts +++ b/packages/state/recoil/selectors/wallet.ts @@ -362,14 +362,14 @@ export const walletTokenCardInfosSelector = selectorFamily< token.denomOrAddress === getNativeTokenForChainId(chainId).denomOrAddress && // Check if anything staked. - Number( + HugeDecimal.from( get( nativeDelegatedBalanceSelector({ address: walletAddress, chainId, }) ).amount - ) > 0 + ).isPositive() const owner = allAccounts[accountIndex] diff --git a/packages/stateful/actions/core/actions/ConfigureRebalancer/Component.tsx b/packages/stateful/actions/core/actions/ConfigureRebalancer/Component.tsx index cc0ab6f49..fa24ed748 100644 --- a/packages/stateful/actions/core/actions/ConfigureRebalancer/Component.tsx +++ b/packages/stateful/actions/core/actions/ConfigureRebalancer/Component.tsx @@ -646,7 +646,7 @@ export const ConfigureRebalancerComponent: ActionComponent< nativeBalances.data.find( ({ token }) => token.denomOrAddress === denom ) ?? {} - const balance = Number(_balance) || 0 + const balance = HugeDecimal.from(_balance || 0) const price = prices.data.find( ({ token: { denomOrAddress: priceDenom } }) => priceDenom === denom @@ -658,9 +658,9 @@ export const ConfigureRebalancerComponent: ActionComponent< return { symbol: token.symbol, - initialAmount: HugeDecimal.from( - balance - ).toHumanReadableNumber(token.decimals), + initialAmount: balance.toHumanReadableNumber( + token.decimals + ), targetProportion: percent / 100, // Add an extra price to account for the initial balance. prices: new Array(rebalanceTimestamps.length + 1) diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/components/ProposalVotes/index.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/components/ProposalVotes/index.tsx index f33030687..3a0279bef 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/components/ProposalVotes/index.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalMultiple/components/ProposalVotes/index.tsx @@ -2,6 +2,7 @@ import uniqBy from 'lodash.uniqby' import { useEffect, useState } from 'react' import { useRecoilCallback } from 'recoil' +import { HugeDecimal } from '@dao-dao/math' import { DaoProposalMultipleSelectors } from '@dao-dao/state' import { ProposalVote, @@ -33,8 +34,8 @@ export const ProposalVotes = (props: BaseProposalVotesProps) => { const voteOptions = useLoadingVoteOptions() const totalPower = loadingProposal.loading - ? 0 - : Number(loadingProposal.data.total_power) + ? HugeDecimal.zero + : HugeDecimal.from(loadingProposal.data.total_power) const [loading, setLoading] = useState(true) const [noMoreVotes, setNoMoreVotes] = useState(false) @@ -79,8 +80,12 @@ export const ProposalVotes = (props: BaseProposalVotesProps) => { ({ vote, voter, power, rationale, votedAt }): ProposalVote => ({ voterAddress: voter, vote, - votingPowerPercent: - totalPower === 0 ? 0 : (Number(power) / totalPower) * 100, + votingPowerPercent: totalPower.isZero() + ? 0 + : HugeDecimal.from(power) + .div(totalPower) + .times(100) + .toNumber(), rationale, votedAt: votedAt ? new Date(votedAt) : undefined, }) @@ -150,8 +155,9 @@ export const ProposalVotes = (props: BaseProposalVotesProps) => { ({ vote, voter, power, rationale, votedAt }): ProposalVote => ({ voterAddress: voter, vote, - votingPowerPercent: - totalPower === 0 ? 0 : (Number(power) / totalPower) * 100, + votingPowerPercent: totalPower.isZero() + ? 0 + : HugeDecimal.from(power).div(totalPower).times(100).toNumber(), rationale, votedAt: votedAt ? new Date(votedAt) : undefined, }) diff --git a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalVotes/index.tsx b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalVotes/index.tsx index 52a063309..14ffa572d 100644 --- a/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalVotes/index.tsx +++ b/packages/stateful/proposal-module-adapter/adapters/DaoProposalSingle/components/ProposalVotes/index.tsx @@ -2,6 +2,7 @@ import uniqBy from 'lodash.uniqby' import { useEffect, useState } from 'react' import { useRecoilCallback } from 'recoil' +import { HugeDecimal } from '@dao-dao/math' import { DaoProposalSingleCommonSelectors } from '@dao-dao/state' import { ProposalVote, @@ -28,8 +29,8 @@ export const ProposalVotes = (props: BaseProposalVotesProps) => { const loadingProposal = useLoadingProposal() const totalPower = loadingProposal.loading - ? 0 - : Number(loadingProposal.data.total_power) + ? HugeDecimal.zero + : HugeDecimal.from(loadingProposal.data.total_power) const [loading, setLoading] = useState(true) const [noMoreVotes, setNoMoreVotes] = useState(false) @@ -74,8 +75,12 @@ export const ProposalVotes = (props: BaseProposalVotesProps) => { ({ vote, voter, power, rationale, votedAt }): ProposalVote => ({ voterAddress: voter, vote, - votingPowerPercent: - totalPower === 0 ? 0 : (Number(power) / totalPower) * 100, + votingPowerPercent: totalPower.isZero() + ? 0 + : HugeDecimal.from(power) + .div(totalPower) + .times(100) + .toNumber(), rationale, votedAt: votedAt ? new Date(votedAt) : undefined, }) @@ -145,8 +150,9 @@ export const ProposalVotes = (props: BaseProposalVotesProps) => { ({ vote, voter, power, rationale, votedAt }): ProposalVote => ({ voterAddress: voter, vote, - votingPowerPercent: - totalPower === 0 ? 0 : (Number(power) / totalPower) * 100, + votingPowerPercent: totalPower.isZero() + ? 0 + : HugeDecimal.from(power).div(totalPower).times(100).toNumber(), rationale, votedAt: votedAt ? new Date(votedAt) : undefined, }) From c06fa11d8bd98959d5fed306a1c32df77db0ef5d Mon Sep 17 00:00:00 2001 From: Noah Saso <noahsaso@gmail.com> Date: Fri, 4 Oct 2024 17:58:08 -0400 Subject: [PATCH 26/26] fixed token value sorting and small token amount display --- .../components/token/TokenAmountDisplay.tsx | 5 +-- .../stateless/hooks/useTokenSortOptions.ts | 36 +++---------------- packages/utils/token.ts | 36 +++++++++++++++++++ 3 files changed, 44 insertions(+), 33 deletions(-) diff --git a/packages/stateless/components/token/TokenAmountDisplay.tsx b/packages/stateless/components/token/TokenAmountDisplay.tsx index 33dbb899b..c87cfb93f 100644 --- a/packages/stateless/components/token/TokenAmountDisplay.tsx +++ b/packages/stateless/components/token/TokenAmountDisplay.tsx @@ -94,8 +94,9 @@ export const TokenAmountDisplay = ({ : HugeDecimal.fromHumanReadable(_amount.data, decimals) : HugeDecimal.zero - // If amount too small, set to min and add `< ` to prefix. - const amountBelowMin = !!minAmount && amount.lt(minAmount) + // If amount too small and nonzero, set to min and add `< ` to prefix. + const amountBelowMin = + !!minAmount && amount.isPositive() && amount.lt(minAmount) if (amountBelowMin) { amount = HugeDecimal.fromHumanReadable(minAmount, decimals) prefix = `< ${prefix || ''}` diff --git a/packages/stateless/hooks/useTokenSortOptions.ts b/packages/stateless/hooks/useTokenSortOptions.ts index ffbf4f912..9999f9572 100644 --- a/packages/stateless/hooks/useTokenSortOptions.ts +++ b/packages/stateless/hooks/useTokenSortOptions.ts @@ -1,7 +1,10 @@ import { useTranslation } from 'react-i18next' import { SortFn, TokenCardInfo, TypedOption } from '@dao-dao/types' -import { sortTokensValueDescending } from '@dao-dao/utils' +import { + sortTokensValueAscending, + sortTokensValueDescending, +} from '@dao-dao/utils' /** * Options to use with the `useButtonPopupSorter` hook and the `ButtonPopup` @@ -19,36 +22,7 @@ export const useTokenSortOptions = (): TypedOption< }, { label: t('info.lowestUsdValue'), - value: (a, b) => { - // If loading or no price, show at bottom. - const aPrice = - a.lazyInfo.loading || !a.lazyInfo.data.usdUnitPrice?.usdPrice - ? undefined - : a.lazyInfo.data.totalBalance.times( - a.lazyInfo.data.usdUnitPrice.usdPrice - ) - const bPrice = - b.lazyInfo.loading || !b.lazyInfo.data.usdUnitPrice?.usdPrice - ? undefined - : b.lazyInfo.data.totalBalance.times( - b.lazyInfo.data.usdUnitPrice.usdPrice - ) - - // If prices are equal, sort alphabetically by symbol. - return aPrice === bPrice - ? a.token.symbol - .toLocaleLowerCase() - .localeCompare(b.token.symbol.toLocaleLowerCase()) - : aPrice === undefined - ? 1 - : bPrice === undefined - ? -1 - : aPrice.eq(bPrice) - ? 0 - : aPrice.gt(bPrice) - ? 1 - : -1 - }, + value: sortTokensValueAscending, }, { // Most token symbols are in English, so no need to translate. diff --git a/packages/utils/token.ts b/packages/utils/token.ts index 384e7f3f6..3d98fa3e2 100644 --- a/packages/utils/token.ts +++ b/packages/utils/token.ts @@ -142,6 +142,42 @@ export const sortTokensValueDescending: SortFn< b.lazyInfo.data.usdUnitPrice.usdPrice ) + // If prices are equal, sort alphabetically by symbol. + return aPrice === bPrice + ? a.token.symbol + .toLocaleLowerCase() + .localeCompare(b.token.symbol.toLocaleLowerCase()) + : aPrice === undefined + ? 1 + : bPrice === undefined + ? -1 + : aPrice.eq(bPrice) + ? 0 + : aPrice.gt(bPrice) + ? -1 + : 1 +} + +/** + * Function to sort token lists ascending by USD value. + */ +export const sortTokensValueAscending: SortFn< + Pick<TokenCardInfo, 'token' | 'unstakedBalance' | 'lazyInfo'> +> = (a, b) => { + // If loading or no price, show at bottom. + const aPrice = + a.lazyInfo.loading || !a.lazyInfo.data.usdUnitPrice?.usdPrice + ? undefined + : a.lazyInfo.data.totalBalance.times( + a.lazyInfo.data.usdUnitPrice.usdPrice + ) + const bPrice = + b.lazyInfo.loading || !b.lazyInfo.data.usdUnitPrice?.usdPrice + ? undefined + : b.lazyInfo.data.totalBalance.times( + b.lazyInfo.data.usdUnitPrice.usdPrice + ) + // If prices are equal, sort alphabetically by symbol. return aPrice === bPrice ? a.token.symbol