Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/socket/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ SocketMessage.AddressTokenBalancesErc20 |
SocketMessage.AddressTokenBalancesErc721 |
SocketMessage.AddressTokenBalancesErc1155 |
SocketMessage.AddressTokenBalancesErc404 |
SocketMessage.AddressTokenBalancesErc7984 |
SocketMessage.AddressCoinBalance |
SocketMessage.AddressTxs |
SocketMessage.AddressTxsPending |
Expand Down Expand Up @@ -74,6 +75,7 @@ export namespace SocketMessage {
export type AddressTokenBalancesErc721 = SocketMessageParamsGeneric<'updated_token_balances_erc_721', AddressTokensBalancesSocketMessage>;
export type AddressTokenBalancesErc1155 = SocketMessageParamsGeneric<'updated_token_balances_erc_1155', AddressTokensBalancesSocketMessage>;
export type AddressTokenBalancesErc404 = SocketMessageParamsGeneric<'updated_token_balances_erc_404', AddressTokensBalancesSocketMessage>;
export type AddressTokenBalancesErc7984 = SocketMessageParamsGeneric<'updated_token_balances_erc_7984', AddressTokensBalancesSocketMessage>;
export type AddressCoinBalance = SocketMessageParamsGeneric<'coin_balance', { coin_balance: AddressCoinBalanceHistoryItem }>;
export type AddressTxs = SocketMessageParamsGeneric<'transaction', { transactions: Array<Transaction> }>;
export type AddressTxsPending = SocketMessageParamsGeneric<'pending_transaction', { transactions: Array<Transaction> }>;
Expand Down
3 changes: 2 additions & 1 deletion lib/token/tokenTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ export const NFT_TOKEN_TYPES: Record<NFTTokenType, string > = {

export const TOKEN_TYPES: Record<TokenType, string > = {
'ERC-20': `${ tokenStandardName }-20`,
'ERC-7984': `${ tokenStandardName }-7984`,
...NFT_TOKEN_TYPES,
};

export const NFT_TOKEN_TYPE_IDS: Array<NFTTokenType> = [ 'ERC-721', 'ERC-1155', 'ERC-404' ];
export const TOKEN_TYPE_IDS: Array<TokenType> = [ 'ERC-20', ...NFT_TOKEN_TYPE_IDS ];
export const TOKEN_TYPE_IDS: Array<TokenType> = [ 'ERC-20', 'ERC-7984', ...NFT_TOKEN_TYPE_IDS ];

export function getTokenTypeName(typeId: TokenType) {
return TOKEN_TYPES[typeId];
Expand Down
2 changes: 1 addition & 1 deletion types/api/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { TokenInfoApplication } from './account';
import type { AddressParam } from './addressParams';

export type NFTTokenType = 'ERC-721' | 'ERC-1155' | 'ERC-404';
export type TokenType = 'ERC-20' | NFTTokenType;
export type TokenType = 'ERC-20' | NFTTokenType | 'ERC-7984';

export type TokenReputation = 'ok' | 'scam';

Expand Down
4 changes: 4 additions & 0 deletions types/api/tokenTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ export type TokenTransfer = (
{
token: TokenInfo<'ERC-404'> | null;
total: Erc404TotalPayload | null;
} |
{
token: TokenInfo<'ERC-7984'> | null;
total: Erc20TotalPayload | null;
}
) & TokenTransferBase;

Expand Down
18 changes: 17 additions & 1 deletion ui/address/AddressTokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import AddressNftDisplayTypeRadio from './tokens/AddressNftDisplayTypeRadio';
import AddressNFTs from './tokens/AddressNFTs';
import AddressNftTypeFilter from './tokens/AddressNftTypeFilter';
import ERC20Tokens from './tokens/ERC20Tokens';
import ERC7984Tokens from './tokens/ERC7984Tokens';
import TokenBalances from './tokens/TokenBalances';
import useAddressNftQuery from './tokens/useAddressNftQuery';

Expand Down Expand Up @@ -65,6 +66,18 @@ const AddressTokens = ({ shouldRender = true, isQueryEnabled = true }: Props) =>
addressHash: hash,
});

const erc7984Query = useQueryWithPages({
resourceName: 'general:address_tokens',
pathParams: { hash },
filters: { type: 'ERC-7984' },
scrollRef,
options: {
enabled: isQueryEnabled && tab === 'tokens_erc7984',
refetchOnMount: false,
placeholderData: generateListStub<'general:address_tokens'>(ADDRESS_TOKEN_BALANCE_ERC_20, 10, { next_page_params: null }),
},
});

if (!isMounted || !shouldRender) {
return null;
}
Expand All @@ -91,12 +104,15 @@ const AddressTokens = ({ shouldRender = true, isQueryEnabled = true }: Props) =>
<AddressNFTs tokensQuery={ nftsQuery } tokenTypes={ nftTokenTypes } onTokenTypesChange={ onTokenTypesChange }/> :
<AddressCollections collectionsQuery={ collectionsQuery } address={ hash } tokenTypes={ nftTokenTypes } onTokenTypesChange={ onTokenTypesChange }/>,
},
{ id: 'tokens_erc7984', title: `${ config.chain.tokenStandard }-7984`, component: <ERC7984Tokens tokensQuery={ erc7984Query }/> },
];

