From 0dc386123e4f7cd6d17913893ab919b15e383103 Mon Sep 17 00:00:00 2001 From: Valentine Date: Fri, 26 Apr 2024 18:51:42 +0000 Subject: [PATCH] Use new registry [part 3] - assets registry (#952) * rebase main * use registry for storage and block processor * wip: add assets registry for minifront * wip * bump @penumbra-labs/registry * asset registry refactoring * fix ui tests * fix minifront tests * refactor zero value view * fix unbonding tests * fix storage tests * format * rebase main * Update apps/minifront/src/components/shared/asset-selector.tsx Co-authored-by: Jesse Pinho * Update apps/minifront/src/components/staking/account/delegation-value-view/index.tsx Co-authored-by: Jesse Pinho * Update apps/minifront/src/state/staking/index.ts Co-authored-by: Jesse Pinho * revert Jesse reduce suggestion * bump registry & use assets from indexed-db * format * move zero-value-view & bump registry * wip * remove equivalentValues * rebase main --------- Co-authored-by: Jesse Pinho --- .../src/components/ibc/ibc-loader.ts | 28 ++- .../components/ibc/ibc-out/ibc-out-form.tsx | 4 +- .../src/components/send/send-form/index.tsx | 21 +- .../src/components/shared/asset-selector.tsx | 26 +- .../src/components/shared/gas-fee.tsx | 12 +- .../delegation-value-view/index.test.tsx | 22 +- .../account/delegation-value-view/index.tsx | 14 +- .../staking/account/delegations.tsx | 8 +- .../staking/account/header/constants.ts | 16 -- .../staking/account/header/index.tsx | 9 +- .../account/header/unbonding-tokens.tsx | 11 +- .../src/components/staking/layout.tsx | 11 +- .../src/components/swap/swap-loader.tsx | 14 +- apps/minifront/src/fetchers/assets.ts | 20 +- apps/minifront/src/fetchers/registry.ts | 26 +- apps/minifront/src/state/ibc.ts | 13 +- .../minifront/src/state/staking/index.test.ts | 4 + apps/minifront/src/state/staking/index.ts | 69 ++++-- apps/minifront/src/state/swap.test.ts | 7 +- apps/minifront/src/utils/zero-value-view.tsx | 18 ++ packages/constants/src/assets.ts | 16 -- .../constants/src/local-asset-registry.json | 233 ------------------ packages/getters/src/assets-response.ts | 6 + packages/getters/src/fee.ts | 4 + packages/query/src/block-processor.ts | 24 +- packages/services-context/package.json | 1 + packages/services-context/src/index.ts | 7 + .../delegations-by-address-index.test.ts | 10 +- .../delegations-by-address-index.ts | 8 +- .../index.test.ts | 9 +- packages/storage/package.json | 1 + packages/storage/src/indexed-db/index.ts | 22 +- .../storage/src/indexed-db/indexed-db.test.ts | 23 +- .../components/ui/tx/view/swap/index.test.tsx | 2 +- .../ui/components/ui/tx/view/swap/index.tsx | 21 +- pnpm-lock.yaml | 11 + 36 files changed, 322 insertions(+), 429 deletions(-) delete mode 100644 apps/minifront/src/components/staking/account/header/constants.ts create mode 100644 apps/minifront/src/utils/zero-value-view.tsx delete mode 100644 packages/constants/src/local-asset-registry.json create mode 100644 packages/getters/src/assets-response.ts create mode 100644 packages/getters/src/fee.ts diff --git a/apps/minifront/src/components/ibc/ibc-loader.ts b/apps/minifront/src/components/ibc/ibc-loader.ts index e228150ad6..c98409ff0a 100644 --- a/apps/minifront/src/components/ibc/ibc-loader.ts +++ b/apps/minifront/src/components/ibc/ibc-loader.ts @@ -3,30 +3,32 @@ import { BalancesResponse } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumb import { getBalances } from '../../fetchers/balances'; import { useStore } from '../../state'; import { filterBalancesPerChain } from '../../state/ibc'; -import { getChainId } from '../../fetchers/chain-id'; -import { Chain, ChainRegistryClient } from '@penumbra-labs/registry'; +import { Chain } from '@penumbra-labs/registry'; +import { Metadata } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb'; +import { getIbcConnections, getStakingTokenMetadata } from '../../fetchers/registry'; +import { getAllAssets } from '../../fetchers/assets'; export interface IbcLoaderResponse { balances: BalancesResponse[]; chains: Chain[]; + stakingTokenMetadata: Metadata; + assets: Metadata[]; } -const getIbcConnections = async () => { - const chainId = await getChainId(); - if (!chainId) throw new Error('Could not fetch chain id'); - - const registryClient = new ChainRegistryClient(); - const { ibcConnections } = await registryClient.get(chainId); - return ibcConnections; -}; - export const IbcLoader: LoaderFunction = async (): Promise => { const assetBalances = await getBalances(); const ibcConnections = await getIbcConnections(); + const stakingTokenMetadata = await getStakingTokenMetadata(); + const assets = await getAllAssets(); if (assetBalances[0]) { const initialChain = ibcConnections[0]; - const initialSelection = filterBalancesPerChain(assetBalances, initialChain)[0]; + const initialSelection = filterBalancesPerChain( + assetBalances, + initialChain, + stakingTokenMetadata, + assets, + )[0]; // set initial account if accounts exist and asset if account has asset list useStore.setState(state => { @@ -35,5 +37,5 @@ export const IbcLoader: LoaderFunction = async (): Promise => }); } - return { balances: assetBalances, chains: ibcConnections }; + return { balances: assetBalances, chains: ibcConnections, stakingTokenMetadata, assets }; }; diff --git a/apps/minifront/src/components/ibc/ibc-out/ibc-out-form.tsx b/apps/minifront/src/components/ibc/ibc-out/ibc-out-form.tsx index df7b65f7ad..c519218736 100644 --- a/apps/minifront/src/components/ibc/ibc-out/ibc-out-form.tsx +++ b/apps/minifront/src/components/ibc/ibc-out/ibc-out-form.tsx @@ -10,7 +10,7 @@ import { IbcLoaderResponse } from '../ibc-loader'; import { LockOpen2Icon } from '@radix-ui/react-icons'; export const IbcOutForm = () => { - const { balances } = useLoaderData() as IbcLoaderResponse; + const { balances, stakingTokenMetadata, assets } = useLoaderData() as IbcLoaderResponse; const { sendIbcWithdraw, destinationChainAddress, @@ -21,7 +21,7 @@ export const IbcOutForm = () => { setSelection, chain, } = useStore(ibcSelector); - const filteredBalances = filterBalancesPerChain(balances, chain); + const filteredBalances = filterBalancesPerChain(balances, chain, stakingTokenMetadata, assets); const validationErrors = useStore(ibcValidationErrors); return ( diff --git a/apps/minifront/src/components/send/send-form/index.tsx b/apps/minifront/src/components/send/send-form/index.tsx index c9ad5ede5c..ce29fdf378 100644 --- a/apps/minifront/src/components/send/send-form/index.tsx +++ b/apps/minifront/src/components/send/send-form/index.tsx @@ -12,8 +12,15 @@ import InputToken from '../../shared/input-token'; import { useRefreshFee } from './use-refresh-fee'; import { GasFee } from '../../shared/gas-fee'; import { BalancesResponse } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb'; +import { Metadata } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb'; +import { getStakingTokenMetadata } from '../../../fetchers/registry'; -export const SendAssetBalanceLoader: LoaderFunction = async (): Promise => { +export interface SendLoaderResponse { + assetBalances: BalancesResponse[]; + feeAssetMetadata: Metadata; +} + +export const SendAssetBalanceLoader: LoaderFunction = async (): Promise => { await throwIfPraxNotConnectedTimeout(); const assetBalances = await getBalances(); @@ -23,12 +30,13 @@ export const SendAssetBalanceLoader: LoaderFunction = async (): Promise { - const assetBalances = useLoaderData() as BalancesResponse[]; + const { assetBalances, feeAssetMetadata } = useLoaderData() as SendLoaderResponse; const { selection, amount, @@ -94,7 +102,12 @@ export const SendForm = () => { balances={assetBalances} /> - + boolean; } -const sortedAssets = [...localAssets].sort((a, b) => - a.symbol.toLocaleLowerCase() < b.symbol.toLocaleLowerCase() ? -1 : 1, -); - /** * If the `filter` rejects the currently selected `asset`, switch to a different * `asset`. @@ -50,17 +47,26 @@ const switchAssetIfNecessary = ({ }; const useFilteredAssets = ({ value, onChange, filter }: AssetSelectorProps) => { + const { assets } = useLoaderData() as SwapLoaderResponse; + const sortedAssets = useMemo( + () => + [...assets].sort((a, b) => + a.symbol.toLocaleLowerCase() < b.symbol.toLocaleLowerCase() ? -1 : 1, + ), + [assets], + ); + const [search, setSearch] = useState(''); - let assets = filter ? sortedAssets.filter(filter) : sortedAssets; - assets = search ? assets.filter(bySearch(search)) : assets; + let filteredAssets = filter ? sortedAssets.filter(filter) : sortedAssets; + filteredAssets = search ? assets.filter(bySearch(search)) : assets; useEffect( - () => switchAssetIfNecessary({ value, onChange, filter, assets }), - [filter, value, assets, onChange], + () => switchAssetIfNecessary({ value, onChange, filter, assets: filteredAssets }), + [filter, value, filteredAssets, onChange], ); - return { assets, search, setSearch }; + return { assets: filteredAssets, search, setSearch }; }; const bySearch = (search: string) => (asset: Metadata) => diff --git a/apps/minifront/src/components/shared/gas-fee.tsx b/apps/minifront/src/components/shared/gas-fee.tsx index 4050d7b75e..6347dc7f95 100644 --- a/apps/minifront/src/components/shared/gas-fee.tsx +++ b/apps/minifront/src/components/shared/gas-fee.tsx @@ -7,11 +7,11 @@ import { SegmentedPickerOption, } from '@penumbra-zone/ui/components/ui/segmented-picker'; import { InputBlock } from './input-block'; -import { localAssets } from '@penumbra-zone/constants/src/assets'; import { ValueViewComponent } from '@penumbra-zone/ui/components/ui/tx/view/value'; -import { ValueView } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb'; - -const PENUMBRA_DENOM_METADATA = localAssets.find(asset => asset.display === 'penumbra')!; +import { + Metadata, + ValueView, +} from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb'; const FEE_TIER_OPTIONS: SegmentedPickerOption[] = [ { @@ -31,10 +31,12 @@ const FEE_TIER_OPTIONS: SegmentedPickerOption[] = [ export const GasFee = ({ fee, feeTier, + feeAssetMetadata, setFeeTier, }: { fee: Fee | undefined; feeTier: FeeTier_Tier; + feeAssetMetadata: Metadata; setFeeTier: (feeTier: FeeTier_Tier) => void; }) => { let feeValueView: ValueView | undefined; @@ -42,7 +44,7 @@ export const GasFee = ({ feeValueView = new ValueView({ valueView: { case: 'knownAssetId', - value: { amount: fee.amount, metadata: PENUMBRA_DENOM_METADATA }, + value: { amount: fee.amount, metadata: feeAssetMetadata }, }, }); diff --git a/apps/minifront/src/components/staking/account/delegation-value-view/index.test.tsx b/apps/minifront/src/components/staking/account/delegation-value-view/index.test.tsx index 3c864774e8..0017e8b76b 100644 --- a/apps/minifront/src/components/staking/account/delegation-value-view/index.test.tsx +++ b/apps/minifront/src/components/staking/account/delegation-value-view/index.test.tsx @@ -5,7 +5,6 @@ import { Metadata, ValueView, } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb'; -import { STAKING_TOKEN_METADATA } from '@penumbra-zone/constants/src/assets'; import { ValidatorInfo } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/component/stake/v1/stake_pb'; import { bech32mIdentityKey } from '@penumbra-zone/bech32m/penumbravalid'; @@ -37,6 +36,15 @@ const SOME_OTHER_TOKEN_METADATA = new Metadata({ symbol: 'SOT', }); +const STAKING_TOKEN_METADATA = new Metadata({ + display: 'penumbra', + base: 'penumbra', + denomUnits: [{ denom: 'upenumbra' }, { denom: 'penumbra', exponent: 6 }], + name: 'penumbra', + penumbraAssetId: { inner: new Uint8Array([2, 5, 6, 7]) }, + symbol: 'UM', +}); + const validatorInfo = new ValidatorInfo({ validator: { identityKey: {}, @@ -92,19 +100,25 @@ const valueView = new ValueView({ describe('', () => { it('shows balance of the delegation token', () => { - const { container } = render(); + const { container } = render( + , + ); expect(container).toHaveTextContent('1delUM(abc...xyz)'); }); it("shows the delegation token's equivalent value in terms of the staking token", () => { - const { container } = render(); + const { container } = render( + , + ); expect(container).toHaveTextContent('1.33UM'); }); it('does not show other equivalent values', () => { - const { container } = render(); + const { container } = render( + , + ); expect(container).not.toHaveTextContent('2.66SOT'); }); diff --git a/apps/minifront/src/components/staking/account/delegation-value-view/index.tsx b/apps/minifront/src/components/staking/account/delegation-value-view/index.tsx index bfae801781..ad4591caec 100644 --- a/apps/minifront/src/components/staking/account/delegation-value-view/index.tsx +++ b/apps/minifront/src/components/staking/account/delegation-value-view/index.tsx @@ -1,4 +1,7 @@ -import { ValueView } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb'; +import { + Metadata, + ValueView, +} from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb'; import { ValidatorInfoComponent } from './validator-info-component'; import { ValueViewComponent } from '@penumbra-zone/ui/components/ui/tx/view/value'; import { StakingActions } from './staking-actions'; @@ -10,7 +13,6 @@ import { getValidatorInfoFromValueView, } from '@penumbra-zone/getters/src/value-view'; import { asValueView } from '@penumbra-zone/getters/src/equivalent-value'; -import { STAKING_TOKEN } from '@penumbra-zone/constants/src/assets'; /** * Renders a `ValueView` that contains a delegation token, along with the @@ -25,6 +27,7 @@ export const DelegationValueView = memo( valueView, votingPowerAsIntegerPercentage, unstakedTokens, + stakingTokenMetadata, }: { /** * A `ValueView` representing the address's balance of the given delegation @@ -37,18 +40,19 @@ export const DelegationValueView = memo( * Used to show the user how many tokens they have available to delegate. */ unstakedTokens?: ValueView; + stakingTokenMetadata: Metadata; }) => { const validatorInfo = getValidatorInfoFromValueView(valueView); const metadata = getMetadata(valueView); const equivalentValueOfStakingToken = useMemo(() => { - const equivalentValue = getEquivalentValues(valueView).find( - equivalentValue => equivalentValue.numeraire?.display === STAKING_TOKEN, + const equivalentValue = getEquivalentValues(valueView).find(equivalentValue => + equivalentValue.numeraire?.penumbraAssetId?.equals(stakingTokenMetadata.penumbraAssetId), ); if (equivalentValue) return asValueView(equivalentValue); return undefined; - }, [valueView]); + }, [valueView, stakingTokenMetadata.penumbraAssetId]); return (
diff --git a/apps/minifront/src/components/staking/account/delegations.tsx b/apps/minifront/src/components/staking/account/delegations.tsx index 75e3202176..d0318c3f64 100644 --- a/apps/minifront/src/components/staking/account/delegations.tsx +++ b/apps/minifront/src/components/staking/account/delegations.tsx @@ -1,7 +1,10 @@ import { AnimatePresence, motion } from 'framer-motion'; import { AllSlices } from '../../../state'; import { DelegationValueView } from './delegation-value-view'; -import { ValueView } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb'; +import { + Metadata, + ValueView, +} from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb'; import { useStoreShallow } from '../../../utils/use-store-shallow'; import { getValidatorIdentityKeyFromValueView } from '@penumbra-zone/getters/src/value-view'; import { bech32mIdentityKey } from '@penumbra-zone/bech32m/penumbravalid'; @@ -19,7 +22,7 @@ const delegationsSelector = (state: AllSlices) => ({ votingPowerByValidatorInfo: state.staking.votingPowerByValidatorInfo, }); -export const Delegations = () => { +export const Delegations = ({ stakingTokenMetadata }: { stakingTokenMetadata: Metadata }) => { const { delegations, unstakedTokens, votingPowerByValidatorInfo } = useStoreShallow(delegationsSelector); @@ -35,6 +38,7 @@ export const Delegations = () => { ({ account: state.staking.account, @@ -33,6 +35,7 @@ export const Header = () => { const unstakedTokens = unstakedTokensByAccount.get(account); const unbondingTokens = unbondingTokensByAccount.get(account); + const stakingTokenMetadata = useLoaderData() as Metadata; return ( @@ -41,7 +44,7 @@ export const Header = () => {
- + @@ -49,6 +52,7 @@ export const Header = () => { helpText='Total amount of UM you will receive, assuming no slashing, when you claim your unbonding tokens that are currently still in their unbonding period.' tokens={unbondingTokens?.notYetClaimable.tokens} total={unbondingTokens?.notYetClaimable.total} + stakingTokenMetadata={stakingTokenMetadata} /> @@ -57,6 +61,7 @@ export const Header = () => { helpText='Total amount of UM you will receive, assuming no slashing, when you claim your unbonding tokens that are currently eligible for claiming.' tokens={unbondingTokens?.claimable.tokens} total={unbondingTokens?.claimable.total} + stakingTokenMetadata={stakingTokenMetadata} > {!!unbondingTokens?.claimable.tokens.length && (