diff --git a/apps/console-lite-e2e/src/support/mocks/generate-assets.ts b/apps/console-lite-e2e/src/support/mocks/generate-assets.ts index dfee971f3..952fe07e4 100644 --- a/apps/console-lite-e2e/src/support/mocks/generate-assets.ts +++ b/apps/console-lite-e2e/src/support/mocks/generate-assets.ts @@ -16,9 +16,36 @@ export const generateAssets = (override?: PartialDeep) => { source: { __typename: 'ERC20', contractAddress: '0x0158031158Bb4dF2AD02eAA31e8963E84EA978a4', + lifetimeLimit: '1', + withdrawThreshold: '2', }, quantum: '', status: Types.AssetStatus.STATUS_ENABLED, + infrastructureFeeAccount: { + balance: '1', + __typename: 'Account', + }, + globalRewardPoolAccount: { + balance: '2', + __typename: 'Account', + }, + takerFeeRewardAccount: { + balance: '3', + __typename: 'Account', + }, + makerFeeRewardAccount: { + balance: '4', + __typename: 'Account', + }, + lpFeeRewardAccount: { + balance: '5', + __typename: 'Account', + }, + marketProposerRewardAccount: { + balance: '6', + __typename: 'Account', + }, + __typename: 'Asset', }, }, { @@ -30,9 +57,36 @@ export const generateAssets = (override?: PartialDeep) => { source: { __typename: 'ERC20', contractAddress: '0x0158031158Bb4dF2AD02eAA31e8963E84EA978a4', + lifetimeLimit: '1', + withdrawThreshold: '2', }, quantum: '', status: Types.AssetStatus.STATUS_ENABLED, + infrastructureFeeAccount: { + balance: '1', + __typename: 'Account', + }, + globalRewardPoolAccount: { + balance: '2', + __typename: 'Account', + }, + takerFeeRewardAccount: { + balance: '3', + __typename: 'Account', + }, + makerFeeRewardAccount: { + balance: '4', + __typename: 'Account', + }, + lpFeeRewardAccount: { + balance: '5', + __typename: 'Account', + }, + marketProposerRewardAccount: { + balance: '6', + __typename: 'Account', + }, + __typename: 'Asset', }, }, { @@ -43,9 +97,20 @@ export const generateAssets = (override?: PartialDeep) => { name: 'Asto', source: { __typename: 'BuiltinAsset', + maxFaucetAmountMint: '3', }, quantum: '', status: Types.AssetStatus.STATUS_ENABLED, + infrastructureFeeAccount: { + balance: '0', + __typename: 'Account', + }, + globalRewardPoolAccount: null, + takerFeeRewardAccount: null, + makerFeeRewardAccount: null, + lpFeeRewardAccount: null, + marketProposerRewardAccount: null, + __typename: 'Asset', }, }, ], diff --git a/apps/token/src/components/vega-wallet/hooks.ts b/apps/token/src/components/vega-wallet/hooks.ts index 84f52a2bf..156b3f69f 100644 --- a/apps/token/src/components/vega-wallet/hooks.ts +++ b/apps/token/src/components/vega-wallet/hooks.ts @@ -17,6 +17,7 @@ import type { } from './__generated__/Delegations'; import { useVegaWallet } from '@vegaprotocol/wallet'; import { useContracts } from '../../contexts/contracts/contracts-context'; +import type { ERC20Asset } from '@vegaprotocol/assets'; import { isAssetTypeERC20 } from '@vegaprotocol/assets'; import { AccountType } from '@vegaprotocol/types'; @@ -118,8 +119,9 @@ export const usePollForDelegations = () => { .filter((a) => a.type === AccountType.ACCOUNT_TYPE_GENERAL) .map((a) => { const isVega = - isAssetTypeERC20(a.asset) && - a.asset.source.contractAddress === vegaToken.address; + isAssetTypeERC20(a.asset as ERC20Asset) && + (a.asset as ERC20Asset).source.contractAddress === + vegaToken.address; return { isVega, @@ -132,8 +134,8 @@ export const usePollForDelegations = () => { ), image: isVega ? vegaBlack : noIcon, border: isVega, - address: isAssetTypeERC20(a.asset) - ? a.asset.source.contractAddress + address: isAssetTypeERC20(a.asset as ERC20Asset) + ? (a.asset as ERC20Asset).source.contractAddress : undefined, }; }) diff --git a/apps/trading-e2e/src/integration/market-info.cy.ts b/apps/trading-e2e/src/integration/market-info.cy.ts index 70267a74b..b0ab42d80 100644 --- a/apps/trading-e2e/src/integration/market-info.cy.ts +++ b/apps/trading-e2e/src/integration/market-info.cy.ts @@ -71,10 +71,20 @@ describe('market info is displayed', { tags: '@smoke' }, () => { it('settlement asset displayed', () => { cy.getByTestId(marketTitle).contains('Settlement asset').click(); - - validateMarketDataRow(0, 'Name', 'tBTC TEST'); - validateMarketDataRow(1, 'Symbol', 'tBTC'); - validateMarketDataRow(2, 'Asset ID', 'market-0'); + validateMarketDataRow(0, 'ID', 'asset-id'); + validateMarketDataRow(1, 'Type', 'ERC20'); + validateMarketDataRow(2, 'Name', 'Euro'); + validateMarketDataRow(3, 'Symbol', 'tEURO'); + validateMarketDataRow(4, 'Decimals', '5'); + validateMarketDataRow(5, 'Quantum', '1'); + validateMarketDataRow(6, 'Status', 'Enabled'); + validateMarketDataRow( + 7, + 'Contract address', + '0x0158031158Bb4dF2AD02eAA31e8963E84EA978a4' + ); + validateMarketDataRow(8, 'Withdrawal threshold', '0.00050'); + validateMarketDataRow(9, 'Lifetime limit', '1,230.00000'); }); it('metadata displayed', () => { @@ -173,7 +183,9 @@ describe('market info is displayed', { tags: '@smoke' }, () => { }); afterEach('close toggle', () => { - cy.get('[data-state="open"]').find('button').click(); + cy.get('[data-state="open"]').then((tab) => { + if (tab) tab.find('button').trigger('click'); + }); }); function validateMarketDataRow( diff --git a/apps/trading-e2e/src/support/mocks/generate-assets.ts b/apps/trading-e2e/src/support/mocks/generate-assets.ts index d5da22d46..01ada70eb 100644 --- a/apps/trading-e2e/src/support/mocks/generate-assets.ts +++ b/apps/trading-e2e/src/support/mocks/generate-assets.ts @@ -1,8 +1,59 @@ import merge from 'lodash/merge'; -import type { AssetsQuery } from '@vegaprotocol/assets'; +import type { AssetQuery, AssetsQuery } from '@vegaprotocol/assets'; import { Schema as Types } from '@vegaprotocol/types'; import type { PartialDeep } from 'type-fest'; +export const generateAsset = (override?: PartialDeep) => { + const defaultAssets: AssetsQuery = { + assetsConnection: { + edges: [ + { + node: { + id: 'asset-id', + symbol: 'tEURO', + decimals: 5, + name: 'Euro', + source: { + contractAddress: '0x0158031158Bb4dF2AD02eAA31e8963E84EA978a4', + lifetimeLimit: '123000000', + withdrawThreshold: '50', + __typename: 'ERC20', + }, + quantum: '1', + status: Types.AssetStatus.STATUS_ENABLED, + infrastructureFeeAccount: { + balance: '1', + __typename: 'Account', + }, + globalRewardPoolAccount: { + balance: '2', + __typename: 'Account', + }, + takerFeeRewardAccount: { + balance: '3', + __typename: 'Account', + }, + makerFeeRewardAccount: { + balance: '4', + __typename: 'Account', + }, + lpFeeRewardAccount: { + balance: '5', + __typename: 'Account', + }, + marketProposerRewardAccount: { + balance: '6', + __typename: 'Account', + }, + __typename: 'Asset', + }, + }, + ], + }, + }; + return merge(defaultAssets, override); +}; + export const generateAssets = (override?: PartialDeep) => { const defaultAssets: AssetsQuery = { assetsConnection: { @@ -14,11 +65,38 @@ export const generateAssets = (override?: PartialDeep) => { decimals: 5, name: 'Euro', source: { - __typename: 'ERC20', contractAddress: '0x0158031158Bb4dF2AD02eAA31e8963E84EA978a4', + lifetimeLimit: '123000000', + withdrawThreshold: '50', + __typename: 'ERC20', }, quantum: '1', status: Types.AssetStatus.STATUS_ENABLED, + infrastructureFeeAccount: { + balance: '1', + __typename: 'Account', + }, + globalRewardPoolAccount: { + balance: '2', + __typename: 'Account', + }, + takerFeeRewardAccount: { + balance: '3', + __typename: 'Account', + }, + makerFeeRewardAccount: { + balance: '4', + __typename: 'Account', + }, + lpFeeRewardAccount: { + balance: '5', + __typename: 'Account', + }, + marketProposerRewardAccount: { + balance: '6', + __typename: 'Account', + }, + __typename: 'Asset', }, }, { @@ -28,11 +106,38 @@ export const generateAssets = (override?: PartialDeep) => { decimals: 5, name: 'DAI', source: { - __typename: 'ERC20', contractAddress: '0x26223f9C67871CFcEa329975f7BC0C9cB8FBDb9b', + lifetimeLimit: '123000000', + withdrawThreshold: '50', + __typename: 'ERC20', }, quantum: '1', status: Types.AssetStatus.STATUS_ENABLED, + infrastructureFeeAccount: { + balance: '1', + __typename: 'Account', + }, + globalRewardPoolAccount: { + balance: '2', + __typename: 'Account', + }, + takerFeeRewardAccount: { + balance: '3', + __typename: 'Account', + }, + makerFeeRewardAccount: { + balance: '4', + __typename: 'Account', + }, + lpFeeRewardAccount: { + balance: '5', + __typename: 'Account', + }, + marketProposerRewardAccount: { + balance: '6', + __typename: 'Account', + }, + __typename: 'Asset', }, }, { @@ -42,10 +147,21 @@ export const generateAssets = (override?: PartialDeep) => { decimals: 5, name: 'Asto', source: { + maxFaucetAmountMint: '5000000000', __typename: 'BuiltinAsset', }, quantum: '1', status: Types.AssetStatus.STATUS_ENABLED, + infrastructureFeeAccount: { + balance: '0', + __typename: 'Account', + }, + globalRewardPoolAccount: null, + takerFeeRewardAccount: null, + makerFeeRewardAccount: null, + lpFeeRewardAccount: null, + marketProposerRewardAccount: null, + __typename: 'Asset', }, }, // NOTE: These assets ids and contract addresses are real assets on Sepolia, this is needed @@ -59,10 +175,36 @@ export const generateAssets = (override?: PartialDeep) => { decimals: 5, status: Types.AssetStatus.STATUS_ENABLED, source: { - __typename: 'ERC20', contractAddress: '0x1d525fB145Af5c51766a89706C09fE07E6058D1D', + lifetimeLimit: '123000000', + withdrawThreshold: '50', + __typename: 'ERC20', }, quantum: '1', + infrastructureFeeAccount: { + balance: '1', + __typename: 'Account', + }, + globalRewardPoolAccount: { + balance: '2', + __typename: 'Account', + }, + takerFeeRewardAccount: { + balance: '3', + __typename: 'Account', + }, + makerFeeRewardAccount: { + balance: '4', + __typename: 'Account', + }, + lpFeeRewardAccount: { + balance: '5', + __typename: 'Account', + }, + marketProposerRewardAccount: { + balance: '6', + __typename: 'Account', + }, __typename: 'Asset', }, __typename: 'AssetEdge', @@ -75,10 +217,36 @@ export const generateAssets = (override?: PartialDeep) => { decimals: 5, status: Types.AssetStatus.STATUS_ENABLED, source: { - __typename: 'ERC20', contractAddress: '0x444b9aDA947130Fc320a144cd22bC1641e5c9d81', + lifetimeLimit: '123000000', + withdrawThreshold: '50', + __typename: 'ERC20', }, quantum: '1', + infrastructureFeeAccount: { + balance: '1', + __typename: 'Account', + }, + globalRewardPoolAccount: { + balance: '2', + __typename: 'Account', + }, + takerFeeRewardAccount: { + balance: '3', + __typename: 'Account', + }, + makerFeeRewardAccount: { + balance: '4', + __typename: 'Account', + }, + lpFeeRewardAccount: { + balance: '5', + __typename: 'Account', + }, + marketProposerRewardAccount: { + balance: '6', + __typename: 'Account', + }, __typename: 'Asset', }, __typename: 'AssetEdge', diff --git a/apps/trading-e2e/src/support/trading.ts b/apps/trading-e2e/src/support/trading.ts index 74f21aedc..4434e08ab 100644 --- a/apps/trading-e2e/src/support/trading.ts +++ b/apps/trading-e2e/src/support/trading.ts @@ -2,7 +2,7 @@ import { aliasQuery } from '@vegaprotocol/cypress'; import type { MarketState } from '@vegaprotocol/types'; import type { CyHttpMessages } from 'cypress/types/net-stubbing'; import { generateAccounts } from './mocks/generate-accounts'; -import { generateAssets } from './mocks/generate-assets'; +import { generateAsset, generateAssets } from './mocks/generate-assets'; import { generateCandles } from './mocks/generate-candles'; import { generateChart } from './mocks/generate-chart'; import { generateDealTicketQuery } from './mocks/generate-deal-ticket-query'; @@ -47,6 +47,7 @@ export const mockTradingPage = ( aliasQuery(req, 'Margins', generateMargins()); aliasQuery(req, 'DealTicket', generateDealTicketQuery({ market: { state } })); aliasQuery(req, 'Assets', generateAssets()); + aliasQuery(req, 'Asset', generateAsset()); aliasQuery( req, diff --git a/libs/accounts/src/lib/accounts-data-provider.ts b/libs/accounts/src/lib/accounts-data-provider.ts index 8a381109d..0781d4b42 100644 --- a/libs/accounts/src/lib/accounts-data-provider.ts +++ b/libs/accounts/src/lib/accounts-data-provider.ts @@ -16,8 +16,9 @@ import { import { AccountType } from '@vegaprotocol/types'; import type { Market } from '@vegaprotocol/market-list'; import { marketsProvider } from '@vegaprotocol/market-list'; -import type { AssetsFieldsFragment } from '@vegaprotocol/assets'; + import { assetsProvider } from '@vegaprotocol/assets'; +import type { Asset } from '@vegaprotocol/assets'; function isAccount( account: @@ -41,7 +42,7 @@ export const getId = ( export type Account = Omit & { market?: Market | null; - asset: AssetsFieldsFragment; + asset: Asset; }; const update = ( @@ -167,7 +168,7 @@ export const accountsDataProvider = makeDerivedDataProvider( (market: Market) => market.id === account.market?.id ); const asset = assets.find( - (asset: AssetsFieldsFragment) => asset.id === account.asset?.id + (asset: Asset) => asset.id === account.asset?.id ); if (asset) { return { diff --git a/libs/assets/src/lib/Asset.graphql b/libs/assets/src/lib/Asset.graphql new file mode 100644 index 000000000..bbae3b952 --- /dev/null +++ b/libs/assets/src/lib/Asset.graphql @@ -0,0 +1,47 @@ +fragment AssetFields on Asset { + id + name + symbol + decimals + quantum + source { + __typename + ... on ERC20 { + contractAddress + lifetimeLimit + withdrawThreshold + } + ... on BuiltinAsset { + maxFaucetAmountMint + } + } + status + infrastructureFeeAccount { + balance + } + globalRewardPoolAccount { + balance + } + takerFeeRewardAccount { + balance + } + makerFeeRewardAccount { + balance + } + lpFeeRewardAccount { + balance + } + marketProposerRewardAccount { + balance + } +} + +query Asset($assetId: ID!) { + assetsConnection(id: $assetId) { + edges { + node { + ...AssetFields + } + } + } +} diff --git a/libs/assets/src/lib/Assets.graphql b/libs/assets/src/lib/Assets.graphql index 4eb0477e6..8a4dbc957 100644 --- a/libs/assets/src/lib/Assets.graphql +++ b/libs/assets/src/lib/Assets.graphql @@ -1,22 +1,8 @@ -fragment AssetsFields on Asset { - id - name - symbol - decimals - quantum - status - source { - ... on ERC20 { - contractAddress - } - } -} - query Assets { assetsConnection { edges { node { - ...AssetsFields + ...AssetFields } } } diff --git a/libs/assets/src/lib/__generated___/Asset.ts b/libs/assets/src/lib/__generated___/Asset.ts new file mode 100644 index 000000000..f0443317e --- /dev/null +++ b/libs/assets/src/lib/__generated___/Asset.ts @@ -0,0 +1,92 @@ +import { Schema as Types } from '@vegaprotocol/types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type AssetFieldsFragment = { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string, status: Types.AssetStatus, source: { __typename: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string }, infrastructureFeeAccount: { __typename?: 'Account', balance: string }, globalRewardPoolAccount?: { __typename?: 'Account', balance: string } | null, takerFeeRewardAccount?: { __typename?: 'Account', balance: string } | null, makerFeeRewardAccount?: { __typename?: 'Account', balance: string } | null, lpFeeRewardAccount?: { __typename?: 'Account', balance: string } | null, marketProposerRewardAccount?: { __typename?: 'Account', balance: string } | null }; + +export type AssetQueryVariables = Types.Exact<{ + assetId: Types.Scalars['ID']; +}>; + + +export type AssetQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string, status: Types.AssetStatus, source: { __typename: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string }, infrastructureFeeAccount: { __typename?: 'Account', balance: string }, globalRewardPoolAccount?: { __typename?: 'Account', balance: string } | null, takerFeeRewardAccount?: { __typename?: 'Account', balance: string } | null, makerFeeRewardAccount?: { __typename?: 'Account', balance: string } | null, lpFeeRewardAccount?: { __typename?: 'Account', balance: string } | null, marketProposerRewardAccount?: { __typename?: 'Account', balance: string } | null } } | null> | null } | null }; + +export const AssetFieldsFragmentDoc = gql` + fragment AssetFields on Asset { + id + name + symbol + decimals + quantum + source { + __typename + ... on ERC20 { + contractAddress + lifetimeLimit + withdrawThreshold + } + ... on BuiltinAsset { + maxFaucetAmountMint + } + } + status + infrastructureFeeAccount { + balance + } + globalRewardPoolAccount { + balance + } + takerFeeRewardAccount { + balance + } + makerFeeRewardAccount { + balance + } + lpFeeRewardAccount { + balance + } + marketProposerRewardAccount { + balance + } +} + `; +export const AssetDocument = gql` + query Asset($assetId: ID!) { + assetsConnection(id: $assetId) { + edges { + node { + ...AssetFields + } + } + } +} + ${AssetFieldsFragmentDoc}`; + +/** + * __useAssetQuery__ + * + * To run a query within a React component, call `useAssetQuery` and pass it any options that fit your needs. + * When your component renders, `useAssetQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useAssetQuery({ + * variables: { + * assetId: // value for 'assetId' + * }, + * }); + */ +export function useAssetQuery(baseOptions: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(AssetDocument, options); + } +export function useAssetLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(AssetDocument, options); + } +export type AssetQueryHookResult = ReturnType; +export type AssetLazyQueryHookResult = ReturnType; +export type AssetQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/libs/assets/src/lib/__generated___/Assets.ts b/libs/assets/src/lib/__generated___/Assets.ts index 07c585f43..ba9ffb87c 100644 --- a/libs/assets/src/lib/__generated___/Assets.ts +++ b/libs/assets/src/lib/__generated___/Assets.ts @@ -1,41 +1,26 @@ import { Schema as Types } from '@vegaprotocol/types'; import { gql } from '@apollo/client'; +import { AssetFieldsFragmentDoc } from './Asset'; import * as Apollo from '@apollo/client'; const defaultOptions = {} as const; -export type AssetsFieldsFragment = { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string, status: Types.AssetStatus, source: { __typename?: 'BuiltinAsset' } | { __typename?: 'ERC20', contractAddress: string } }; - export type AssetsQueryVariables = Types.Exact<{ [key: string]: never; }>; -export type AssetsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string, status: Types.AssetStatus, source: { __typename?: 'BuiltinAsset' } | { __typename?: 'ERC20', contractAddress: string } } } | null> | null } | null }; +export type AssetsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string, status: Types.AssetStatus, source: { __typename: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string }, infrastructureFeeAccount: { __typename?: 'Account', balance: string }, globalRewardPoolAccount?: { __typename?: 'Account', balance: string } | null, takerFeeRewardAccount?: { __typename?: 'Account', balance: string } | null, makerFeeRewardAccount?: { __typename?: 'Account', balance: string } | null, lpFeeRewardAccount?: { __typename?: 'Account', balance: string } | null, marketProposerRewardAccount?: { __typename?: 'Account', balance: string } | null } } | null> | null } | null }; + -export const AssetsFieldsFragmentDoc = gql` - fragment AssetsFields on Asset { - id - name - symbol - decimals - quantum - status - source { - ... on ERC20 { - contractAddress - } - } -} - `; export const AssetsDocument = gql` query Assets { assetsConnection { edges { node { - ...AssetsFields + ...AssetFields } } } } - ${AssetsFieldsFragmentDoc}`; + ${AssetFieldsFragmentDoc}`; /** * __useAssetsQuery__ diff --git a/libs/assets/src/lib/asset-data-provider.ts b/libs/assets/src/lib/asset-data-provider.ts new file mode 100644 index 000000000..196873b0d --- /dev/null +++ b/libs/assets/src/lib/asset-data-provider.ts @@ -0,0 +1,39 @@ +import { makeDataProvider, useDataProvider } from '@vegaprotocol/react-helpers'; +import { useMemo } from 'react'; + +import type { AssetQuery, AssetFieldsFragment } from './__generated___/Asset'; +import { AssetDocument } from './__generated___/Asset'; + +export type Asset = AssetFieldsFragment; + +const getData = (responseData: AssetQuery) => { + const foundAssets = responseData.assetsConnection?.edges + ?.filter((e) => Boolean(e?.node)) + .map((e) => e?.node as Asset); + if (foundAssets && foundAssets?.length > 0) return foundAssets[0]; + return null; +}; + +export const assetProvider = makeDataProvider< + AssetQuery, + Asset | null, + never, + never +>({ + query: AssetDocument, + getData, +}); + +export const useAssetDataProvider = (assetId: string) => { + const variables = useMemo( + () => ({ + assetId, + }), + [assetId] + ); + return useDataProvider({ + dataProvider: assetProvider, + variables, + skip: !assetId, + }); +}; diff --git a/libs/assets/src/lib/asset-details-dialog.spec.tsx b/libs/assets/src/lib/asset-details-dialog.spec.tsx index 29715a999..53b227804 100644 --- a/libs/assets/src/lib/asset-details-dialog.spec.tsx +++ b/libs/assets/src/lib/asset-details-dialog.spec.tsx @@ -1,267 +1,33 @@ import { MockedProvider } from '@apollo/react-testing'; import { render, screen } from '@testing-library/react'; +import { AssetStatus } from '@vegaprotocol/types'; import { AssetDetailsDialog } from './asset-details-dialog'; +import { AssetDetail, testId } from './asset-details-table'; import { AssetsDocument } from './__generated___/Assets'; +import { generateBuiltinAsset, generateERC20Asset } from './test-helpers'; const mockedData = { data: { assetsConnection: { edges: [ { - node: { - id: 'XYZalpha', - name: 'XYZ (α alpha)', - symbol: 'XYZalpha', - decimals: 5, - quantum: '1', - source: { - __typename: 'BuiltinAsset', - }, - __typename: 'Asset', - }, + node: generateERC20Asset(1, AssetStatus.STATUS_ENABLED), __typename: 'AssetEdge', }, { - node: { - id: 'XYZbeta', - name: 'XYZ (β beta)', - symbol: 'XYZbeta', - decimals: 5, - quantum: '1', - source: { - __typename: 'BuiltinAsset', - }, - __typename: 'Asset', - }, + node: generateBuiltinAsset(1, AssetStatus.STATUS_ENABLED), __typename: 'AssetEdge', }, { - node: { - id: 'XYZdelta', - name: 'XYZ (δ delta)', - symbol: 'XYZdelta', - decimals: 5, - quantum: '1', - source: { - __typename: 'BuiltinAsset', - }, - __typename: 'Asset', - }, + node: generateBuiltinAsset(2, AssetStatus.STATUS_PENDING_LISTING), __typename: 'AssetEdge', }, { - node: { - id: 'XYZepsilon', - name: 'XYZ (ε epsilon)', - symbol: 'XYZepsilon', - decimals: 5, - quantum: '1', - source: { - __typename: 'BuiltinAsset', - }, - __typename: 'Asset', - }, + node: generateBuiltinAsset(3, AssetStatus.STATUS_PROPOSED), __typename: 'AssetEdge', }, { - node: { - id: 'XYZgamma', - name: 'XYZ (γ gamma)', - symbol: 'XYZgamma', - decimals: 5, - quantum: '1', - source: { - __typename: 'BuiltinAsset', - }, - __typename: 'Asset', - }, - __typename: 'AssetEdge', - }, - { - node: { - id: '2282ffc06a557173d297739305cc69f6444cdbbb1089df7d9aef32bbfd735ba1', - name: 'Tim Token (Vega)', - symbol: 'TIM', - decimals: 18, - quantum: '1', - source: { - contractAddress: '0x920B2375BCAC8cCDfDEFD74426c55C48e0304e4F', - lifetimeLimit: '0', - withdrawThreshold: '0', - __typename: 'ERC20', - }, - __typename: 'Asset', - }, - __typename: 'AssetEdge', - }, - { - node: { - id: '449dbfb66e7a444c485b4fdc77ddc6bbf81abbf7c8e247ac299c25e9557b99cf', - name: 'Taker Reward Token (Vega)', - symbol: 'TAK', - decimals: 18, - quantum: '1', - source: { - contractAddress: '0xf700Ce952B6EA11c01b43e5579C6D63286ff8CF0', - lifetimeLimit: '0', - withdrawThreshold: '0', - __typename: 'ERC20', - }, - __typename: 'Asset', - }, - __typename: 'AssetEdge', - }, - { - node: { - id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c', - name: 'tBTC TEST', - symbol: 'tBTC', - decimals: 5, - quantum: '1', - source: { - contractAddress: '0xC912F059b4eCCEF6C969B2E0e2544A1A2581C094', - lifetimeLimit: '0', - withdrawThreshold: '0', - __typename: 'ERC20', - }, - __typename: 'Asset', - }, - __typename: 'AssetEdge', - }, - { - node: { - id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61', - name: 'tDAI TEST', - symbol: 'tDAI', - decimals: 5, - quantum: '1', - source: { - contractAddress: '0xF4A2bcC43D24D14C4189Ef45fCf681E870675333', - lifetimeLimit: '0', - withdrawThreshold: '0', - __typename: 'ERC20', - }, - __typename: 'Asset', - }, - __typename: 'AssetEdge', - }, - { - node: { - id: '8b52d4a3a4b0ffe733cddbc2b67be273816cfeb6ca4c8b339bac03ffba08e4e4', - name: 'tEURO TEST', - symbol: 'tEURO', - decimals: 5, - quantum: '1', - source: { - contractAddress: '0xD52b6C949E35A6E4C64b987B1B192A8608931a7b', - lifetimeLimit: '0', - withdrawThreshold: '0', - __typename: 'ERC20', - }, - __typename: 'Asset', - }, - __typename: 'AssetEdge', - }, - { - node: { - id: '98032ba34576f8012de9b822e1da3ed4b6223a4f4e05f573002d441ffb4bf314', - name: 'Liquidity Reward Token (Vega)', - symbol: 'LIQ', - decimals: 18, - quantum: '1', - source: { - contractAddress: '0xD2f21E37e78dD91b60FE3dD74A112e1a53b33057', - lifetimeLimit: '0', - withdrawThreshold: '0', - __typename: 'ERC20', - }, - __typename: 'Asset', - }, - __typename: 'AssetEdge', - }, - { - node: { - id: '993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede', - name: 'tUSDC TEST', - symbol: 'tUSDC', - decimals: 5, - quantum: '1', - source: { - contractAddress: '0x3773A5c7aFF77e014cBF067dd31801b4C6dc4136', - lifetimeLimit: '0', - withdrawThreshold: '0', - __typename: 'ERC20', - }, - __typename: 'Asset', - }, - __typename: 'AssetEdge', - }, - { - node: { - id: 'ba98cdeeec849a053e60cc03808e91e90d9d2e62425c76a590617b95ad41a066', - name: 'Steve Token (Vega)', - symbol: 'STE', - decimals: 18, - quantum: '1', - source: { - contractAddress: '0x5a16941cca2Db4AcdFC28Ac77a3e9652Fdf102e1', - lifetimeLimit: '0', - withdrawThreshold: '0', - __typename: 'ERC20', - }, - __typename: 'Asset', - }, - __typename: 'AssetEdge', - }, - { - node: { - id: 'ce3fb1ab0717f0adbce019d7aef53aacdbadefe2d30ad1647b55f134d4072c90', - name: 'Woz Token (Vega)', - symbol: 'WOZ', - decimals: 18, - quantum: '1', - source: { - contractAddress: '0x5E4b9aDA947130Fc320a144cd22bC1641e5c9d81', - lifetimeLimit: '0', - withdrawThreshold: '0', - __typename: 'ERC20', - }, - __typename: 'Asset', - }, - __typename: 'AssetEdge', - }, - { - node: { - id: 'ebcd94151ae1f0d39a4bde3b21a9c7ae81a80ea4352fb075a92e07608d9c953d', - name: 'Maker Reward Token (Vega)', - symbol: 'MAK', - decimals: 18, - quantum: '1', - source: { - contractAddress: '0x8ec701DA58394F5d2c8C2873D31039454D5845C1', - lifetimeLimit: '1000000000000', - withdrawThreshold: '0', - __typename: 'ERC20', - }, - __typename: 'Asset', - }, - __typename: 'AssetEdge', - }, - { - node: { - id: 'fc7fd956078fb1fc9db5c19b88f0874c4299b2a7639ad05a47a28c0aef291b55', - name: 'Vega (testnet)', - symbol: 'VEGA', - decimals: 18, - quantum: '1', - source: { - contractAddress: '0xDc335304979D378255015c33AbFf09B60c31EBAb', - lifetimeLimit: '0', - withdrawThreshold: '100000000', - __typename: 'ERC20', - }, - __typename: 'Asset', - }, + node: generateBuiltinAsset(4, AssetStatus.STATUS_REJECTED), __typename: 'AssetEdge', }, ], @@ -295,26 +61,122 @@ const WrappedAssetDetailsDialog = ({ ); describe('AssetDetailsDialog', () => { - it('should show no data message given unknown asset symbol', () => { + it('should show no data message given unknown asset symbol', async () => { render(); - expect(screen.getByText('No data')).toBeInTheDocument(); + expect((await screen.findByTestId('splash')).textContent).toContain( + 'No data' + ); }); - const cases = [ - ['tDAI', 'tDAI', 'tDAI TEST', '21,000,000'], - ['VEGA', 'VEGA', 'Vega (testnet)', '64,999,723,000,000,000,000,000,000'], - ['tUSDC', 'tUSDC', 'tUSDC TEST', '21,000,000'], + const cases: [string, { key: AssetDetail; value: string }[]][] = [ + [ + 'EA01', + [ + { key: AssetDetail.ID, value: 'E-01' }, + { key: AssetDetail.TYPE, value: 'ERC20' }, + { key: AssetDetail.NAME, value: 'ERC20 01' }, + { key: AssetDetail.SYMBOL, value: 'EA01' }, + { key: AssetDetail.DECIMALS, value: '3' }, + { key: AssetDetail.QUANTUM, value: '1' }, + { key: AssetDetail.STATUS, value: 'Enabled' }, + { key: AssetDetail.CONTRACT_ADDRESS, value: '0x123' }, + { key: AssetDetail.WITHDRAWAL_THRESHOLD, value: '0.050' }, + { key: AssetDetail.LIFETIME_LIMIT, value: '123,000.000' }, + { + key: AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE, + value: '0.001', + }, + { + key: AssetDetail.GLOBAL_REWARD_POOL_ACCOUNT_BALANCE, + value: '0.002', + }, + { key: AssetDetail.TAKER_FEE_REWARD_ACCOUNT_BALANCE, value: '0.003' }, + { key: AssetDetail.MAKER_FEE_REWARD_ACCOUNT_BALANCE, value: '0.004' }, + { key: AssetDetail.LP_FEE_REWARD_ACCOUNT_BALANCE, value: '0.005' }, + { + key: AssetDetail.MARKET_PROPOSER_REWARD_ACCOUNT_BALANCE, + value: '0.006', + }, + ], + ], + [ + 'BIA01', + [ + { key: AssetDetail.ID, value: 'B-01' }, + { key: AssetDetail.TYPE, value: 'Builtin asset' }, + { key: AssetDetail.NAME, value: 'Builtin 01' }, + { key: AssetDetail.SYMBOL, value: 'BIA01' }, + { key: AssetDetail.DECIMALS, value: '5' }, + { key: AssetDetail.QUANTUM, value: '1' }, + { key: AssetDetail.STATUS, value: 'Enabled' }, + { key: AssetDetail.MAX_FAUCET_AMOUNT_MINT, value: '50,000.00000' }, + { + key: AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE, + value: '0.00000', + }, + ], + ], + [ + 'BIA02', + [ + { key: AssetDetail.ID, value: 'B-02' }, + { key: AssetDetail.TYPE, value: 'Builtin asset' }, + { key: AssetDetail.NAME, value: 'Builtin 02' }, + { key: AssetDetail.SYMBOL, value: 'BIA02' }, + { key: AssetDetail.DECIMALS, value: '5' }, + { key: AssetDetail.QUANTUM, value: '1' }, + { key: AssetDetail.STATUS, value: 'Pending listing' }, + { key: AssetDetail.MAX_FAUCET_AMOUNT_MINT, value: '50,000.00000' }, + { + key: AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE, + value: '0.00000', + }, + ], + ], + [ + 'BIA03', + [ + { key: AssetDetail.ID, value: 'B-03' }, + { key: AssetDetail.TYPE, value: 'Builtin asset' }, + { key: AssetDetail.NAME, value: 'Builtin 03' }, + { key: AssetDetail.SYMBOL, value: 'BIA03' }, + { key: AssetDetail.DECIMALS, value: '5' }, + { key: AssetDetail.QUANTUM, value: '1' }, + { key: AssetDetail.STATUS, value: 'Proposed' }, + { key: AssetDetail.MAX_FAUCET_AMOUNT_MINT, value: '50,000.00000' }, + { + key: AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE, + value: '0.00000', + }, + ], + ], + [ + 'BIA04', + [ + { key: AssetDetail.ID, value: 'B-04' }, + { key: AssetDetail.TYPE, value: 'Builtin asset' }, + { key: AssetDetail.NAME, value: 'Builtin 04' }, + { key: AssetDetail.SYMBOL, value: 'BIA04' }, + { key: AssetDetail.DECIMALS, value: '5' }, + { key: AssetDetail.QUANTUM, value: '1' }, + { key: AssetDetail.STATUS, value: 'Rejected' }, + { key: AssetDetail.MAX_FAUCET_AMOUNT_MINT, value: '50,000.00000' }, + { + key: AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE, + value: '0.00000', + }, + ], + ], ]; it.each(cases)( 'should show correct data given %p symbol', - async (requestedSymbol, symbol, name, totalSupply) => { - render(); - expect((await screen.findByTestId('symbol_value')).textContent).toContain( - symbol - ); - expect((await screen.findByTestId('name_value')).textContent).toContain( - name - ); + async (symbol, details) => { + render(); + for (const detail of details) { + expect( + (await screen.findByTestId(testId(detail.key, 'value'))).textContent + ).toContain(detail.value); + } } ); }); diff --git a/libs/assets/src/lib/asset-details-dialog.tsx b/libs/assets/src/lib/asset-details-dialog.tsx index e5cc37d27..aa31645e8 100644 --- a/libs/assets/src/lib/asset-details-dialog.tsx +++ b/libs/assets/src/lib/asset-details-dialog.tsx @@ -1,21 +1,9 @@ -import { - addDecimalsFormatNumber, - t, - useDataProvider, -} from '@vegaprotocol/react-helpers'; -import type { Asset } from './assets-data-provider'; -import { - Button, - Dialog, - Icon, - KeyValueTable, - KeyValueTableRow, - Splash, - Tooltip, -} from '@vegaprotocol/ui-toolkit'; -import { assetsProvider } from './assets-data-provider'; -import type { Schema } from '@vegaprotocol/types'; +import { t } from '@vegaprotocol/react-helpers'; +import { useAssetsDataProvider } from './assets-data-provider'; +import { Button, Dialog, Icon, Splash } from '@vegaprotocol/ui-toolkit'; import create from 'zustand'; +import { AssetDetailsTable } from './asset-details-table'; +import type { Asset } from './asset-data-provider'; export type AssetDetailsDialogStore = { isOpen: boolean; @@ -43,117 +31,30 @@ export const useAssetDetailsDialogStore = create( }) ); -type AssetDetails = { - key: string; - label: string; - value: string; - tooltip: string; -}[]; - export interface AssetDetailsDialogProps { assetSymbol: string | Asset; trigger?: HTMLElement | null; open: boolean; onChange: (open: boolean) => void; } - export const AssetDetailsDialog = ({ assetSymbol, trigger, open, onChange, }: AssetDetailsDialogProps) => { - const { data } = useDataProvider({ dataProvider: assetsProvider }); + const { data } = useAssetsDataProvider(); + const symbol = typeof assetSymbol === 'string' ? assetSymbol : assetSymbol.symbol; const asset = data?.find((a) => a?.symbol === symbol); - let details: AssetDetails = []; - if (asset != null) { - details = [ - { - key: 'name', - label: t('Name'), - value: asset.name, - tooltip: '', // t('Name of the asset (e.g: Great British Pound)') - }, - { - key: 'symbol', - label: t('Symbol'), - value: asset.symbol, - tooltip: '', // t('Symbol of the asset (e.g: GBP)') - }, - { - key: 'decimals', - label: t('Decimals'), - value: asset.decimals.toString(), - tooltip: t('Number of decimal / precision handled by this asset'), - }, - { - key: 'quantum', - label: t('Quantum'), - value: asset.quantum, - tooltip: t('The minimum economically meaningful amount in the asset'), - }, - { - key: 'contractaddress', - label: t('Contract address'), - value: (asset.source as Schema.ERC20).contractAddress, - tooltip: t( - 'The address of the contract for the token, on the ethereum network' - ), - }, - { - key: 'withdrawalthreshold', - label: t('Withdrawal threshold'), - value: addDecimalsFormatNumber( - (asset.source as Schema.ERC20).withdrawThreshold, - asset.decimals - ), - tooltip: t( - 'The maximum allowed per withdraw note: this is a temporary measure for restricted mainnet' - ), - }, - { - key: 'lifetimelimit', - label: t('Lifetime limit'), - value: addDecimalsFormatNumber( - (asset.source as Schema.ERC20).lifetimeLimit, - asset.decimals - ), - tooltip: t( - 'The lifetime limits deposit per address note: this is a temporary measure for restricted mainnet' - ), - }, - ]; - } - const content = asset ? (
- - {details - .filter(({ value }) => value && value.length > 0) - .map(({ key, label, value, tooltip }) => ( - -
- {tooltip.length > 0 ? ( - - {label} - - ) : ( - {label} - )} -
-
{value}
-
- ))} -
+
) : ( -
+
{t('No data')}
); @@ -183,7 +84,7 @@ export const AssetDetailsDialog = ({ size="sm" onClick={() => onChange(false)} > - Close + {t('Close')}
diff --git a/libs/assets/src/lib/asset-details-table.spec.tsx b/libs/assets/src/lib/asset-details-table.spec.tsx new file mode 100644 index 000000000..b6646d428 --- /dev/null +++ b/libs/assets/src/lib/asset-details-table.spec.tsx @@ -0,0 +1,80 @@ +import { render, screen } from '@testing-library/react'; +import { AssetStatus } from '@vegaprotocol/types'; +import type { Asset } from './asset-data-provider'; +import { + AssetDetail, + AssetDetailsTable, + rows, + testId, +} from './asset-details-table'; +import { generateBuiltinAsset, generateERC20Asset } from './test-helpers'; + +describe('AssetDetailsTable', () => { + const cases: [string, Asset, { key: AssetDetail; value: string }[]][] = [ + [ + 'ERC20 asset', + generateERC20Asset(1, AssetStatus.STATUS_ENABLED), + [ + { key: AssetDetail.ID, value: 'E-01' }, + { key: AssetDetail.TYPE, value: 'ERC20' }, + { key: AssetDetail.NAME, value: 'ERC20 01' }, + { key: AssetDetail.SYMBOL, value: 'EA01' }, + { key: AssetDetail.DECIMALS, value: '3' }, + { key: AssetDetail.QUANTUM, value: '1' }, + { key: AssetDetail.STATUS, value: 'Enabled' }, + { key: AssetDetail.CONTRACT_ADDRESS, value: '0x123' }, + { key: AssetDetail.WITHDRAWAL_THRESHOLD, value: '0.050' }, + { key: AssetDetail.LIFETIME_LIMIT, value: '123,000.000' }, + { + key: AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE, + value: '0.001', + }, + { + key: AssetDetail.GLOBAL_REWARD_POOL_ACCOUNT_BALANCE, + value: '0.002', + }, + { key: AssetDetail.TAKER_FEE_REWARD_ACCOUNT_BALANCE, value: '0.003' }, + { key: AssetDetail.MAKER_FEE_REWARD_ACCOUNT_BALANCE, value: '0.004' }, + { key: AssetDetail.LP_FEE_REWARD_ACCOUNT_BALANCE, value: '0.005' }, + { + key: AssetDetail.MARKET_PROPOSER_REWARD_ACCOUNT_BALANCE, + value: '0.006', + }, + ], + ], + [ + 'Builtin asset', + generateBuiltinAsset(1, AssetStatus.STATUS_ENABLED), + [ + { key: AssetDetail.ID, value: 'B-01' }, + { key: AssetDetail.TYPE, value: 'Builtin asset' }, + { key: AssetDetail.NAME, value: 'Builtin 01' }, + { key: AssetDetail.SYMBOL, value: 'BIA01' }, + { key: AssetDetail.DECIMALS, value: '5' }, + { key: AssetDetail.QUANTUM, value: '1' }, + { key: AssetDetail.STATUS, value: 'Enabled' }, + { key: AssetDetail.MAX_FAUCET_AMOUNT_MINT, value: '50,000.00000' }, + { + key: AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE, + value: '0.00000', + }, + ], + ], + ]; + it.each(cases)( + "displays the available asset's data of %p with correct labels", + async (_type, asset, details) => { + render(); + for (const detail of details) { + expect( + await ( + await screen.findByTestId(testId(detail.key, 'label')) + ).textContent + ).toContain(rows.find((r) => r.key === detail.key)?.label); + expect( + (await screen.findByTestId(testId(detail.key, 'value'))).textContent + ).toContain(detail.value); + } + } + ); +}); diff --git a/libs/assets/src/lib/asset-details-table.tsx b/libs/assets/src/lib/asset-details-table.tsx new file mode 100644 index 000000000..b3c7a2b2d --- /dev/null +++ b/libs/assets/src/lib/asset-details-table.tsx @@ -0,0 +1,251 @@ +import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers'; +import type { Schema } from '@vegaprotocol/types'; +import type { KeyValueTableRowProps } from '@vegaprotocol/ui-toolkit'; +import { + KeyValueTable, + KeyValueTableRow, + Tooltip, +} from '@vegaprotocol/ui-toolkit'; +import type { Asset } from './asset-data-provider'; + +type Rows = { + key: AssetDetail; + label: string; + tooltip: string; + value: (asset: Asset) => string | null | undefined; + valueTooltip?: (asset: Asset) => string | null | undefined; +}[]; + +export enum AssetDetail { + ID, + TYPE, + NAME, + SYMBOL, + DECIMALS, + QUANTUM, + STATUS, + // erc20 details: + CONTRACT_ADDRESS, + WITHDRAWAL_THRESHOLD, + LIFETIME_LIMIT, + // builtin details: + MAX_FAUCET_AMOUNT_MINT, + // balances: + INFRASTRUCTURE_FEE_ACCOUNT_BALANCE, + GLOBAL_REWARD_POOL_ACCOUNT_BALANCE, + TAKER_FEE_REWARD_ACCOUNT_BALANCE, + MAKER_FEE_REWARD_ACCOUNT_BALANCE, + LP_FEE_REWARD_ACCOUNT_BALANCE, + MARKET_PROPOSER_REWARD_ACCOUNT_BALANCE, +} + +type Mapping = { [key in string]: { value: string; tooltip: string } }; + +const num = (asset: Asset, n: string | undefined | null) => { + if (typeof n === 'undefined' || n == null) return ''; + return addDecimalsFormatNumber(n, asset.decimals); +}; + +export const rows: Rows = [ + { + key: AssetDetail.ID, + label: t('ID'), + tooltip: '', + value: (asset) => asset.id, + }, + { + key: AssetDetail.TYPE, + label: t('Type'), + tooltip: '', + value: (asset) => AssetTypeMapping[asset.source.__typename].value, + valueTooltip: (asset) => AssetTypeMapping[asset.source.__typename].tooltip, + }, + { + key: AssetDetail.NAME, + label: t('Name'), + tooltip: '', + value: (asset) => asset.name, + }, + { + key: AssetDetail.SYMBOL, + label: t('Symbol'), + tooltip: '', + value: (asset) => asset.symbol, + }, + { + key: AssetDetail.DECIMALS, + label: t('Decimals'), + tooltip: t('Number of decimal / precision handled by this asset'), + value: (asset) => asset.decimals.toString(), + }, + { + key: AssetDetail.QUANTUM, + label: t('Quantum'), + tooltip: t('The minimum economically meaningful amount in the asset'), + value: (asset) => asset.quantum, + }, + { + key: AssetDetail.STATUS, + label: t('Status'), + tooltip: t('The status of the asset in the Vega network'), + value: (asset) => AssetStatusMapping[asset.status].value, + valueTooltip: (asset) => AssetStatusMapping[asset.status].tooltip, + }, + { + key: AssetDetail.CONTRACT_ADDRESS, + label: t('Contract address'), + tooltip: t( + 'The address of the contract for the token, on the ethereum network' + ), + value: (asset) => (asset.source as Schema.ERC20).contractAddress, + }, + { + key: AssetDetail.WITHDRAWAL_THRESHOLD, + label: t('Withdrawal threshold'), + tooltip: t( + 'The maximum allowed per withdraw note: this is a temporary measure for restricted mainnet' + ), + value: (asset) => + num(asset, (asset.source as Schema.ERC20).withdrawThreshold), + }, + { + key: AssetDetail.LIFETIME_LIMIT, + label: t('Lifetime limit'), + tooltip: t( + 'The lifetime limits deposit per address note: this is a temporary measure for restricted mainnet' + ), + value: (asset) => num(asset, (asset.source as Schema.ERC20).lifetimeLimit), + }, + { + key: AssetDetail.MAX_FAUCET_AMOUNT_MINT, + label: t('Max faucet amount'), + tooltip: t( + 'Maximum amount that can be requested by a party through the built-in asset faucet at a time' + ), + value: (asset) => + num(asset, (asset.source as Schema.BuiltinAsset).maxFaucetAmountMint), + }, + { + key: AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE, + label: t('Infrastructure fee account balance'), + tooltip: t('The infrastructure fee account for this asset'), + value: (asset) => num(asset, asset.infrastructureFeeAccount.balance), + }, + { + key: AssetDetail.GLOBAL_REWARD_POOL_ACCOUNT_BALANCE, + label: t('Global reward pool account balance'), + tooltip: t('The global reward pool account for this asset'), + value: (asset) => num(asset, asset.globalRewardPoolAccount?.balance), + }, + { + key: AssetDetail.TAKER_FEE_REWARD_ACCOUNT_BALANCE, + label: t('Taker fee reward account balance'), + tooltip: t('The taker fee reward account for this asset'), + value: (asset) => num(asset, asset.takerFeeRewardAccount?.balance), + }, + { + key: AssetDetail.MAKER_FEE_REWARD_ACCOUNT_BALANCE, + label: t('Maker fee reward account balance'), + tooltip: t('The maker fee reward account for this asset'), + value: (asset) => num(asset, asset.makerFeeRewardAccount?.balance), + }, + { + key: AssetDetail.LP_FEE_REWARD_ACCOUNT_BALANCE, + label: t('Liquidity provision fee reward account balance'), + tooltip: t('The liquidity provision reward account for this asset'), + value: (asset) => num(asset, asset.lpFeeRewardAccount?.balance), + }, + { + key: AssetDetail.MARKET_PROPOSER_REWARD_ACCOUNT_BALANCE, + label: t('Market proposer reward account balance'), + tooltip: t('The market proposer reward account for this asset'), + value: (asset) => num(asset, asset.marketProposerRewardAccount?.balance), + }, +]; + +const AssetStatusMapping: Mapping = { + STATUS_ENABLED: { + value: t('Enabled'), + tooltip: t('Asset can be used on the Vega network'), + }, + STATUS_PENDING_LISTING: { + value: t('Pending listing'), + tooltip: t('Asset is pending listing on the ethereum bridge'), + }, + STATUS_PROPOSED: { + value: t('Proposed'), + tooltip: t('Asset is proposed to be added to the network'), + }, + STATUS_REJECTED: { + value: t('Rejected'), + tooltip: t('Asset has been rejected'), + }, +}; + +const AssetTypeMapping: Mapping = { + BuiltinAsset: { + value: 'Builtin asset', + tooltip: t('A Vega builtin asset'), + }, + ERC20: { + value: 'ERC20', + tooltip: t('An asset originated from an Ethereum ERC20 Token'), + }, +}; + +export const testId = (detail: AssetDetail, field: 'label' | 'value') => + `${detail}_${field}`; + +export type AssetDetailsTableProps = { + asset: Asset; +} & Omit; +export const AssetDetailsTable = ({ + asset, + ...props +}: AssetDetailsTableProps) => { + const longStringModifiers = (key: AssetDetail, value: string) => + (value && key === AssetDetail.CONTRACT_ADDRESS) || key === AssetDetail.ID + ? { className: 'truncate', title: value } + : {}; + + const details = rows.map((r) => ({ + ...r, + value: r.value(asset), + valueTooltip: r.valueTooltip?.(asset), + })); + + return ( + + {details + .filter(({ value }) => value && value.length > 0) + .map(({ key, label, value, tooltip, valueTooltip }) => ( + +
+ {tooltip.length > 0 ? ( + + {label} + + ) : ( + {label} + )} +
+
+ {valueTooltip && valueTooltip?.length > 0 ? ( + + {value} + + ) : ( + value + )} +
+
+ ))} +
+ ); +}; diff --git a/libs/assets/src/lib/assets-data-provider.ts b/libs/assets/src/lib/assets-data-provider.ts index c811357c4..aaec1684d 100644 --- a/libs/assets/src/lib/assets-data-provider.ts +++ b/libs/assets/src/lib/assets-data-provider.ts @@ -1,19 +1,18 @@ import { makeDataProvider, makeDerivedDataProvider, + useDataProvider, } from '@vegaprotocol/react-helpers'; import { AssetsDocument } from './__generated___/Assets'; import { AssetStatus } from '@vegaprotocol/types'; -import type { - AssetsQuery, - AssetsFieldsFragment, -} from './__generated___/Assets'; - -export type Asset = AssetsFieldsFragment; +import type { AssetsQuery } from './__generated___/Assets'; +import type { Asset } from './asset-data-provider'; export interface ERC20AssetSource { __typename: 'ERC20'; contractAddress: string; + lifetimeLimit: string; + withdrawThreshold: string; } export interface BuiltinAssetSource { @@ -58,3 +57,8 @@ export const enabledAssetsProvider = makeDerivedDataProvider< (a) => a.status === AssetStatus.STATUS_ENABLED ) ); + +export const useAssetsDataProvider = () => + useDataProvider({ + dataProvider: assetsProvider, + }); diff --git a/libs/assets/src/lib/index.ts b/libs/assets/src/lib/index.ts index b8041e7cb..e8e130fec 100644 --- a/libs/assets/src/lib/index.ts +++ b/libs/assets/src/lib/index.ts @@ -1,3 +1,6 @@ +export * from './__generated___/Asset'; export * from './__generated___/Assets'; +export * from './asset-data-provider'; export * from './assets-data-provider'; export * from './asset-details-dialog'; +export * from './asset-details-table'; diff --git a/libs/assets/src/lib/test-helpers.ts b/libs/assets/src/lib/test-helpers.ts new file mode 100644 index 000000000..7f08b80b4 --- /dev/null +++ b/libs/assets/src/lib/test-helpers.ts @@ -0,0 +1,68 @@ +import type { AssetStatus } from '@vegaprotocol/types'; +import type { Asset } from './asset-data-provider'; + +export const generateERC20Asset = (i: number, status: AssetStatus): Asset => ({ + id: `E-0${i}`, + name: `ERC20 0${i}`, + symbol: `EA0${i}`, + decimals: 3, + quantum: '1', + source: { + contractAddress: '0x123', + lifetimeLimit: '123000000', + withdrawThreshold: '50', + __typename: 'ERC20', + }, + status: status, + infrastructureFeeAccount: { + balance: '1', + __typename: 'Account', + }, + globalRewardPoolAccount: { + balance: '2', + __typename: 'Account', + }, + takerFeeRewardAccount: { + balance: '3', + __typename: 'Account', + }, + makerFeeRewardAccount: { + balance: '4', + __typename: 'Account', + }, + lpFeeRewardAccount: { + balance: '5', + __typename: 'Account', + }, + marketProposerRewardAccount: { + balance: '6', + __typename: 'Account', + }, + __typename: 'Asset', +}); + +export const generateBuiltinAsset = ( + i: number, + status: AssetStatus +): Asset => ({ + id: `B-0${i}`, + name: `Builtin 0${i}`, + symbol: `BIA0${i}`, + decimals: 5, + quantum: '1', + source: { + maxFaucetAmountMint: '5000000000', + __typename: 'BuiltinAsset', + }, + status: status, + infrastructureFeeAccount: { + balance: '0', + __typename: 'Account', + }, + globalRewardPoolAccount: null, + takerFeeRewardAccount: null, + makerFeeRewardAccount: null, + lpFeeRewardAccount: null, + marketProposerRewardAccount: null, + __typename: 'Asset', +}); diff --git a/libs/market-info/src/components/market-info/info-market.tsx b/libs/market-info/src/components/market-info/info-market.tsx index a6a8ed24c..ce399f323 100644 --- a/libs/market-info/src/components/market-info/info-market.tsx +++ b/libs/market-info/src/components/market-info/info-market.tsx @@ -23,6 +23,7 @@ import { generatePath } from 'react-router-dom'; import { useEnvironment } from '@vegaprotocol/environment'; import { Link as UiToolkitLink } from '@vegaprotocol/ui-toolkit'; import Link from 'next/link'; +import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets'; const Links = { PROPOSAL_PAGE: ':tokenUrl/governance/:proposalId', @@ -92,6 +93,11 @@ export const Info = ({ market, onSelect }: InfoProps) => { const dayVolume = calcCandleVolume(market); const assetSymbol = market.tradableInstrument.instrument.product?.settlementAsset.symbol; + const assetId = useMemo( + () => market.tradableInstrument.instrument.product?.settlementAsset.id, + [market] + ); + const { data: asset } = useAssetDataProvider(assetId); const marketDataPanels = [ { title: t('Current fees'), @@ -201,18 +207,16 @@ export const Info = ({ market, onSelect }: InfoProps) => { }, { title: t('Settlement asset'), - content: ( - + ) : ( + {t('No data')} ), }, { diff --git a/libs/withdraws/src/lib/test-helpers.ts b/libs/withdraws/src/lib/test-helpers.ts index a5fc19964..51fe5db5b 100644 --- a/libs/withdraws/src/lib/test-helpers.ts +++ b/libs/withdraws/src/lib/test-helpers.ts @@ -11,7 +11,6 @@ import type { Withdrawals_party_withdrawalsConnection_edges_node } from './__gen export const generateAsset = (override?: PartialDeep) => { const defaultAsset: Asset = { - __typename: 'Asset', id: 'asset-id', symbol: 'asset-symbol', name: 'asset-name', @@ -19,9 +18,36 @@ export const generateAsset = (override?: PartialDeep) => { decimals: 5, status: AssetStatus.STATUS_ENABLED, source: { - __typename: 'ERC20', contractAddress: 'contract-address', + lifetimeLimit: '123000000', + withdrawThreshold: '50', + __typename: 'ERC20', }, + infrastructureFeeAccount: { + balance: '1', + __typename: 'Account', + }, + globalRewardPoolAccount: { + balance: '2', + __typename: 'Account', + }, + takerFeeRewardAccount: { + balance: '3', + __typename: 'Account', + }, + makerFeeRewardAccount: { + balance: '4', + __typename: 'Account', + }, + lpFeeRewardAccount: { + balance: '5', + __typename: 'Account', + }, + marketProposerRewardAccount: { + balance: '6', + __typename: 'Account', + }, + __typename: 'Asset', }; return merge(defaultAsset, override); }; diff --git a/libs/withdraws/src/lib/use-verify-withdrawal.ts b/libs/withdraws/src/lib/use-verify-withdrawal.ts index 55803216d..fe51e1f56 100644 --- a/libs/withdraws/src/lib/use-verify-withdrawal.ts +++ b/libs/withdraws/src/lib/use-verify-withdrawal.ts @@ -11,6 +11,7 @@ import type { Erc20ApprovalVariables, } from './__generated__/Erc20Approval'; import { useApolloClient } from '@apollo/client'; +import type { ERC20Asset } from '@vegaprotocol/assets'; export enum ApprovalStatus { Idle = 'Idle', @@ -80,7 +81,7 @@ export const useVerifyWithdrawal = () => { addDecimal(withdrawal.amount, withdrawal.asset.decimals) ); - const threshold = await getThreshold(withdrawal.asset); + const threshold = await getThreshold(withdrawal.asset as ERC20Asset); if (threshold && amount.isGreaterThan(threshold)) { const delaySecs = await getDelay();