let pagination: PaginationParams | undefined;

if (tab === 'tokens_nfts') {
pagination = nftDisplayType === 'list' ? nftsQuery.pagination : collectionsQuery.pagination;
} else if (tab === 'tokens_erc7984') {
pagination = erc7984Query.pagination;
} else {
pagination = erc20Query.pagination;
}
Expand All @@ -105,7 +121,7 @@ const AddressTokens = ({ shouldRender = true, isQueryEnabled = true }: Props) =>
(!nftsQuery.isPlaceholderData && nftsQuery.data?.items.length) ||
(!collectionsQuery.isPlaceholderData && collectionsQuery.data?.items.length);

const isNftTab = tab !== 'tokens' && tab !== 'tokens_erc20';
const isNftTab = tab !== 'tokens' && tab !== 'tokens_erc20' && tab !== 'tokens_erc7984';

const rightSlot = (
<>
Expand Down
10 changes: 10 additions & 0 deletions ui/address/tokenSelect/TokenSelectItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ const TokenSelectItem = ({ data }: Props) => {
</>
);
}
case 'ERC-7984': {
const text = `••••• ${ data.token.symbol || '' }`;

return (
<>
<TruncatedValue value={ text }/>
{ data.token.exchange_rate && <chakra.span ml={ 2 }>@{ Number(data.token.exchange_rate).toLocaleString() }</chakra.span> }
</>
);
}
}
})();

Expand Down
55 changes: 55 additions & 0 deletions ui/address/tokens/ERC7984Tokens.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Box } from '@chakra-ui/react';
import React from 'react';

import useIsMobile from 'lib/hooks/useIsMobile';
import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination';
import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';

import ERC7984TokensListItem from './ERC7984TokensListItem';
import ERC7984TokensTable from './ERC7984TokensTable';

type Props = {
tokensQuery: QueryWithPagesResult<'general:address_tokens'>;
};

const ERC7984Tokens = ({ tokensQuery }: Props) => {
const isMobile = useIsMobile();

const { isError, isPlaceholderData, data, pagination } = tokensQuery;

const actionBar = isMobile && pagination.isVisible && (
<ActionBar mt={ -6 }>
<Pagination ml="auto" { ...pagination }/>
</ActionBar>
);

const content = data?.items ? (
<>
<Box hideBelow="lg"><ERC7984TokensTable data={ data.items } top={ pagination.isVisible ? 72 : 0 } isLoading={ isPlaceholderData }/></Box>
<Box hideFrom="lg">{ data.items.map((item, index) => (
<ERC7984TokensListItem
key={ item.token.address_hash + (isPlaceholderData ? index : '') }
{ ...item }
isLoading={ isPlaceholderData }
/>
)) }
</Box>
</>
) : null;

return (
<DataListDisplay
isError={ isError }
itemsNum={ data?.items.length }
emptyText="There are no tokens of selected type."
actionBar={ actionBar }
>
{ content }
</DataListDisplay>
);

};

