diff --git a/mocks/tokens/tokenHolders.ts b/mocks/tokens/tokenHolders.ts index dc8743b2b8..582476e903 100644 --- a/mocks/tokens/tokenHolders.ts +++ b/mocks/tokens/tokenHolders.ts @@ -2,15 +2,40 @@ import type { TokenHolders } from 'types/api/token'; import { withName, withoutName } from 'mocks/address/address'; -export const tokenHolders: TokenHolders = { +import { tokenInfoERC1155a, tokenInfoERC20a } from './tokenInfo'; + +export const tokenHoldersERC20: TokenHolders = { + items: [ + { + address: withName, + token: tokenInfoERC20a, + value: '107014805905725000000', + }, + { + address: withoutName, + token: tokenInfoERC20a, + value: '207014805905725000000', + }, + ], + next_page_params: { + value: '50', + items_count: 50, + }, +}; + +export const tokenHoldersERC1155: TokenHolders = { items: [ { address: withName, + token: tokenInfoERC1155a, value: '107014805905725000000', + token_id: '12345', }, { address: withoutName, + token: tokenInfoERC1155a, value: '207014805905725000000', + token_id: '12345', }, ], next_page_params: { diff --git a/mocks/tokens/tokenInfo.ts b/mocks/tokens/tokenInfo.ts index c2e5e520aa..be4a6b564c 100644 --- a/mocks/tokens/tokenInfo.ts +++ b/mocks/tokens/tokenInfo.ts @@ -18,7 +18,7 @@ export const tokenCounters: TokenCounters = { transfers_count: '88282281', }; -export const tokenInfoERC20a: TokenInfo = { +export const tokenInfoERC20a: TokenInfo<'ERC-20'> = { address: '0xb2a90505dc6680a7a695f7975d0d32EeF610f456', circulating_market_cap: '117268489.23970924', decimals: '18', @@ -31,7 +31,7 @@ export const tokenInfoERC20a: TokenInfo = { icon_url: 'https://example.com/token-icon.png', }; -export const tokenInfoERC20b: TokenInfo = { +export const tokenInfoERC20b: TokenInfo<'ERC-20'> = { address: '0xc1116c98ba622a6218433fF90a2E40DEa482d7A7', circulating_market_cap: '115060192.36105014', decimals: '6', @@ -44,7 +44,7 @@ export const tokenInfoERC20b: TokenInfo = { icon_url: null, }; -export const tokenInfoERC20c: TokenInfo = { +export const tokenInfoERC20c: TokenInfo<'ERC-20'> = { address: '0xc1116c98ba622a6218433fF90a2E40DEa482d7A7', circulating_market_cap: null, decimals: '18', @@ -57,7 +57,7 @@ export const tokenInfoERC20c: TokenInfo = { icon_url: null, }; -export const tokenInfoERC20d: TokenInfo = { +export const tokenInfoERC20d: TokenInfo<'ERC-20'> = { address: '0xCc7bb2D219A0FC08033E130629C2B854b7bA9195', circulating_market_cap: null, decimals: '18', @@ -70,7 +70,7 @@ export const tokenInfoERC20d: TokenInfo = { icon_url: null, }; -export const tokenInfoERC20LongSymbol: TokenInfo = { +export const tokenInfoERC20LongSymbol: TokenInfo<'ERC-20'> = { address: '0xCc7bb2D219A0FC08033E130629C2B854b7bA9195', circulating_market_cap: '112855875.75888918', decimals: '18', @@ -83,7 +83,7 @@ export const tokenInfoERC20LongSymbol: TokenInfo = { icon_url: null, }; -export const tokenInfoERC721a: TokenInfo = { +export const tokenInfoERC721a: TokenInfo<'ERC-721'> = { address: '0xDe7cAc71E072FCBd4453E5FB3558C2684d1F88A0', circulating_market_cap: null, decimals: null, @@ -96,7 +96,7 @@ export const tokenInfoERC721a: TokenInfo = { icon_url: null, }; -export const tokenInfoERC721b: TokenInfo = { +export const tokenInfoERC721b: TokenInfo<'ERC-721'> = { address: '0xA8d5C7beEA8C9bB57f5fBa35fB638BF45550b11F', circulating_market_cap: null, decimals: null, @@ -109,7 +109,7 @@ export const tokenInfoERC721b: TokenInfo = { icon_url: null, }; -export const tokenInfoERC721c: TokenInfo = { +export const tokenInfoERC721c: TokenInfo<'ERC-721'> = { address: '0x47646F1d7dc4Dd2Db5a41D092e2Cf966e27A4992', circulating_market_cap: null, decimals: null, @@ -122,7 +122,7 @@ export const tokenInfoERC721c: TokenInfo = { icon_url: null, }; -export const tokenInfoERC721LongSymbol: TokenInfo = { +export const tokenInfoERC721LongSymbol: TokenInfo<'ERC-721'> = { address: '0x47646F1d7dc4Dd2Db5a41D092e2Cf966e27A4992', circulating_market_cap: null, decimals: null, @@ -135,7 +135,7 @@ export const tokenInfoERC721LongSymbol: TokenInfo = { icon_url: null, }; -export const tokenInfoERC1155a: TokenInfo = { +export const tokenInfoERC1155a: TokenInfo<'ERC-1155'> = { address: '0x4b333DEd10c7ca855EA2C8D4D90A0a8b73788c8e', circulating_market_cap: null, decimals: null, @@ -148,7 +148,7 @@ export const tokenInfoERC1155a: TokenInfo = { icon_url: null, }; -export const tokenInfoERC1155b: TokenInfo = { +export const tokenInfoERC1155b: TokenInfo<'ERC-1155'> = { address: '0xf4b71b179132ad457f6bcae2a55efa9e4b26eefc', circulating_market_cap: null, decimals: null, @@ -161,7 +161,7 @@ export const tokenInfoERC1155b: TokenInfo = { icon_url: null, }; -export const tokenInfoERC1155WithoutName: TokenInfo = { +export const tokenInfoERC1155WithoutName: TokenInfo<'ERC-1155'> = { address: '0x4b333DEd10c7ca855EA2C8D4D90A0a8b73788c8e', circulating_market_cap: null, decimals: null, diff --git a/stubs/token.ts b/stubs/token.ts index 9ae80fc903..30b8b487bf 100644 --- a/stubs/token.ts +++ b/stubs/token.ts @@ -36,8 +36,16 @@ export const TOKEN_COUNTERS: TokenCounters = { transfers_count: '123456', }; -export const TOKEN_HOLDER: TokenHolder = { +export const TOKEN_HOLDER_ERC_20: TokenHolder = { address: ADDRESS_PARAMS, + token: TOKEN_INFO_ERC_20, + value: '1021378038331138520', +}; + +export const TOKEN_HOLDER_ERC_1155: TokenHolder = { + address: ADDRESS_PARAMS, + token: TOKEN_INFO_ERC_1155, + token_id: '12345', value: '1021378038331138520', }; diff --git a/types/api/token.ts b/types/api/token.ts index 6617ab59db..a103f890a3 100644 --- a/types/api/token.ts +++ b/types/api/token.ts @@ -26,11 +26,22 @@ export interface TokenHolders { next_page_params: TokenHoldersPagination | null; } -export type TokenHolder = { +export type TokenHolder = TokenHolderERC20ERC721 | TokenHolderERC1155; + +export type TokenHolderBase = { address: AddressParam; value: string; } +export type TokenHolderERC20ERC721 = TokenHolderBase & { + token: TokenInfo<'ERC-20'> | TokenInfo<'ERC-721'>; +} + +export type TokenHolderERC1155 = TokenHolderBase & { + token: TokenInfo<'ERC-1155'>; + token_id: string; +} + export type TokenHoldersPagination = { items_count: number; value: string; diff --git a/ui/pages/Token.tsx b/ui/pages/Token.tsx index 5502296d4a..0bd0be5dbc 100644 --- a/ui/pages/Token.tsx +++ b/ui/pages/Token.tsx @@ -157,7 +157,8 @@ const TokenPageContent = () => { scrollRef, options: { enabled: Boolean(hashString && tab === 'holders' && hasData), - placeholderData: generateListStub<'token_holders'>(tokenStubs.TOKEN_HOLDER, 50, { next_page_params: null }), + placeholderData: generateListStub<'token_holders'>( + tokenQuery.data?.type === 'ERC-1155' ? tokenStubs.TOKEN_HOLDER_ERC_1155 : tokenStubs.TOKEN_HOLDER_ERC_20, 50, { next_page_params: null }), }, }); diff --git a/ui/pages/TokenInstance.tsx b/ui/pages/TokenInstance.tsx index cef84776f6..7f21932ca3 100644 --- a/ui/pages/TokenInstance.tsx +++ b/ui/pages/TokenInstance.tsx @@ -71,7 +71,8 @@ const TokenInstanceContent = () => { scrollRef, options: { enabled: Boolean(hash && tab === 'holders' && shouldFetchHolders), - placeholderData: generateListStub<'token_instance_holders'>(tokenStubs.TOKEN_HOLDER, 10, { next_page_params: null }), + placeholderData: generateListStub<'token_instance_holders'>( + tokenInstanceQuery.data?.token.type === 'ERC-1155' ? tokenStubs.TOKEN_HOLDER_ERC_1155 : tokenStubs.TOKEN_HOLDER_ERC_20, 10, { next_page_params: null }), }, }); diff --git a/ui/token/TokenHolders/TokenHoldersList.pw.tsx b/ui/token/TokenHolders/TokenHoldersList.pw.tsx index ec8cb334ae..2bd3fd1267 100644 --- a/ui/token/TokenHolders/TokenHoldersList.pw.tsx +++ b/ui/token/TokenHolders/TokenHoldersList.pw.tsx @@ -1,18 +1,28 @@ import { test, expect, devices } from '@playwright/experimental-ct-react'; import React from 'react'; -import { tokenHolders } from 'mocks/tokens/tokenHolders'; -import { tokenInfo } from 'mocks/tokens/tokenInfo'; +import { tokenHoldersERC20, tokenHoldersERC1155 } from 'mocks/tokens/tokenHolders'; +import { tokenInfo, tokenInfoERC1155a } from 'mocks/tokens/tokenInfo'; import TestApp from 'playwright/TestApp'; import TokenHoldersList from './TokenHoldersList'; test.use({ viewport: devices['iPhone 13 Pro'].viewport }); -test('base view', async({ mount }) => { +test('base view without IDs', async({ mount }) => { const component = await mount( - + + , + ); + + await expect(component).toHaveScreenshot(); +}); + +test('base view with IDs', async({ mount }) => { + const component = await mount( + + , ); diff --git a/ui/token/TokenHolders/TokenHoldersListItem.tsx b/ui/token/TokenHolders/TokenHoldersListItem.tsx index 27c641d40a..cfe3b31a76 100644 --- a/ui/token/TokenHolders/TokenHoldersListItem.tsx +++ b/ui/token/TokenHolders/TokenHoldersListItem.tsx @@ -1,11 +1,11 @@ -import { Box, Flex, Skeleton } from '@chakra-ui/react'; +import { Skeleton } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; import type { TokenHolder, TokenInfo } from 'types/api/token'; import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; +import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import Utilization from 'ui/shared/Utilization/Utilization'; interface Props { @@ -18,30 +18,50 @@ const TokenHoldersListItem = ({ holder, token, isLoading }: Props) => { const quantity = BigNumber(holder.value).div(BigNumber(10 ** Number(token.decimals))).dp(6).toFormat(); return ( - - - - - - { quantity } - - { token.total_supply && ( + + Address + + + + + { token.type === 'ERC-1155' && 'token_id' in holder && ( + <> + ID# + + + { holder.token_id } + + + + ) } + + Quantity + + + { quantity } + + + + { token.total_supply && ( + <> + Percentage + - ) } - - - + + + ) } + + ); }; diff --git a/ui/token/TokenHolders/TokenHoldersTable.pw.tsx b/ui/token/TokenHolders/TokenHoldersTable.pw.tsx index 1ead148cbe..1f81e97af5 100644 --- a/ui/token/TokenHolders/TokenHoldersTable.pw.tsx +++ b/ui/token/TokenHolders/TokenHoldersTable.pw.tsx @@ -2,17 +2,28 @@ import { Box } from '@chakra-ui/react'; import { test, expect } from '@playwright/experimental-ct-react'; import React from 'react'; -import { tokenHolders } from 'mocks/tokens/tokenHolders'; -import { tokenInfo } from 'mocks/tokens/tokenInfo'; +import { tokenHoldersERC20, tokenHoldersERC1155 } from 'mocks/tokens/tokenHolders'; +import { tokenInfo, tokenInfoERC1155a } from 'mocks/tokens/tokenInfo'; import TestApp from 'playwright/TestApp'; import TokenHoldersTable from './TokenHoldersTable'; -test('base view', async({ mount }) => { +test('base view without IDs', async({ mount }) => { const component = await mount( - + + , + ); + + await expect(component).toHaveScreenshot(); +}); + +test('base view with IDs', async({ mount }) => { + const component = await mount( + + + , ); diff --git a/ui/token/TokenHolders/TokenHoldersTable.tsx b/ui/token/TokenHolders/TokenHoldersTable.tsx index 3b35b0dd32..e44e981bb6 100644 --- a/ui/token/TokenHolders/TokenHoldersTable.tsx +++ b/ui/token/TokenHolders/TokenHoldersTable.tsx @@ -19,6 +19,7 @@ const TokenHoldersTable = ({ data, token, top, isLoading }: Props) => { Holder + { token.type === 'ERC-1155' && ID# } Quantity { token.total_supply && Percentage } diff --git a/ui/token/TokenHolders/TokenHoldersTableItem.tsx b/ui/token/TokenHolders/TokenHoldersTableItem.tsx index a9747e61f9..8bfc187e0a 100644 --- a/ui/token/TokenHolders/TokenHoldersTableItem.tsx +++ b/ui/token/TokenHolders/TokenHoldersTableItem.tsx @@ -26,6 +26,13 @@ const TokenTransferTableItem = ({ holder, token, isLoading }: Props) => { fontWeight="700" /> + { token.type === 'ERC-1155' && 'token_id' in holder && ( + + + { 'token_id' in holder && holder.token_id } + + + ) } { quantity } diff --git a/ui/token/TokenHolders/__screenshots__/TokenHoldersList.pw.tsx_default_base-view-1.png b/ui/token/TokenHolders/__screenshots__/TokenHoldersList.pw.tsx_default_base-view-1.png deleted file mode 100644 index 5e28f85a5e..0000000000 Binary files a/ui/token/TokenHolders/__screenshots__/TokenHoldersList.pw.tsx_default_base-view-1.png and /dev/null differ diff --git a/ui/token/TokenHolders/__screenshots__/TokenHoldersList.pw.tsx_default_base-view-with-IDs-1.png b/ui/token/TokenHolders/__screenshots__/TokenHoldersList.pw.tsx_default_base-view-with-IDs-1.png new file mode 100644 index 0000000000..ba1c5737ec Binary files /dev/null and b/ui/token/TokenHolders/__screenshots__/TokenHoldersList.pw.tsx_default_base-view-with-IDs-1.png differ diff --git a/ui/token/TokenHolders/__screenshots__/TokenHoldersList.pw.tsx_default_base-view-without-IDs-1.png b/ui/token/TokenHolders/__screenshots__/TokenHoldersList.pw.tsx_default_base-view-without-IDs-1.png new file mode 100644 index 0000000000..d0008aab2a Binary files /dev/null and b/ui/token/TokenHolders/__screenshots__/TokenHoldersList.pw.tsx_default_base-view-without-IDs-1.png differ diff --git a/ui/token/TokenHolders/__screenshots__/TokenHoldersTable.pw.tsx_default_base-view-with-IDs-1.png b/ui/token/TokenHolders/__screenshots__/TokenHoldersTable.pw.tsx_default_base-view-with-IDs-1.png new file mode 100644 index 0000000000..3286a6d30a Binary files /dev/null and b/ui/token/TokenHolders/__screenshots__/TokenHoldersTable.pw.tsx_default_base-view-with-IDs-1.png differ diff --git a/ui/token/TokenHolders/__screenshots__/TokenHoldersTable.pw.tsx_default_base-view-1.png b/ui/token/TokenHolders/__screenshots__/TokenHoldersTable.pw.tsx_default_base-view-without-IDs-1.png similarity index 100% rename from ui/token/TokenHolders/__screenshots__/TokenHoldersTable.pw.tsx_default_base-view-1.png rename to ui/token/TokenHolders/__screenshots__/TokenHoldersTable.pw.tsx_default_base-view-without-IDs-1.png