export default ERC7984Tokens;
68 changes: 68 additions & 0 deletions ui/address/tokens/ERC7984TokensListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Flex, HStack } from '@chakra-ui/react';
import React from 'react';

import type { AddressTokenBalance } from 'types/api/address';

import config from 'configs/app';
import { Skeleton } from 'toolkit/chakra/skeleton';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import NativeTokenTag from 'ui/shared/celo/NativeTokenTag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';

const celoFeature = config.features.celo;

type Props = AddressTokenBalance & { isLoading: boolean };

const ERC7984TokensListItem = ({ token, isLoading }: Props) => {

const isNativeToken = celoFeature.isEnabled && token.address_hash.toLowerCase() === celoFeature.nativeTokenAddress?.toLowerCase();

return (
<ListItemMobile rowGap={ 2 }>
<Flex alignItems="center" width="100%" columnGap={ 2 }>
<TokenEntity
token={ token }
isLoading={ isLoading }
noCopy
jointSymbol
fontWeight="700"
width="auto"
/>
{ isNativeToken && <NativeTokenTag/> }
</Flex>
<Flex alignItems="center" pl={ 8 }>
<AddressEntity
address={{ hash: token.address_hash }}
isLoading={ isLoading }
truncation="constant"
noIcon
/>
<AddressAddToWallet token={ token } ml={ 2 } isLoading={ isLoading }/>
</Flex>
{ token.exchange_rate !== undefined && token.exchange_rate !== null && (
<HStack gap={ 3 }>
<Skeleton loading={ isLoading } fontSize="sm" fontWeight={ 500 }>Price</Skeleton>
<Skeleton loading={ isLoading } fontSize="sm" color="text.secondary">
<span>{ `$${ Number(token.exchange_rate).toLocaleString() }` }</span>
</Skeleton>
</HStack>
) }
<HStack gap={ 3 } alignItems="baseline">
<Skeleton loading={ isLoading } fontSize="sm" fontWeight={ 500 }>Quantity</Skeleton>
<Skeleton loading={ isLoading } fontSize="sm" color="text.secondary" whiteSpace="pre-wrap" wordBreak="break-word">
<span>•••••</span>
</Skeleton>
</HStack>
<HStack gap={ 3 } alignItems="baseline">
<Skeleton loading={ isLoading } fontSize="sm" fontWeight={ 500 }>Value</Skeleton>
<Skeleton loading={ isLoading } fontSize="sm" color="text.secondary" whiteSpace="pre-wrap" wordBreak="break-word">
<span>•••••</span>
</Skeleton>
</HStack>
</ListItemMobile>
);
};

export default ERC7984TokensListItem;
36 changes: 36 additions & 0 deletions ui/address/tokens/ERC7984TokensTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';

import type { AddressTokenBalance } from 'types/api/address';

import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';

import ERC7984TokensTableItem from './ERC7984TokensTableItem';

interface Props {
data: Array<AddressTokenBalance>;
top: number;
isLoading: boolean;
}

const ERC7984TokensTable = ({ data, top, isLoading }: Props) => {
return (
<TableRoot>
<TableHeaderSticky top={ top }>
<TableRow>
<TableColumnHeader width="30%">Asset</TableColumnHeader>
<TableColumnHeader width="30%">Contract address</TableColumnHeader>
<TableColumnHeader width="10%" isNumeric>Price</TableColumnHeader>
<TableColumnHeader width="15%" isNumeric>Quantity</TableColumnHeader>
<TableColumnHeader width="15%" isNumeric>Value</TableColumnHeader>
</TableRow>
</TableHeaderSticky>
<TableBody>
{ data.map((item, index) => (
<ERC7984TokensTableItem key={ item.token.address_hash + (isLoading ? index : '') } { ...item } isLoading={ isLoading }/>
)) }
</TableBody>
</TableRoot>
);
};

export default ERC7984TokensTable;
70 changes: 70 additions & 0 deletions ui/address/tokens/ERC7984TokensTableItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Flex, HStack } from '@chakra-ui/react';
import React from 'react';

import type { AddressTokenBalance } from 'types/api/address';

import config from 'configs/app';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { TableCell, TableRow } from 'toolkit/chakra/table';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import NativeTokenTag from 'ui/shared/celo/NativeTokenTag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';

type Props = AddressTokenBalance & { isLoading: boolean };

const celoFeature = config.features.celo;

const ERC7984TokensTableItem = ({
token,
isLoading,
}: Props) => {

const isNativeToken = celoFeature.isEnabled && token.address_hash.toLowerCase() === celoFeature.nativeTokenAddress?.toLowerCase();

return (
<TableRow role="group" >
<TableCell verticalAlign="middle">
<HStack gap={ 2 }>
<TokenEntity
token={ token }
isLoading={ isLoading }
noCopy
jointSymbol
fontWeight="700"
width="auto"
/>
{ isNativeToken && <NativeTokenTag/> }
</HStack>
</TableCell>
<TableCell verticalAlign="middle">
<Flex alignItems="center" width="150px" justifyContent="space-between">
<AddressEntity
address={{ hash: token.address_hash }}
isLoading={ isLoading }
truncation="constant"
noIcon
/>
<AddressAddToWallet token={ token } ml={ 4 } isLoading={ isLoading } opacity="0" _groupHover={{ opacity: 1 }}/>
</Flex>
</TableCell>
<TableCell isNumeric verticalAlign="middle">
<Skeleton loading={ isLoading } display="inline-block" color={ isNativeToken ? 'text.secondary' : undefined }>
{ token.exchange_rate && `$${ Number(token.exchange_rate).toLocaleString() }` }
</Skeleton>
</TableCell>
<TableCell isNumeric verticalAlign="middle">
<Skeleton loading={ isLoading } display="inline" color={ isNativeToken ? 'text.secondary' : undefined }>
•••••
</Skeleton>
</TableCell>
<TableCell isNumeric verticalAlign="middle">
<Skeleton loading={ isLoading } display="inline" color={ isNativeToken ? 'text.secondary' : undefined }>
•••••
</Skeleton>
</TableCell>
</TableRow>
);
};

export default React.memo(ERC7984TokensTableItem);
5 changes: 4 additions & 1 deletion ui/address/utils/tokenUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export interface TokenSelectDataItem {

type TokenGroup = [string, TokenSelectDataItem];

const TOKEN_GROUPS_ORDER: Array<TokenType> = [ 'ERC-20', 'ERC-721', 'ERC-1155', 'ERC-404' ];
const TOKEN_GROUPS_ORDER: Array<TokenType> = [ 'ERC-20', 'ERC-721', 'ERC-1155', 'ERC-404', 'ERC-7984' ];

export const sortTokenGroups = (groupA: TokenGroup, groupB: TokenGroup) => {
return TOKEN_GROUPS_ORDER.indexOf(groupA[0] as TokenType) > TOKEN_GROUPS_ORDER.indexOf(groupB[0] as TokenType) ? 1 : -1;
Expand Down Expand Up @@ -67,11 +67,14 @@ const sortErc20Tokens = (sort: Sort) => (dataA: TokenEnhancedData, dataB: TokenE

const sortErc721Tokens = () => () => 0;

const sortErc7984Tokens = () => () => 0;

export const sortingFns = {
'ERC-20': sortErc20Tokens,
'ERC-721': sortErc721Tokens,
'ERC-1155': sortErc1155or404Tokens,
'ERC-404': sortErc1155or404Tokens,
'ERC-7984': sortErc7984Tokens,
};

export const filterTokens = (searchTerm: string) => ({ token }: AddressTokenBalance) => {
Expand Down
Loading