mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
Zilliqa chain type: support for ZRC-2 (#3202)
* Zilliqa chain type: support for ZRC-2 * add zrc-2 address * add socket support and token select info * add token types to watchlist notification settings * add token tag * add is fungible helper * aboba * review fixes
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { RollupType } from 'types/client/rollup';
|
||||
import type { AdditionalTokenType } from 'types/client/token';
|
||||
import type { NetworkVerificationType, NetworkVerificationTypeEnvs } from 'types/networks';
|
||||
|
||||
import { urlValidator } from 'toolkit/components/forms/validators/url';
|
||||
@@ -48,6 +49,7 @@ const chain = Object.freeze({
|
||||
},
|
||||
hasMultipleGasCurrencies: getEnvValue('NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES') === 'true',
|
||||
tokenStandard: getEnvValue('NEXT_PUBLIC_NETWORK_TOKEN_STANDARD_NAME') || 'ERC',
|
||||
additionalTokenTypes: parseEnvJson<Array<AdditionalTokenType>>(getEnvValue('NEXT_PUBLIC_NETWORK_ADDITIONAL_TOKEN_TYPES')) || [],
|
||||
rpcUrls,
|
||||
isTestnet: getEnvValue('NEXT_PUBLIC_IS_TESTNET') === 'true',
|
||||
verificationType,
|
||||
|
||||
@@ -51,4 +51,5 @@ NEXT_PUBLIC_VIEWS_ADDRESS_BECH_32_PREFIX=zil
|
||||
NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT=["base16", "bech32"]
|
||||
NEXT_PUBLIC_VIEWS_CONTRACT_DECODED_BYTECODE_ENABLED=true
|
||||
NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED=true
|
||||
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
|
||||
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
|
||||
NEXT_PUBLIC_NETWORK_ADDITIONAL_TOKEN_TYPES=[{'id':'ZRC-2','name':'ZRC-2'}]
|
||||
@@ -42,5 +42,13 @@ export default yup.object({
|
||||
otherwise: (schema) => schema,
|
||||
}),
|
||||
NEXT_PUBLIC_NETWORK_TOKEN_STANDARD_NAME: yup.string(),
|
||||
NEXT_PUBLIC_NETWORK_ADDITIONAL_TOKEN_TYPES: yup
|
||||
.array()
|
||||
.transform(replaceQuotes)
|
||||
.json()
|
||||
.of(yup.object({
|
||||
id: yup.string().required(),
|
||||
name: yup.string().required(),
|
||||
}).noUnknown(true)),
|
||||
NEXT_PUBLIC_IS_TESTNET: yup.boolean(),
|
||||
});
|
||||
@@ -119,6 +119,7 @@ Also, be aware that if you customize the name of the currency or any of its deno
|
||||
| NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES | `boolean` | Set to `true` for networks where users can pay transaction fees in either the native coin or ERC-20 tokens. | - | `false` | `true` | v1.33.0+ |
|
||||
| NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE | `validation` \| `mining` \| 'fee reception' | Verification type in the network. Irrelevant for Arbitrum (verification type is always `posting`) and ZkEvm (verification type is always `sequencing`) L2s | - | `mining` | `validation` | v1.0.x+ |
|
||||
| NEXT_PUBLIC_NETWORK_TOKEN_STANDARD_NAME | `string` | Name of the standard for creating tokens | - | `ERC` | `BEP` | v1.31.0+ |
|
||||
| NEXT_PUBLIC_NETWORK_ADDITIONAL_TOKEN_TYPES | `Array<{id: string; name: string;}>` | List of additional **ERC-20-like (fungible)** token types supported by the explorer UI (extends token type filters/labels). *Note: the token type id must also be supported by the backend API responses and filters.* | - | `[]` | `'[{"id":"ERC-777","name":"ERC-777"}]'` | v2.6.0+ |
|
||||
| NEXT_PUBLIC_IS_TESTNET | `boolean`| Set to true if network is testnet | - | `false` | `true` | v1.0.x+ |
|
||||
|
||||
|
||||
|
||||
+26
-6
@@ -3,21 +3,41 @@ import type { NFTTokenType, TokenType } from 'types/api/token';
|
||||
import config from 'configs/app';
|
||||
|
||||
const tokenStandardName = config.chain.tokenStandard;
|
||||
const additionalTokenTypes = config.chain.additionalTokenTypes;
|
||||
|
||||
export const NFT_TOKEN_TYPES: Record<NFTTokenType, string > = {
|
||||
export const NFT_TOKEN_TYPES: Record<NFTTokenType, string> = {
|
||||
'ERC-721': `${ tokenStandardName }-721`,
|
||||
'ERC-1155': `${ tokenStandardName }-1155`,
|
||||
'ERC-404': `${ tokenStandardName }-404`,
|
||||
};
|
||||
|
||||
export const TOKEN_TYPES: Record<TokenType, string > = {
|
||||
export const TOKEN_TYPES: Record<string, string> = {
|
||||
'ERC-20': `${ tokenStandardName }-20`,
|
||||
...additionalTokenTypes.reduce((result, item) => {
|
||||
result[item.id] = item.name;
|
||||
return result;
|
||||
}, {} as Record<string, string>),
|
||||
...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 NFT_TOKEN_TYPE_IDS: Array<NFTTokenType> = Object.keys(NFT_TOKEN_TYPES) as Array<NFTTokenType>;
|
||||
export const TOKEN_TYPE_IDS = Object.keys(TOKEN_TYPES);
|
||||
|
||||
export function getTokenTypeName(typeId: TokenType) {
|
||||
return TOKEN_TYPES[typeId];
|
||||
export function getTokenTypeName(typeId: string) {
|
||||
return TOKEN_TYPES[typeId] || typeId;
|
||||
}
|
||||
|
||||
export function isFungibleTokenType(typeId: TokenType): boolean {
|
||||
return typeId === 'ERC-20' || additionalTokenTypes.some((item) => item.id === typeId);
|
||||
}
|
||||
|
||||
export function hasTokenTransferValue(typeId: TokenType) {
|
||||
if (typeId === 'ERC-20' || typeId === 'ERC-1155' || typeId === 'ERC-404') {
|
||||
return true;
|
||||
}
|
||||
return additionalTokenTypes.some((item) => item.id === typeId);
|
||||
}
|
||||
|
||||
export function hasTokenIds(typeId: TokenType) {
|
||||
return typeId === 'ERC-1155' || typeId === 'ERC-404';
|
||||
}
|
||||
|
||||
+30
-30
@@ -8,7 +8,7 @@ export const tokenInfo: TokenInfo = {
|
||||
holders_count: '46554',
|
||||
name: 'ARIANEE',
|
||||
symbol: 'ARIA',
|
||||
type: 'ERC-20' as const,
|
||||
type: 'ERC-20',
|
||||
total_supply: '1235',
|
||||
icon_url: 'http://localhost:3000/token-icon.png',
|
||||
reputation: 'ok',
|
||||
@@ -19,7 +19,7 @@ export const tokenCounters: TokenCounters = {
|
||||
transfers_count: '88282281',
|
||||
};
|
||||
|
||||
export const tokenInfoERC20a: TokenInfo<'ERC-20'> = {
|
||||
export const tokenInfoERC20a: TokenInfo = {
|
||||
address_hash: '0xb2a90505dc6680a7a695f7975d0d32EeF610f456',
|
||||
circulating_market_cap: '117268489.23970924',
|
||||
decimals: '18',
|
||||
@@ -28,12 +28,12 @@ export const tokenInfoERC20a: TokenInfo<'ERC-20'> = {
|
||||
name: 'hyfi.token',
|
||||
symbol: 'HyFi',
|
||||
total_supply: '369000000000000000000000000',
|
||||
type: 'ERC-20' as const,
|
||||
type: 'ERC-20',
|
||||
icon_url: 'http://localhost:3000/token-icon.png',
|
||||
reputation: 'ok',
|
||||
};
|
||||
|
||||
export const tokenInfoERC20b: TokenInfo<'ERC-20'> = {
|
||||
export const tokenInfoERC20b: TokenInfo = {
|
||||
address_hash: '0xc1116c98ba622a6218433fF90a2E40DEa482d7A7',
|
||||
circulating_market_cap: '115060192.36105014',
|
||||
decimals: '6',
|
||||
@@ -42,12 +42,12 @@ export const tokenInfoERC20b: TokenInfo<'ERC-20'> = {
|
||||
name: 'USD Coin',
|
||||
symbol: 'USDC',
|
||||
total_supply: '900000000000000000000000000',
|
||||
type: 'ERC-20' as const,
|
||||
type: 'ERC-20',
|
||||
icon_url: null,
|
||||
reputation: 'ok',
|
||||
};
|
||||
|
||||
export const tokenInfoERC20c: TokenInfo<'ERC-20'> = {
|
||||
export const tokenInfoERC20c: TokenInfo = {
|
||||
address_hash: '0xc1116c98ba622a6218433fF90a2E40DEa482d7A8',
|
||||
circulating_market_cap: null,
|
||||
decimals: '18',
|
||||
@@ -56,12 +56,12 @@ export const tokenInfoERC20c: TokenInfo<'ERC-20'> = {
|
||||
name: 'Ethereum',
|
||||
symbol: 'ETH',
|
||||
total_supply: '1000000000000000000000000',
|
||||
type: 'ERC-20' as const,
|
||||
type: 'ERC-20',
|
||||
icon_url: null,
|
||||
reputation: 'ok',
|
||||
};
|
||||
|
||||
export const tokenInfoERC20d: TokenInfo<'ERC-20'> = {
|
||||
export const tokenInfoERC20d: TokenInfo = {
|
||||
address_hash: '0xCc7bb2D219A0FC08033E130629C2B854b7bA9196',
|
||||
circulating_market_cap: null,
|
||||
decimals: '18',
|
||||
@@ -70,12 +70,12 @@ export const tokenInfoERC20d: TokenInfo<'ERC-20'> = {
|
||||
name: 'Zeta',
|
||||
symbol: 'ZETA',
|
||||
total_supply: '2100000000000000000000000000',
|
||||
type: 'ERC-20' as const,
|
||||
type: 'ERC-20',
|
||||
icon_url: null,
|
||||
reputation: 'ok',
|
||||
};
|
||||
|
||||
export const tokenInfoERC20LongSymbol: TokenInfo<'ERC-20'> = {
|
||||
export const tokenInfoERC20LongSymbol: TokenInfo = {
|
||||
address_hash: '0xCc7bb2D219A0FC08033E130629C2B854b7bA9197',
|
||||
circulating_market_cap: '112855875.75888918',
|
||||
decimals: '18',
|
||||
@@ -84,12 +84,12 @@ export const tokenInfoERC20LongSymbol: TokenInfo<'ERC-20'> = {
|
||||
name: 'Zeta',
|
||||
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
|
||||
total_supply: '2100000000000000000000000000',
|
||||
type: 'ERC-20' as const,
|
||||
type: 'ERC-20',
|
||||
icon_url: null,
|
||||
reputation: 'ok',
|
||||
};
|
||||
|
||||
export const tokenInfoERC721a: TokenInfo<'ERC-721'> = {
|
||||
export const tokenInfoERC721a: TokenInfo = {
|
||||
address_hash: '0xDe7cAc71E072FCBd4453E5FB3558C2684d1F88A0',
|
||||
circulating_market_cap: null,
|
||||
decimals: null,
|
||||
@@ -98,12 +98,12 @@ export const tokenInfoERC721a: TokenInfo<'ERC-721'> = {
|
||||
name: 'HyFi Athena',
|
||||
symbol: 'HYFI_ATHENA',
|
||||
total_supply: '105',
|
||||
type: 'ERC-721' as const,
|
||||
type: 'ERC-721',
|
||||
icon_url: null,
|
||||
reputation: 'ok',
|
||||
};
|
||||
|
||||
export const tokenInfoERC721b: TokenInfo<'ERC-721'> = {
|
||||
export const tokenInfoERC721b: TokenInfo = {
|
||||
address_hash: '0xA8d5C7beEA8C9bB57f5fBa35fB638BF45550b11F',
|
||||
circulating_market_cap: null,
|
||||
decimals: null,
|
||||
@@ -112,12 +112,12 @@ export const tokenInfoERC721b: TokenInfo<'ERC-721'> = {
|
||||
name: 'World Of Women Galaxy',
|
||||
symbol: 'WOWG',
|
||||
total_supply: null,
|
||||
type: 'ERC-721' as const,
|
||||
type: 'ERC-721',
|
||||
icon_url: null,
|
||||
reputation: 'ok',
|
||||
};
|
||||
|
||||
export const tokenInfoERC721c: TokenInfo<'ERC-721'> = {
|
||||
export const tokenInfoERC721c: TokenInfo = {
|
||||
address_hash: '0x47646F1d7dc4Dd2Db5a41D092e2Cf966e27A4992',
|
||||
circulating_market_cap: null,
|
||||
decimals: null,
|
||||
@@ -126,12 +126,12 @@ export const tokenInfoERC721c: TokenInfo<'ERC-721'> = {
|
||||
name: 'Puma',
|
||||
symbol: 'PUMA',
|
||||
total_supply: null,
|
||||
type: 'ERC-721' as const,
|
||||
type: 'ERC-721',
|
||||
icon_url: null,
|
||||
reputation: 'ok',
|
||||
};
|
||||
|
||||
export const tokenInfoERC721LongSymbol: TokenInfo<'ERC-721'> = {
|
||||
export const tokenInfoERC721LongSymbol: TokenInfo = {
|
||||
address_hash: '0x47646F1d7dc4Dd2Db5a41D092e2Cf966e27A4993',
|
||||
circulating_market_cap: null,
|
||||
decimals: null,
|
||||
@@ -140,12 +140,12 @@ export const tokenInfoERC721LongSymbol: TokenInfo<'ERC-721'> = {
|
||||
name: 'Puma',
|
||||
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
|
||||
total_supply: null,
|
||||
type: 'ERC-721' as const,
|
||||
type: 'ERC-721',
|
||||
icon_url: null,
|
||||
reputation: 'ok',
|
||||
};
|
||||
|
||||
export const tokenInfoERC1155a: TokenInfo<'ERC-1155'> = {
|
||||
export const tokenInfoERC1155a: TokenInfo = {
|
||||
address_hash: '0x4b333DEd10c7ca855EA2C8D4D90A0a8b73788c8e',
|
||||
circulating_market_cap: null,
|
||||
decimals: null,
|
||||
@@ -154,12 +154,12 @@ export const tokenInfoERC1155a: TokenInfo<'ERC-1155'> = {
|
||||
name: 'HyFi Membership',
|
||||
symbol: 'HYFI_MEMBERSHIP',
|
||||
total_supply: '482',
|
||||
type: 'ERC-1155' as const,
|
||||
type: 'ERC-1155',
|
||||
icon_url: null,
|
||||
reputation: 'ok',
|
||||
};
|
||||
|
||||
export const tokenInfoERC1155b: TokenInfo<'ERC-1155'> = {
|
||||
export const tokenInfoERC1155b: TokenInfo = {
|
||||
address_hash: '0xf4b71b179132ad457f6bcae2a55efa9e4b26eefc',
|
||||
circulating_market_cap: null,
|
||||
decimals: null,
|
||||
@@ -168,12 +168,12 @@ export const tokenInfoERC1155b: TokenInfo<'ERC-1155'> = {
|
||||
name: 'WinkyVerse Collections',
|
||||
symbol: 'WVC',
|
||||
total_supply: '4943',
|
||||
type: 'ERC-1155' as const,
|
||||
type: 'ERC-1155',
|
||||
icon_url: null,
|
||||
reputation: 'ok',
|
||||
};
|
||||
|
||||
export const tokenInfoERC1155WithoutName: TokenInfo<'ERC-1155'> = {
|
||||
export const tokenInfoERC1155WithoutName: TokenInfo = {
|
||||
address_hash: '0x4b333DEd10c7ca855EA2C8D4D90A0a8b73788c8a',
|
||||
circulating_market_cap: null,
|
||||
decimals: null,
|
||||
@@ -182,12 +182,12 @@ export const tokenInfoERC1155WithoutName: TokenInfo<'ERC-1155'> = {
|
||||
name: null,
|
||||
symbol: null,
|
||||
total_supply: '482',
|
||||
type: 'ERC-1155' as const,
|
||||
type: 'ERC-1155',
|
||||
icon_url: null,
|
||||
reputation: 'ok',
|
||||
};
|
||||
|
||||
export const tokenInfoERC404: TokenInfo<'ERC-404'> = {
|
||||
export const tokenInfoERC404: TokenInfo = {
|
||||
address_hash: '0xB5C457dDB4cE3312a6C5a2b056a1652bd542a208',
|
||||
circulating_market_cap: '0.0',
|
||||
decimals: '18',
|
||||
@@ -197,11 +197,11 @@ export const tokenInfoERC404: TokenInfo<'ERC-404'> = {
|
||||
name: 'OMNI404',
|
||||
symbol: 'O404',
|
||||
total_supply: '6482275000000000000',
|
||||
type: 'ERC-404' as const,
|
||||
type: 'ERC-404',
|
||||
reputation: 'ok',
|
||||
};
|
||||
|
||||
export const bridgedTokenA: TokenInfo<'ERC-20'> = {
|
||||
export const bridgedTokenA: TokenInfo = {
|
||||
...tokenInfoERC20a,
|
||||
is_bridged: true,
|
||||
origin_chain_id: '1',
|
||||
@@ -209,7 +209,7 @@ export const bridgedTokenA: TokenInfo<'ERC-20'> = {
|
||||
foreign_address: '0x4b333DEd10c7ca855EA2C8D4D90A0a8b73788c8b',
|
||||
};
|
||||
|
||||
export const bridgedTokenB: TokenInfo<'ERC-20'> = {
|
||||
export const bridgedTokenB: TokenInfo = {
|
||||
...tokenInfoERC20b,
|
||||
is_bridged: true,
|
||||
origin_chain_id: '56',
|
||||
@@ -217,7 +217,7 @@ export const bridgedTokenB: TokenInfo<'ERC-20'> = {
|
||||
foreign_address: '0xf4b71b179132ad457f6bcae2a55efa9e4b26eefd',
|
||||
};
|
||||
|
||||
export const bridgedTokenC: TokenInfo<'ERC-20'> = {
|
||||
export const bridgedTokenC: TokenInfo = {
|
||||
...tokenInfoERC20d,
|
||||
is_bridged: true,
|
||||
origin_chain_id: '99',
|
||||
|
||||
@@ -154,7 +154,7 @@ export const erc1155A: TokenTransfer = {
|
||||
export const erc1155B: TokenTransfer = {
|
||||
...erc1155A,
|
||||
token: {
|
||||
...(erc1155A.token as TokenInfo<'ERC-1155'>),
|
||||
...(erc1155A.token as TokenInfo),
|
||||
name: 'SastanaNFT',
|
||||
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
|
||||
},
|
||||
@@ -164,7 +164,7 @@ export const erc1155B: TokenTransfer = {
|
||||
export const erc1155C: TokenTransfer = {
|
||||
...erc1155A,
|
||||
token: {
|
||||
...(erc1155A.token as TokenInfo<'ERC-1155'>),
|
||||
...(erc1155A.token as TokenInfo),
|
||||
name: 'SastanaNFT',
|
||||
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
|
||||
},
|
||||
@@ -174,7 +174,7 @@ export const erc1155C: TokenTransfer = {
|
||||
export const erc1155D: TokenTransfer = {
|
||||
...erc1155A,
|
||||
token: {
|
||||
...(erc1155A.token as TokenInfo<'ERC-1155'>),
|
||||
...(erc1155A.token as TokenInfo),
|
||||
name: 'SastanaNFT',
|
||||
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
|
||||
},
|
||||
@@ -235,7 +235,7 @@ export const erc404A: TokenTransfer = {
|
||||
export const erc404B: TokenTransfer = {
|
||||
...erc404A,
|
||||
token: {
|
||||
...(erc404A.token as TokenInfo<'ERC-404'>),
|
||||
...(erc404A.token as TokenInfo),
|
||||
name: 'SastanaNFT',
|
||||
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
|
||||
},
|
||||
|
||||
@@ -60,10 +60,7 @@ export const joinChannel = async(socket: WebSocket, channelName: string) => {
|
||||
|
||||
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'coin_balance', payload: { coin_balance: AddressCoinBalanceHistoryItem }): void;
|
||||
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'token_balance', payload: { block_number: number }): void;
|
||||
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'updated_token_balances_erc_20', payload: AddressTokensBalancesSocketMessage): void;
|
||||
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'updated_token_balances_erc_721', payload: AddressTokensBalancesSocketMessage): void;
|
||||
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'updated_token_balances_erc_1155', payload: AddressTokensBalancesSocketMessage): void;
|
||||
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'updated_token_balances_erc_404', payload: AddressTokensBalancesSocketMessage): void;
|
||||
export function sendMessage(socket: WebSocket, channel: Channel, msg: `updated_token_balances_${ string }`, payload: AddressTokensBalancesSocketMessage): void;
|
||||
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'transaction', payload: { transaction: number }): void;
|
||||
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'transaction', payload: { transactions: Array<Transaction> }): void;
|
||||
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'pending_transaction', payload: { pending_transaction: number }): void;
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@ import { generateListStub } from './utils';
|
||||
|
||||
export const BLOCK_HASH = '0x8fa7b9e5e5e79deeb62d608db22ba9a5cb45388c7ebb9223ae77331c6080dc70';
|
||||
|
||||
export const TOKEN_INFO_ERC_20: TokenInfo<'ERC-20'> = {
|
||||
export const TOKEN_INFO_ERC_20: TokenInfo = {
|
||||
address_hash: ADDRESS_HASH,
|
||||
circulating_market_cap: '117629601.61913824',
|
||||
decimals: '18',
|
||||
@@ -30,19 +30,19 @@ export const TOKEN_INFO_ERC_20: TokenInfo<'ERC-20'> = {
|
||||
reputation: 'ok',
|
||||
};
|
||||
|
||||
export const TOKEN_INFO_ERC_721: TokenInfo<'ERC-721'> = {
|
||||
export const TOKEN_INFO_ERC_721: TokenInfo = {
|
||||
...TOKEN_INFO_ERC_20,
|
||||
circulating_market_cap: null,
|
||||
type: 'ERC-721',
|
||||
};
|
||||
|
||||
export const TOKEN_INFO_ERC_1155: TokenInfo<'ERC-1155'> = {
|
||||
export const TOKEN_INFO_ERC_1155: TokenInfo = {
|
||||
...TOKEN_INFO_ERC_20,
|
||||
circulating_market_cap: null,
|
||||
type: 'ERC-1155',
|
||||
};
|
||||
|
||||
export const TOKEN_INFO_ERC_404: TokenInfo<'ERC-404'> = {
|
||||
export const TOKEN_INFO_ERC_404: TokenInfo = {
|
||||
...TOKEN_INFO_ERC_20,
|
||||
circulating_market_cap: null,
|
||||
type: 'ERC-404',
|
||||
|
||||
@@ -39,6 +39,7 @@ export interface NotificationSettings {
|
||||
'ERC-20': NotificationDirection;
|
||||
'ERC-721': NotificationDirection;
|
||||
'ERC-404': NotificationDirection;
|
||||
[key: string]: NotificationDirection;
|
||||
}
|
||||
|
||||
export interface NotificationMethods {
|
||||
|
||||
@@ -144,7 +144,7 @@ export type AddressTokenTransferFilters = {
|
||||
};
|
||||
|
||||
export type AddressTokensFilter = {
|
||||
type: TokenType;
|
||||
type: TokenType | Array<TokenType>;
|
||||
};
|
||||
|
||||
export type AddressNFTTokensFilter = {
|
||||
|
||||
+2
-2
@@ -41,7 +41,7 @@ export type CeloEpochDetails = {
|
||||
carbon_offsetting_transfer: TokenTransfer | null;
|
||||
community_transfer: TokenTransfer | null;
|
||||
transfers_total: {
|
||||
token: TokenInfo<'ERC-20'> | null;
|
||||
token: TokenInfo | null;
|
||||
total: Erc20TotalPayload | null;
|
||||
} | null;
|
||||
} | null;
|
||||
@@ -50,7 +50,7 @@ export type CeloEpochDetails = {
|
||||
|
||||
export interface CeloEpochElectionReward {
|
||||
count: number;
|
||||
token: TokenInfo<'ERC-20'>;
|
||||
token: TokenInfo;
|
||||
total: string;
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -2,13 +2,14 @@ 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;
|
||||
// token type can come from the environment config, so it can be any string
|
||||
export type TokenType = string;
|
||||
|
||||
export type TokenReputation = 'ok' | 'scam';
|
||||
|
||||
export interface TokenInfo<T extends TokenType = TokenType> {
|
||||
export interface TokenInfo {
|
||||
address_hash: string;
|
||||
type: T;
|
||||
type: TokenType;
|
||||
symbol: string | null;
|
||||
name: string | null;
|
||||
decimals: string | null;
|
||||
@@ -24,6 +25,7 @@ export interface TokenInfo<T extends TokenType = TokenType> {
|
||||
origin_chain_id?: string | null;
|
||||
foreign_address?: string | null;
|
||||
filecoin_robust_address?: string | null;
|
||||
zilliqa?: { zrc2_address_hash?: string };
|
||||
}
|
||||
|
||||
export interface TokenCounters {
|
||||
|
||||
@@ -30,19 +30,19 @@ export type Erc404TotalPayload = {
|
||||
|
||||
export type TokenTransfer = (
|
||||
{
|
||||
token: TokenInfo<'ERC-20'> | null;
|
||||
token: TokenInfo | null;
|
||||
total: Erc20TotalPayload | null;
|
||||
} |
|
||||
{
|
||||
token: TokenInfo<'ERC-721'> | null;
|
||||
token: TokenInfo | null;
|
||||
total: Erc721TotalPayload | null;
|
||||
} |
|
||||
{
|
||||
token: TokenInfo<'ERC-1155'> | null;
|
||||
token: TokenInfo | null;
|
||||
total: Erc1155TotalPayload | null;
|
||||
} |
|
||||
{
|
||||
token: TokenInfo<'ERC-404'> | null;
|
||||
token: TokenInfo | null;
|
||||
total: Erc404TotalPayload | null;
|
||||
}
|
||||
) & TokenTransferBase;
|
||||
|
||||
@@ -82,7 +82,7 @@ export type Transaction = {
|
||||
};
|
||||
// Celo fields
|
||||
celo?: {
|
||||
gas_token: TokenInfo<'ERC-20'> | null;
|
||||
gas_token: TokenInfo | null;
|
||||
};
|
||||
// zkEvm fields
|
||||
zkevm_verify_hash?: string;
|
||||
|
||||
@@ -21,13 +21,13 @@ type ChangeDirection = 'from' | 'to';
|
||||
|
||||
export interface TxStateChangeTokenErc20 {
|
||||
type: 'token';
|
||||
token: TokenInfo<'ERC-20'>;
|
||||
token: TokenInfo;
|
||||
change: string;
|
||||
}
|
||||
|
||||
export interface TxStateChangeTokenErc721 {
|
||||
type: 'token';
|
||||
token: TokenInfo<'ERC-721'>;
|
||||
token: TokenInfo;
|
||||
change: Array<{
|
||||
direction: ChangeDirection;
|
||||
total: Erc721TotalPayload;
|
||||
@@ -36,14 +36,14 @@ export interface TxStateChangeTokenErc721 {
|
||||
|
||||
export interface TxStateChangeTokenErc1155 {
|
||||
type: 'token';
|
||||
token: TokenInfo<'ERC-1155'>;
|
||||
token: TokenInfo;
|
||||
change: string;
|
||||
token_id: string;
|
||||
}
|
||||
|
||||
export interface TxStateChangeTokenErc404 {
|
||||
type: 'token';
|
||||
token: TokenInfo<'ERC-404'>;
|
||||
token: TokenInfo;
|
||||
change: string;
|
||||
token_id: string;
|
||||
}
|
||||
|
||||
@@ -22,3 +22,8 @@ export interface TokenBridge {
|
||||
title: string;
|
||||
short_title: string;
|
||||
}
|
||||
|
||||
export interface AdditionalTokenType {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { AddressTokensResponse } from 'types/api/address';
|
||||
import type { AddressTokenBalance, AddressTokensResponse } from 'types/api/address';
|
||||
|
||||
import * as addressMock from 'mocks/address/address';
|
||||
import * as tokensMock from 'mocks/address/tokens';
|
||||
@@ -268,6 +268,77 @@ test.describe('update balances via socket', () => {
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('custom token ZRC-2', async({ render, page, createSocket, mockApiResponse, mockEnvs }) => {
|
||||
test.slow();
|
||||
|
||||
await mockEnvs([
|
||||
[ 'NEXT_PUBLIC_NETWORK_ADDITIONAL_TOKEN_TYPES', '[{"id":"ZRC-2","name":"ZRC-2"}]' ],
|
||||
]);
|
||||
|
||||
const hooksConfig = {
|
||||
router: {
|
||||
query: { hash: ADDRESS_HASH, tab: 'tokens_erc20' },
|
||||
isReady: true,
|
||||
},
|
||||
};
|
||||
|
||||
const responseErc20AndZrc2 = {
|
||||
items: [ tokensMock.erc20a ],
|
||||
next_page_params: null,
|
||||
};
|
||||
const responseZrc2 = {
|
||||
items: [],
|
||||
next_page_params: null,
|
||||
};
|
||||
|
||||
const erc20AndZrc2ApiUrl = await mockApiResponse(
|
||||
'general:address_tokens',
|
||||
responseErc20AndZrc2,
|
||||
{ pathParams: { hash: ADDRESS_HASH }, queryParams: { type: [ 'ERC-20', 'ZRC-2' ] } },
|
||||
);
|
||||
const zrc2ApiUrl = await mockApiResponse(
|
||||
'general:address_tokens',
|
||||
responseZrc2,
|
||||
{ pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ZRC-2' } },
|
||||
);
|
||||
|
||||
const component = await render(
|
||||
<Box pt={{ base: '134px', lg: 6 }}>
|
||||
<AddressTokens/>
|
||||
</Box>,
|
||||
{ hooksConfig },
|
||||
{ withSocket: true },
|
||||
);
|
||||
|
||||
await page.waitForResponse(erc20AndZrc2ApiUrl);
|
||||
await page.waitForResponse(zrc2ApiUrl);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
|
||||
const zrc2TokenBalance: AddressTokenBalance = {
|
||||
...tokensMock.erc20a,
|
||||
token: {
|
||||
...tokensMock.erc20a.token,
|
||||
// custom token type enabled via NEXT_PUBLIC_NETWORK_ADDITIONAL_TOKEN_TYPES
|
||||
type: 'ZRC-2',
|
||||
address_hash: '0x00000000000000000000000000000000000000f2',
|
||||
name: 'ZRC-2 Token',
|
||||
symbol: 'ZRC2',
|
||||
exchange_rate: '0.02',
|
||||
} as unknown as AddressTokenBalance['token'],
|
||||
value: '1234500000000000000',
|
||||
};
|
||||
|
||||
const socket = await createSocket();
|
||||
const channel = await socketServer.joinChannel(socket, `addresses:${ ADDRESS_HASH.toLowerCase() }`);
|
||||
socketServer.sendMessage(socket, channel, 'updated_token_balances_zrc_2', {
|
||||
overflow: false,
|
||||
token_balances: [ zrc2TokenBalance ],
|
||||
});
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
});
|
||||
|
||||
test('native token', async({ render, mockEnvs }) => {
|
||||
|
||||
@@ -50,7 +50,7 @@ const AddressTokens = ({ shouldRender = true, isQueryEnabled = true }: Props) =>
|
||||
const erc20Query = useQueryWithPages({
|
||||
resourceName: 'general:address_tokens',
|
||||
pathParams: { hash },
|
||||
filters: { type: 'ERC-20' },
|
||||
filters: { type: [ 'ERC-20', ...config.chain.additionalTokenTypes.map(item => item.id) ] },
|
||||
scrollRef,
|
||||
options: {
|
||||
enabled: isQueryEnabled && (!tab || tab === 'tokens' || tab === 'tokens_erc20'),
|
||||
@@ -74,7 +74,10 @@ const AddressTokens = ({ shouldRender = true, isQueryEnabled = true }: Props) =>
|
||||
const tabs = [
|
||||
{
|
||||
id: 'tokens_erc20',
|
||||
title: `${ config.chain.tokenStandard }-20`,
|
||||
title: [
|
||||
`${ config.chain.tokenStandard }-20`,
|
||||
...config.chain.additionalTokenTypes.map((item) => item.name),
|
||||
].join(' & '),
|
||||
component: (
|
||||
<ERC20Tokens
|
||||
items={ erc20Query.data?.items }
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
@@ -6,6 +6,7 @@ import { route } from 'nextjs/routes';
|
||||
|
||||
import config from 'configs/app';
|
||||
import multichainConfig from 'configs/multichain';
|
||||
import { isFungibleTokenType } from 'lib/token/tokenTypes';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
import { TruncatedText } from 'toolkit/components/truncation/TruncatedText';
|
||||
import NativeTokenTag from 'ui/shared/celo/NativeTokenTag';
|
||||
@@ -34,18 +35,21 @@ const TokenSelectItem = ({ data }: Props) => {
|
||||
}, [ data.chain_values ]);
|
||||
|
||||
const secondRow = (() => {
|
||||
switch (data.token.type) {
|
||||
case 'ERC-20': {
|
||||
const tokenDecimals = Number(data.token.decimals ?? 18);
|
||||
const text = `${ BigNumber(data.value).dividedBy(10 ** tokenDecimals).dp(8).toFormat() } ${ data.token.symbol || '' }`;
|
||||
const isFungibleToken = isFungibleTokenType(data.token.type);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TruncatedText text={ text }/>
|
||||
{ data.token.exchange_rate && <chakra.span ml={ 2 }>@{ Number(data.token.exchange_rate).toLocaleString() }</chakra.span> }
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (isFungibleToken) {
|
||||
const tokenDecimals = Number(data.token.decimals ?? 18);
|
||||
const text = `${ BigNumber(data.value).dividedBy(10 ** tokenDecimals).dp(8).toFormat() } ${ data.token.symbol || '' }`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<TruncatedText text={ text }/>
|
||||
{ data.token.exchange_rate && <chakra.span ml={ 2 }>@{ Number(data.token.exchange_rate).toLocaleString() }</chakra.span> }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
switch (data.token.type) {
|
||||
case 'ERC-721': {
|
||||
const text = `${ BigNumber(data.value).toFormat() } ${ data.token.symbol || '' }`;
|
||||
return <TruncatedText text={ text }/>;
|
||||
|
||||
@@ -3,7 +3,6 @@ import { sumBy } from 'es-toolkit';
|
||||
import React from 'react';
|
||||
|
||||
import type { FormattedData } from './types';
|
||||
import type { TokenType } from 'types/api/token';
|
||||
|
||||
import { getTokenTypeName } from 'lib/token/tokenTypes';
|
||||
import { Link } from 'toolkit/chakra/link';
|
||||
@@ -12,20 +11,18 @@ import { thinsp } from 'toolkit/utils/htmlEntities';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
|
||||
import type { Sort } from '../utils/tokenUtils';
|
||||
import { sortTokenGroups, sortingFns } from '../utils/tokenUtils';
|
||||
import { getSortingFn, sortTokenGroups } from '../utils/tokenUtils';
|
||||
import TokenSelectItem from './TokenSelectItem';
|
||||
|
||||
interface Props {
|
||||
searchTerm: string;
|
||||
erc20sort: Sort;
|
||||
erc1155sort: Sort;
|
||||
erc404sort: Sort;
|
||||
getSort: (typeId: string) => Sort;
|
||||
filteredData: FormattedData;
|
||||
onInputChange: (searchTerm: string) => void;
|
||||
onSortClick: (event: React.SyntheticEvent) => void;
|
||||
}
|
||||
|
||||
const TokenSelectMenu = ({ erc20sort, erc1155sort, erc404sort, filteredData, onInputChange, onSortClick, searchTerm }: Props) => {
|
||||
const TokenSelectMenu = ({ getSort, filteredData, onInputChange, onSortClick, searchTerm }: Props) => {
|
||||
const hasFilteredResult = sumBy(Object.values(filteredData), ({ items }) => items.length) > 0;
|
||||
|
||||
return (
|
||||
@@ -43,27 +40,14 @@ const TokenSelectMenu = ({ erc20sort, erc1155sort, erc404sort, filteredData, onI
|
||||
return null;
|
||||
}
|
||||
|
||||
const type = tokenType as TokenType;
|
||||
const arrowTransform =
|
||||
(type === 'ERC-1155' && erc1155sort === 'desc') ||
|
||||
(type === 'ERC-404' && erc404sort === 'desc') ||
|
||||
(type === 'ERC-20' && erc20sort === 'desc') ?
|
||||
'rotate(90deg)' :
|
||||
'rotate(-90deg)';
|
||||
const sortDirection: Sort = (() => {
|
||||
switch (type) {
|
||||
case 'ERC-1155':
|
||||
return erc1155sort;
|
||||
case 'ERC-20':
|
||||
return erc20sort;
|
||||
default:
|
||||
return 'desc';
|
||||
}
|
||||
})();
|
||||
const hasSort =
|
||||
const type = tokenType;
|
||||
const sortDirection = getSort(type);
|
||||
const arrowTransform = sortDirection === 'desc' ? 'rotate(90deg)' : 'rotate(-90deg)';
|
||||
const hasSort = tokenInfo.items.length > 1 && (
|
||||
(type === 'ERC-404' && tokenInfo.items.some(item => item.value)) ||
|
||||
type === 'ERC-1155' ||
|
||||
(type === 'ERC-20' && tokenInfo.items.some(({ usd }) => usd));
|
||||
(type !== 'ERC-721' && tokenInfo.items.some(({ usd }) => usd))
|
||||
);
|
||||
const numPrefix = tokenInfo.isOverflow ? `>${ thinsp }` : '';
|
||||
|
||||
return (
|
||||
@@ -78,7 +62,7 @@ const TokenSelectMenu = ({ erc20sort, erc1155sort, erc404sort, filteredData, onI
|
||||
</Link>
|
||||
) }
|
||||
</Flex>
|
||||
{ tokenInfo.items.sort(sortingFns[type](sortDirection)).map((data) =>
|
||||
{ tokenInfo.items.sort(getSortingFn(type)(sortDirection)).map((data) =>
|
||||
<TokenSelectItem key={ data.token.address_hash + data.token_id } data={ data }/>) }
|
||||
</Box>
|
||||
);
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
@@ -1,8 +1,6 @@
|
||||
import type { TokenType } from 'types/api/token';
|
||||
|
||||
import type { TokenEnhancedData } from 'ui/address/utils/tokenUtils';
|
||||
|
||||
export type FormattedData = Record<TokenType, FormattedDataItem>;
|
||||
export type FormattedData = Record<string, FormattedDataItem>;
|
||||
|
||||
export interface FormattedDataItem {
|
||||
items: Array<TokenEnhancedData>;
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function useTokenSelect(data: FormattedData) {
|
||||
const [ searchTerm, setSearchTerm ] = React.useState('');
|
||||
const [ erc1155sort, setErc1155Sort ] = React.useState<Sort>('desc');
|
||||
const [ erc404sort, setErc404Sort ] = React.useState<Sort>('desc');
|
||||
const [ erc20sort, setErc20Sort ] = React.useState<Sort>('desc');
|
||||
const [ fungibleSortByType, setFungibleSortByType ] = React.useState<Record<string, Sort>>({});
|
||||
|
||||
const onInputChange = React.useCallback((searchTerm: string) => {
|
||||
setSearchTerm(searchTerm);
|
||||
@@ -20,13 +20,22 @@ export default function useTokenSelect(data: FormattedData) {
|
||||
const tokenType = (event.currentTarget as HTMLAnchorElement).getAttribute('data-type');
|
||||
if (tokenType === 'ERC-1155') {
|
||||
setErc1155Sort((prevValue) => prevValue === 'desc' ? 'asc' : 'desc');
|
||||
return;
|
||||
}
|
||||
if (tokenType === 'ERC-404') {
|
||||
setErc404Sort((prevValue) => prevValue === 'desc' ? 'asc' : 'desc');
|
||||
return;
|
||||
}
|
||||
if (tokenType === 'ERC-20') {
|
||||
setErc20Sort((prevValue) => prevValue === 'desc' ? 'asc' : 'desc');
|
||||
if (!tokenType) {
|
||||
return;
|
||||
}
|
||||
setFungibleSortByType((prevValue) => {
|
||||
const current = prevValue[tokenType] || 'desc';
|
||||
return {
|
||||
...prevValue,
|
||||
[tokenType]: current === 'desc' ? 'asc' : 'desc',
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
const filteredData = React.useMemo(() => {
|
||||
@@ -36,9 +45,19 @@ export default function useTokenSelect(data: FormattedData) {
|
||||
}));
|
||||
}, [ data, searchTerm ]);
|
||||
|
||||
const getSort = React.useCallback((typeId: string): Sort => {
|
||||
if (typeId === 'ERC-1155') {
|
||||
return erc1155sort;
|
||||
}
|
||||
if (typeId === 'ERC-404') {
|
||||
return erc404sort;
|
||||
}
|
||||
return fungibleSortByType[typeId] || 'desc';
|
||||
}, [ erc1155sort, erc404sort, fungibleSortByType ]);
|
||||
|
||||
return {
|
||||
searchTerm,
|
||||
erc20sort,
|
||||
getSort,
|
||||
erc1155sort,
|
||||
erc404sort,
|
||||
onInputChange,
|
||||
|
||||
@@ -4,6 +4,7 @@ import React from 'react';
|
||||
import type { AddressTokenBalance } from 'types/api/address';
|
||||
import type { PaginationParams } from 'ui/shared/pagination/types';
|
||||
|
||||
import config from 'configs/app';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import ActionBar from 'ui/shared/ActionBar';
|
||||
import DataListDisplay from 'ui/shared/DataListDisplay';
|
||||
@@ -23,6 +24,8 @@ type Props = {
|
||||
const ERC20Tokens = ({ items, isLoading, pagination, isError, top }: Props) => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const hasAdditionalTokenTypes = config.chain.additionalTokenTypes.length > 0;
|
||||
|
||||
const actionBar = isMobile && pagination.isVisible && (
|
||||
<ActionBar mt={ -6 }>
|
||||
<Pagination ml="auto" { ...pagination }/>
|
||||
@@ -31,12 +34,19 @@ const ERC20Tokens = ({ items, isLoading, pagination, isError, top }: Props) => {
|
||||
|
||||
const content = items ? (
|
||||
<>
|
||||
<Box hideBelow="lg"><ERC20TokensTable data={ items } top={ top ?? (pagination.isVisible ? 72 : 0) } isLoading={ isLoading }/></Box>
|
||||
<Box hideBelow="lg">
|
||||
<ERC20TokensTable
|
||||
data={ items }
|
||||
top={ top ?? (pagination.isVisible ? 72 : 0) }
|
||||
isLoading={ isLoading }
|
||||
hasAdditionalTokenTypes={ hasAdditionalTokenTypes }/>
|
||||
</Box>
|
||||
<Box hideFrom="lg">{ items.map((item, index) => (
|
||||
<ERC20TokensListItem
|
||||
key={ item.token.address_hash + (isLoading ? index : '') }
|
||||
{ ...item }
|
||||
isLoading={ isLoading }
|
||||
hasAdditionalTokenTypes={ hasAdditionalTokenTypes }
|
||||
/>
|
||||
)) }
|
||||
</Box>
|
||||
|
||||
@@ -6,7 +6,9 @@ import type { AddressTokensErc20Item } from './types';
|
||||
|
||||
import config from 'configs/app';
|
||||
import multichainConfig from 'configs/multichain';
|
||||
import { getTokenTypeName } from 'lib/token/tokenTypes';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import { Tag } from 'toolkit/chakra/tag';
|
||||
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
|
||||
import NativeTokenTag from 'ui/shared/celo/NativeTokenTag';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
@@ -16,10 +18,15 @@ import calculateUsdValue from 'ui/shared/value/calculateUsdValue';
|
||||
import SimpleValue from 'ui/shared/value/SimpleValue';
|
||||
import { DEFAULT_ACCURACY_USD } from 'ui/shared/value/utils';
|
||||
|
||||
type Props = AddressTokensErc20Item & { isLoading: boolean };
|
||||
|
||||
const ERC20TokensListItem = ({ token, value, isLoading, chain_values: chainValues }: Props) => {
|
||||
type Props = AddressTokensErc20Item & { isLoading: boolean; hasAdditionalTokenTypes?: boolean };
|
||||
|
||||
const ERC20TokensListItem = ({
|
||||
token,
|
||||
value,
|
||||
isLoading,
|
||||
chain_values: chainValues,
|
||||
hasAdditionalTokenTypes,
|
||||
}: Props) => {
|
||||
const {
|
||||
valueBn: tokenQuantity,
|
||||
usdBn: tokenValue,
|
||||
@@ -51,6 +58,7 @@ const ERC20TokensListItem = ({ token, value, isLoading, chain_values: chainValue
|
||||
width="auto"
|
||||
/>
|
||||
{ isNativeToken && <NativeTokenTag/> }
|
||||
{ hasAdditionalTokenTypes && <Tag loading={ isLoading }>{ getTokenTypeName(token.type) }</Tag> }
|
||||
</Flex>
|
||||
<Flex alignItems="center" pl={ 8 }>
|
||||
<AddressEntity
|
||||
|
||||
@@ -10,9 +10,10 @@ interface Props {
|
||||
data: Array<AddressTokensErc20Item>;
|
||||
top: number;
|
||||
isLoading: boolean;
|
||||
hasAdditionalTokenTypes?: boolean;
|
||||
}
|
||||
|
||||
const ERC20TokensTable = ({ data, top, isLoading }: Props) => {
|
||||
const ERC20TokensTable = ({ data, top, isLoading, hasAdditionalTokenTypes }: Props) => {
|
||||
return (
|
||||
<TableRoot>
|
||||
<TableHeaderSticky top={ top }>
|
||||
@@ -30,6 +31,7 @@ const ERC20TokensTable = ({ data, top, isLoading }: Props) => {
|
||||
key={ item.token.address_hash + (isLoading ? index : '') + (item.chain_values ? Object.keys(item.chain_values).join(',') : '') }
|
||||
{ ...item }
|
||||
isLoading={ isLoading }
|
||||
hasAdditionalTokenTypes={ hasAdditionalTokenTypes }
|
||||
/>
|
||||
)) }
|
||||
</TableBody>
|
||||
|
||||
@@ -6,7 +6,9 @@ import type { AddressTokensErc20Item } from './types';
|
||||
|
||||
import config from 'configs/app';
|
||||
import multichainConfig from 'configs/multichain';
|
||||
import { getTokenTypeName } from 'lib/token/tokenTypes';
|
||||
import { TableCell, TableRow } from 'toolkit/chakra/table';
|
||||
import { Tag } from 'toolkit/chakra/tag';
|
||||
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
|
||||
import NativeTokenTag from 'ui/shared/celo/NativeTokenTag';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
@@ -15,13 +17,14 @@ import calculateUsdValue from 'ui/shared/value/calculateUsdValue';
|
||||
import SimpleValue from 'ui/shared/value/SimpleValue';
|
||||
import { DEFAULT_ACCURACY_USD } from 'ui/shared/value/utils';
|
||||
|
||||
type Props = AddressTokensErc20Item & { isLoading: boolean };
|
||||
type Props = AddressTokensErc20Item & { isLoading: boolean; hasAdditionalTokenTypes?: boolean };
|
||||
|
||||
const ERC20TokensTableItem = ({
|
||||
token,
|
||||
value,
|
||||
chain_values: chainValues,
|
||||
isLoading,
|
||||
hasAdditionalTokenTypes,
|
||||
}: Props) => {
|
||||
|
||||
const {
|
||||
@@ -42,9 +45,11 @@ const ERC20TokensTableItem = ({
|
||||
return chain;
|
||||
}, [ chainValues ]);
|
||||
|
||||
const cellVerticalAlign = hasAdditionalTokenTypes ? 'top' : 'middle';
|
||||
|
||||
return (
|
||||
<TableRow className="group" >
|
||||
<TableCell verticalAlign="middle">
|
||||
<TableCell verticalAlign={ cellVerticalAlign }>
|
||||
<HStack gap={ 2 }>
|
||||
<TokenEntity
|
||||
token={ token }
|
||||
@@ -57,8 +62,9 @@ const ERC20TokensTableItem = ({
|
||||
/>
|
||||
{ isNativeToken && <NativeTokenTag/> }
|
||||
</HStack>
|
||||
{ hasAdditionalTokenTypes && <Tag loading={ isLoading } mt={ 2 }>{ getTokenTypeName(token.type) }</Tag> }
|
||||
</TableCell>
|
||||
<TableCell verticalAlign="middle">
|
||||
<TableCell verticalAlign={ cellVerticalAlign }>
|
||||
<Flex alignItems="center" width="150px" justifyContent="space-between">
|
||||
<AddressEntity
|
||||
address={{ hash: token.address_hash }}
|
||||
@@ -69,7 +75,7 @@ const ERC20TokensTableItem = ({
|
||||
<AddressAddToWallet token={ token } ml={ 4 } isLoading={ isLoading } opacity="0" _groupHover={{ opacity: 1 }}/>
|
||||
</Flex>
|
||||
</TableCell>
|
||||
<TableCell isNumeric verticalAlign="middle">
|
||||
<TableCell isNumeric verticalAlign={ cellVerticalAlign }>
|
||||
{ token.exchange_rate ? (
|
||||
<SimpleValue
|
||||
value={ BigNumber(token.exchange_rate) }
|
||||
@@ -79,14 +85,14 @@ const ERC20TokensTableItem = ({
|
||||
/>
|
||||
) : null }
|
||||
</TableCell>
|
||||
<TableCell isNumeric verticalAlign="middle">
|
||||
<TableCell isNumeric verticalAlign={ cellVerticalAlign }>
|
||||
<SimpleValue
|
||||
value={ tokenQuantity }
|
||||
color={ isNativeToken ? 'text.secondary' : undefined }
|
||||
loading={ isLoading }
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell isNumeric verticalAlign="middle">
|
||||
<TableCell isNumeric verticalAlign={ cellVerticalAlign }>
|
||||
{ token.exchange_rate && (
|
||||
<SimpleValue
|
||||
value={ tokenValue }
|
||||
|
||||
@@ -2,10 +2,10 @@ import BigNumber from 'bignumber.js';
|
||||
import { mapValues } from 'es-toolkit';
|
||||
|
||||
import type { AddressTokenBalance } from 'types/api/address';
|
||||
import type { TokenType } from 'types/api/token';
|
||||
|
||||
import config from 'configs/app';
|
||||
import sumBnReducer from 'lib/bigint/sumBnReducer';
|
||||
import { isFungibleTokenType } from 'lib/token/tokenTypes';
|
||||
import { ZERO } from 'toolkit/utils/consts';
|
||||
|
||||
const isNativeToken = (token: TokenEnhancedData) =>
|
||||
@@ -19,7 +19,7 @@ export type TokenEnhancedData = AddressTokenBalance & {
|
||||
|
||||
export type Sort = 'desc' | 'asc';
|
||||
|
||||
export type TokenSelectData = Record<TokenType, TokenSelectDataItem>;
|
||||
export type TokenSelectData = Record<string, TokenSelectDataItem>;
|
||||
|
||||
export interface TokenSelectDataItem {
|
||||
items: Array<TokenEnhancedData>;
|
||||
@@ -28,10 +28,28 @@ export interface TokenSelectDataItem {
|
||||
|
||||
type TokenGroup = [string, TokenSelectDataItem];
|
||||
|
||||
const TOKEN_GROUPS_ORDER: Array<TokenType> = [ 'ERC-20', 'ERC-721', 'ERC-1155', 'ERC-404' ];
|
||||
const NFT_TOKEN_GROUPS_ORDER = [ 'ERC-721', 'ERC-1155', 'ERC-404' ] as const;
|
||||
|
||||
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;
|
||||
const additionalTypeIds = config.chain.additionalTokenTypes.map((item) => item.id);
|
||||
|
||||
const tokenGroupsOrder = [
|
||||
'ERC-20',
|
||||
...additionalTypeIds,
|
||||
...NFT_TOKEN_GROUPS_ORDER,
|
||||
] as const;
|
||||
|
||||
const orderA = tokenGroupsOrder.indexOf(groupA[0] as (typeof tokenGroupsOrder)[number]);
|
||||
const orderB = tokenGroupsOrder.indexOf(groupB[0] as (typeof tokenGroupsOrder)[number]);
|
||||
|
||||
const normalizedA = orderA === -1 ? 999 : orderA;
|
||||
const normalizedB = orderB === -1 ? 999 : orderB;
|
||||
|
||||
if (normalizedA !== normalizedB) {
|
||||
return normalizedA > normalizedB ? 1 : -1;
|
||||
}
|
||||
|
||||
return groupA[0].localeCompare(groupB[0]);
|
||||
};
|
||||
|
||||
const sortErc1155or404Tokens = (sort: Sort) => (dataA: AddressTokenBalance, dataB: AddressTokenBalance) => {
|
||||
@@ -74,6 +92,10 @@ export const sortingFns = {
|
||||
'ERC-404': sortErc1155or404Tokens,
|
||||
};
|
||||
|
||||
export const getSortingFn = (typeId: string) => {
|
||||
return sortingFns[typeId as keyof typeof sortingFns] || sortErc20Tokens;
|
||||
};
|
||||
|
||||
export const filterTokens = (searchTerm: string) => ({ token }: AddressTokenBalance) => {
|
||||
if (!token.name) {
|
||||
return !searchTerm ? true : token.address_hash.toLowerCase().includes(searchTerm);
|
||||
@@ -83,7 +105,9 @@ export const filterTokens = (searchTerm: string) => ({ token }: AddressTokenBala
|
||||
};
|
||||
|
||||
export const calculateUsdValue = (data: AddressTokenBalance): TokenEnhancedData => {
|
||||
if (data.token.type !== 'ERC-20') {
|
||||
const isFungibleToken = isFungibleTokenType(data.token.type);
|
||||
|
||||
if (!isFungibleToken) {
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useQueries, useQueryClient } from '@tanstack/react-query';
|
||||
import React from 'react';
|
||||
|
||||
import type { SocketMessage } from 'lib/socket/types';
|
||||
import type { AddressTokenBalance, AddressTokensBalancesSocketMessage, AddressTokensResponse } from 'types/api/address';
|
||||
import type { TokenType } from 'types/api/token';
|
||||
|
||||
import config from 'configs/app';
|
||||
import useApiFetch from 'lib/api/useApiFetch';
|
||||
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
|
||||
import { useMultichainContext } from 'lib/contexts/multichain';
|
||||
import useSocketChannel from 'lib/socket/useSocketChannel';
|
||||
import useSocketMessage from 'lib/socket/useSocketMessage';
|
||||
|
||||
import type { TokenEnhancedData } from './tokenUtils';
|
||||
import { calculateUsdValue } from './tokenUtils';
|
||||
|
||||
interface Props {
|
||||
hash?: string;
|
||||
enabled?: boolean;
|
||||
@@ -21,6 +26,13 @@ const tokenBalanceItemIdentityFactory = (match: AddressTokenBalance) => (item: A
|
||||
match.token_instance?.id === item.token_instance?.id
|
||||
));
|
||||
|
||||
const socketEventForTokenType = (tokenTypeId: string): string => {
|
||||
const normalizedTypeId = tokenTypeId.toLowerCase().replace(/[^a-z0-9]+/g, '_');
|
||||
return `updated_token_balances_${ normalizedTypeId }`;
|
||||
};
|
||||
|
||||
const additionalTokenTypes = config.chain.additionalTokenTypes;
|
||||
|
||||
export default function useFetchTokens({ hash, enabled }: Props) {
|
||||
const erc20query = useApiQuery('general:address_tokens', {
|
||||
pathParams: { hash },
|
||||
@@ -43,9 +55,29 @@ export default function useFetchTokens({ hash, enabled }: Props) {
|
||||
queryOptions: { enabled: Boolean(hash) && enabled, refetchOnMount: false },
|
||||
});
|
||||
|
||||
const apiFetch = useApiFetch();
|
||||
const multichainContext = useMultichainContext();
|
||||
const chain = multichainContext?.chain;
|
||||
|
||||
const additionalTokenQueries = useQueries({
|
||||
queries: additionalTokenTypes.map((item) => ({
|
||||
queryKey: getResourceKey('general:address_tokens', { pathParams: { hash }, queryParams: { type: item.id as unknown as TokenType }, chainId: chain?.id }),
|
||||
queryFn: async({ signal }) => {
|
||||
return apiFetch('general:address_tokens', {
|
||||
pathParams: { hash },
|
||||
queryParams: { type: item.id as unknown as TokenType },
|
||||
chain,
|
||||
fetchParams: { signal },
|
||||
}) as Promise<AddressTokensResponse>;
|
||||
},
|
||||
enabled: Boolean(hash) && enabled,
|
||||
refetchOnMount: false,
|
||||
})),
|
||||
});
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const updateTokensData = React.useCallback((type: TokenType, payload: AddressTokensBalancesSocketMessage) => {
|
||||
const updateTokensData = React.useCallback((type: TokenType | Array<TokenType>, payload: AddressTokensBalancesSocketMessage) => {
|
||||
const queryKey = getResourceKey('general:address_tokens', { pathParams: { hash }, queryParams: { type } });
|
||||
|
||||
queryClient.setQueryData(queryKey, (prevData: AddressTokensResponse | undefined) => {
|
||||
@@ -72,9 +104,15 @@ export default function useFetchTokens({ hash, enabled }: Props) {
|
||||
});
|
||||
}, [ hash, queryClient ]);
|
||||
|
||||
const additionalTokenTypesIds = React.useMemo(() => {
|
||||
return additionalTokenTypes.map((item) => item.id);
|
||||
}, []);
|
||||
|
||||
const handleTokenBalancesErc20Message: SocketMessage.AddressTokenBalancesErc20['handler'] = React.useCallback((payload) => {
|
||||
updateTokensData('ERC-20', payload);
|
||||
}, [ updateTokensData ]);
|
||||
// udpate ERC-20 & additional token types query, that is used on the address tokens list
|
||||
updateTokensData([ 'ERC-20', ...additionalTokenTypesIds ], payload);
|
||||
}, [ updateTokensData, additionalTokenTypesIds ]);
|
||||
|
||||
const handleTokenBalancesErc721Message: SocketMessage.AddressTokenBalancesErc721['handler'] = React.useCallback((payload) => {
|
||||
updateTokensData('ERC-721', payload);
|
||||
@@ -84,7 +122,7 @@ export default function useFetchTokens({ hash, enabled }: Props) {
|
||||
updateTokensData('ERC-1155', payload);
|
||||
}, [ updateTokensData ]);
|
||||
|
||||
const handleTokenBalancesErc404Message: SocketMessage.AddressTokenBalancesErc1155['handler'] = React.useCallback((payload) => {
|
||||
const handleTokenBalancesErc404Message: SocketMessage.AddressTokenBalancesErc404['handler'] = React.useCallback((payload) => {
|
||||
updateTokensData('ERC-404', payload);
|
||||
}, [ updateTokensData ]);
|
||||
|
||||
@@ -114,7 +152,39 @@ export default function useFetchTokens({ hash, enabled }: Props) {
|
||||
handler: handleTokenBalancesErc404Message,
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!channel || additionalTokenTypes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const refs = additionalTokenTypes.map((item) => {
|
||||
const event = socketEventForTokenType(item.id);
|
||||
|
||||
return channel.on(event, (payload: AddressTokensBalancesSocketMessage) => {
|
||||
updateTokensData(item.id as unknown as TokenType, payload);
|
||||
|
||||
// keep the "ERC-20 & custom token types" list in sync
|
||||
updateTokensData([ 'ERC-20', ...additionalTokenTypesIds ], payload);
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
refs.forEach((ref, index) => {
|
||||
channel.off(socketEventForTokenType(additionalTokenTypes[index]?.id || ''), ref);
|
||||
});
|
||||
};
|
||||
}, [ channel, additionalTokenTypesIds, updateTokensData ]);
|
||||
|
||||
const data = React.useMemo(() => {
|
||||
const additionalGroups = additionalTokenTypes.reduce((result, item, index) => {
|
||||
const query = additionalTokenQueries[index];
|
||||
result[item.id] = {
|
||||
items: query?.data?.items.map(calculateUsdValue) || [],
|
||||
isOverflow: Boolean(query?.data?.next_page_params),
|
||||
};
|
||||
return result;
|
||||
}, {} as Record<string, { items: Array<TokenEnhancedData>; isOverflow: boolean }>);
|
||||
|
||||
return {
|
||||
'ERC-20': {
|
||||
items: erc20query.data?.items.map(calculateUsdValue) || [],
|
||||
@@ -130,14 +200,29 @@ export default function useFetchTokens({ hash, enabled }: Props) {
|
||||
},
|
||||
'ERC-404': {
|
||||
items: erc404query.data?.items.map(calculateUsdValue) || [],
|
||||
isOverflow: Boolean(erc1155query.data?.next_page_params),
|
||||
isOverflow: Boolean(erc404query.data?.next_page_params),
|
||||
},
|
||||
...additionalGroups,
|
||||
};
|
||||
}, [ erc1155query.data, erc20query.data, erc721query.data, erc404query.data ]);
|
||||
}, [ additionalTokenQueries, erc1155query.data, erc20query.data, erc721query.data, erc404query.data ]);
|
||||
|
||||
const isPending =
|
||||
erc20query.isPending ||
|
||||
erc721query.isPending ||
|
||||
erc1155query.isPending ||
|
||||
erc404query.isPending ||
|
||||
additionalTokenQueries.some((query) => query.isPending);
|
||||
|
||||
const isError =
|
||||
erc20query.isError ||
|
||||
erc721query.isError ||
|
||||
erc1155query.isError ||
|
||||
erc404query.isError ||
|
||||
additionalTokenQueries.some((query) => query.isError);
|
||||
|
||||
return {
|
||||
isPending: erc20query.isPending || erc721query.isPending || erc1155query.isPending || erc404query.isPending,
|
||||
isError: erc20query.isError || erc721query.isError || erc1155query.isError || erc404query.isError,
|
||||
isPending,
|
||||
isError,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export const TOKEN = {
|
||||
total_supply: '76923002799740785',
|
||||
type: 'ERC-20' as const,
|
||||
reputation: 'ok' as const,
|
||||
} satisfies TokenInfo<'ERC-20'>;
|
||||
} satisfies TokenInfo;
|
||||
|
||||
const LinkShowcase = () => {
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import AppActionButton from 'ui/shared/AppActionButton/AppActionButton';
|
||||
import useAppActionData from 'ui/shared/AppActionButton/useAppActionData';
|
||||
import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo';
|
||||
import DetailedInfoSponsoredItem from 'ui/shared/DetailedInfo/DetailedInfoSponsoredItem';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
import AssetValue from 'ui/shared/value/AssetValue';
|
||||
|
||||
import TokenNftMarketplaces from './TokenNftMarketplaces';
|
||||
@@ -91,10 +92,27 @@ const TokenDetails = ({ tokenQuery }: Props) => {
|
||||
decimals,
|
||||
symbol,
|
||||
type,
|
||||
zilliqa,
|
||||
} = tokenQuery.data || {};
|
||||
|
||||
return (
|
||||
<DetailedInfo.Container>
|
||||
{ zilliqa?.zrc2_address_hash && (
|
||||
<>
|
||||
<DetailedInfo.ItemLabel
|
||||
hint="ZRC-2 address of the token"
|
||||
isLoading={ tokenQuery.isPlaceholderData }
|
||||
>
|
||||
ZRC-2 Address
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton loading={ tokenQuery.isPlaceholderData } display="inline-block">
|
||||
<AddressEntity address={{ hash: zilliqa.zrc2_address_hash }} isLoading={ tokenQuery.isPlaceholderData }/>
|
||||
</Skeleton>
|
||||
</DetailedInfo.ItemValue>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ exchangeRate && (
|
||||
<>
|
||||
<DetailedInfo.ItemLabel
|
||||
|
||||
@@ -3,6 +3,7 @@ import React from 'react';
|
||||
|
||||
import type { TokenHolder, TokenInfo } from 'types/api/token';
|
||||
|
||||
import { hasTokenIds } from 'lib/token/tokenTypes';
|
||||
import { TruncatedText } from 'toolkit/components/truncation/TruncatedText';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
|
||||
@@ -28,7 +29,7 @@ const TokenHoldersListItem = ({ holder, token, isLoading }: Props) => {
|
||||
/>
|
||||
</ListItemMobileGrid.Value>
|
||||
|
||||
{ (token.type === 'ERC-1155' || token.type === 'ERC-404') && 'token_id' in holder && (
|
||||
{ (hasTokenIds(token.type)) && 'token_id' in holder && (
|
||||
<>
|
||||
<ListItemMobileGrid.Label isLoading={ isLoading }>ID#</ListItemMobileGrid.Label>
|
||||
<ListItemMobileGrid.Value>
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
|
||||
import type { TokenHolder, TokenInfo } from 'types/api/token';
|
||||
|
||||
import { hasTokenIds } from 'lib/token/tokenTypes';
|
||||
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
|
||||
import TokenHoldersTableItem from 'ui/token/TokenHolders/TokenHoldersTableItem';
|
||||
|
||||
@@ -18,7 +19,7 @@ const TokenHoldersTable = ({ data, token, top, isLoading }: Props) => {
|
||||
<TableHeaderSticky top={ top }>
|
||||
<TableRow>
|
||||
<TableColumnHeader w="70%">Holder</TableColumnHeader>
|
||||
{ (token.type === 'ERC-1155' || token.type === 'ERC-404') && <TableColumnHeader w="30%">ID#</TableColumnHeader> }
|
||||
{ (hasTokenIds(token.type)) && <TableColumnHeader w="30%">ID#</TableColumnHeader> }
|
||||
<TableColumnHeader isNumeric width="220px">Quantity</TableColumnHeader>
|
||||
{ token.total_supply && token.type !== 'ERC-404' && <TableColumnHeader isNumeric width="175px">Percentage</TableColumnHeader> }
|
||||
</TableRow>
|
||||
|
||||
@@ -3,6 +3,7 @@ import React from 'react';
|
||||
|
||||
import type { TokenHolder, TokenInfo } from 'types/api/token';
|
||||
|
||||
import { hasTokenIds } from 'lib/token/tokenTypes';
|
||||
import { TableCell, TableRow } from 'toolkit/chakra/table';
|
||||
import { TruncatedText } from 'toolkit/components/truncation/TruncatedText';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
@@ -26,7 +27,7 @@ const TokenTransferTableItem = ({ holder, token, isLoading }: Props) => {
|
||||
fontWeight="700"
|
||||
/>
|
||||
</TableCell>
|
||||
{ (token.type === 'ERC-1155' || token.type === 'ERC-404') && 'token_id' in holder && (
|
||||
{ (hasTokenIds(token.type)) && 'token_id' in holder && (
|
||||
<TableCell verticalAlign="middle">
|
||||
<TruncatedText text={ holder.token_id } loading={ isLoading } w="100%"/>
|
||||
</TableCell>
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { TokenInstance } from 'types/api/token';
|
||||
import type { TokenTransfer } from 'types/api/tokenTransfer';
|
||||
import type { ClusterChainConfig } from 'types/multichain';
|
||||
|
||||
import { NFT_TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
|
||||
import { hasTokenTransferValue, NFT_TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
|
||||
import { Badge } from 'toolkit/chakra/badge';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import { TruncatedText } from 'toolkit/components/truncation/TruncatedText';
|
||||
@@ -63,7 +63,7 @@ const TokenTransferListItem = ({
|
||||
w="100%"
|
||||
fontWeight="500"
|
||||
/>
|
||||
{ total && 'value' in total && token && (token.type === 'ERC-20' || token.type === 'ERC-1155') && (
|
||||
{ total && 'value' in total && token && (hasTokenTransferValue(token.type)) && (
|
||||
<Flex alignItems="center" columnGap={ 2 } maxW="100%">
|
||||
<Skeleton
|
||||
display="inline-flex"
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { TokenTransfer } from 'types/api/tokenTransfer';
|
||||
|
||||
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
|
||||
import { useMultichainContext } from 'lib/contexts/multichain';
|
||||
import { NFT_TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
|
||||
import { hasTokenIds, hasTokenTransferValue, NFT_TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
|
||||
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
|
||||
import { TruncatedText } from 'toolkit/components/truncation/TruncatedText';
|
||||
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
|
||||
@@ -42,9 +42,9 @@ const TokenTransferTable = ({ data, top, showSocketInfo, showSocketErrorAlert, s
|
||||
<TableColumnHeader width="200px">Method</TableColumnHeader>
|
||||
<TableColumnHeader width={{ lg: '224px', xl: '380px' }}>From/To</TableColumnHeader>
|
||||
{ (NFT_TOKEN_TYPE_IDS.includes(tokenType)) &&
|
||||
<TableColumnHeader width={ tokenType === 'ERC-1155' || tokenType === 'ERC-404' ? '50%' : '100%' }>Token ID</TableColumnHeader>
|
||||
<TableColumnHeader width={ hasTokenIds(tokenType) ? '50%' : '100%' }>Token ID</TableColumnHeader>
|
||||
}
|
||||
{ (tokenType === 'ERC-20' || tokenType === 'ERC-1155' || tokenType === 'ERC-404') && (
|
||||
{ hasTokenTransferValue(tokenType) && (
|
||||
<TableColumnHeader width={ tokenType === 'ERC-20' ? '100%' : '50%' } isNumeric>
|
||||
<TruncatedText text={ `Value ${ token?.symbol || '' }` } w="100%" verticalAlign="middle"/>
|
||||
</TableColumnHeader>
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { TokenInstance } from 'types/api/token';
|
||||
import type { TokenTransfer } from 'types/api/tokenTransfer';
|
||||
import type { ClusterChainConfig } from 'types/multichain';
|
||||
|
||||
import { NFT_TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
|
||||
import { hasTokenTransferValue, NFT_TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
|
||||
import { Badge } from 'toolkit/chakra/badge';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import { TableCell, TableRow } from 'toolkit/chakra/table';
|
||||
@@ -92,7 +92,7 @@ const TokenTransferTableItem = ({
|
||||
}
|
||||
</TableCell>
|
||||
) }
|
||||
{ token && (token.type === 'ERC-20' || token.type === 'ERC-1155' || token.type === 'ERC-404') && (
|
||||
{ token && (hasTokenTransferValue(token.type)) && (
|
||||
<TableCell isNumeric verticalAlign="top">
|
||||
<AssetValue
|
||||
amount={ total && 'value' in total ? total.value : null }
|
||||
|
||||
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
import type { TokenTransfer } from 'types/api/tokenTransfer';
|
||||
import type { ClusterChainConfig } from 'types/multichain';
|
||||
|
||||
import { NFT_TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
|
||||
import { hasTokenTransferValue, NFT_TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
|
||||
import { Badge } from 'toolkit/chakra/badge';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
|
||||
@@ -79,7 +79,7 @@ const TokenTransfersListItem = ({ item, isLoading, chainData }: Props) => {
|
||||
</>
|
||||
) }
|
||||
|
||||
{ item.token && item.total && 'value' in item.total && item.total.value !== null && (item.token.type === 'ERC-20' || item.token.type === 'ERC-1155') && (
|
||||
{ item.token && item.total && 'value' in item.total && item.total.value !== null && (hasTokenTransferValue(item.token.type)) && (
|
||||
<>
|
||||
<ListItemMobileGrid.Label isLoading={ isLoading }>Amount</ListItemMobileGrid.Label>
|
||||
<ListItemMobileGrid.Value>
|
||||
|
||||
@@ -8,7 +8,7 @@ import GasPriceValue from 'ui/shared/value/GasPriceValue';
|
||||
import TokenValue from 'ui/shared/value/TokenValue';
|
||||
|
||||
interface Props {
|
||||
gasToken?: TokenInfo<'ERC-20'> | null;
|
||||
gasToken?: TokenInfo | null;
|
||||
gasPrice: string | null;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
@@ -15,10 +15,7 @@ import { FormFieldAddress } from 'toolkit/components/forms/fields/FormFieldAddre
|
||||
import { FormFieldCheckbox } from 'toolkit/components/forms/fields/FormFieldCheckbox';
|
||||
import { FormFieldText } from 'toolkit/components/forms/fields/FormFieldText';
|
||||
|
||||
import AddressFormNotifications from './AddressFormNotifications';
|
||||
|
||||
// does it depend on the network?
|
||||
const NOTIFICATIONS = [ 'native', 'ERC-20', 'ERC-721', 'ERC-404' ] as const;
|
||||
import AddressFormNotifications, { NOTIFICATIONS } from './AddressFormNotifications';
|
||||
|
||||
const TAG_MAX_LENGTH = 35;
|
||||
|
||||
@@ -36,19 +33,7 @@ export type Inputs = {
|
||||
tag: string;
|
||||
notification: boolean;
|
||||
notification_settings: {
|
||||
'native': {
|
||||
outcoming: boolean;
|
||||
incoming: boolean;
|
||||
};
|
||||
'ERC-721': {
|
||||
outcoming: boolean;
|
||||
incoming: boolean;
|
||||
};
|
||||
'ERC-20': {
|
||||
outcoming: boolean;
|
||||
incoming: boolean;
|
||||
};
|
||||
'ERC-404': {
|
||||
[key: string]: {
|
||||
outcoming: boolean;
|
||||
incoming: boolean;
|
||||
};
|
||||
@@ -60,7 +45,9 @@ const AddressForm: React.FC<Props> = ({ data, onSuccess, setAlertVisible, isAdd,
|
||||
|
||||
let notificationsDefault = {} as Inputs['notification_settings'];
|
||||
if (!data?.notification_settings) {
|
||||
NOTIFICATIONS.forEach(n => notificationsDefault[n] = { incoming: hasEmail, outcoming: hasEmail });
|
||||
NOTIFICATIONS.forEach((notificationType) => {
|
||||
notificationsDefault[notificationType] = { incoming: hasEmail, outcoming: hasEmail };
|
||||
});
|
||||
} else {
|
||||
notificationsDefault = data.notification_settings;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Grid, GridItem } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import type { Path } from 'react-hook-form';
|
||||
|
||||
import config from 'configs/app';
|
||||
import { FormFieldCheckbox } from 'toolkit/components/forms/fields/FormFieldCheckbox';
|
||||
@@ -8,21 +9,29 @@ import type { Inputs as FormFields } from './AddressForm';
|
||||
|
||||
const tokenStandardName = config.chain.tokenStandard;
|
||||
|
||||
const NOTIFICATIONS = [ 'native', 'ERC-20', 'ERC-721', 'ERC-404' ] as const;
|
||||
const NOTIFICATIONS_NAMES = [
|
||||
config.chain.currency.symbol,
|
||||
`${ tokenStandardName }-20`,
|
||||
`${ tokenStandardName }-721, ${ tokenStandardName }-1155 (NFT)`,
|
||||
`${ tokenStandardName }-404` ];
|
||||
interface NotificationOption {
|
||||
readonly id: string;
|
||||
readonly label: string;
|
||||
}
|
||||
|
||||
export const NOTIFICATION_OPTIONS: ReadonlyArray<NotificationOption> = [
|
||||
{ id: 'native', label: config.chain.currency.symbol || '' },
|
||||
{ id: 'ERC-20', label: `${ tokenStandardName }-20` },
|
||||
...config.chain.additionalTokenTypes.map((item) => ({ id: item.id, label: item.name })),
|
||||
{ id: 'ERC-721', label: `${ tokenStandardName }-721, ${ tokenStandardName }-1155 (NFT)` },
|
||||
{ id: 'ERC-404', label: `${ tokenStandardName }-404` },
|
||||
];
|
||||
|
||||
export const NOTIFICATIONS = NOTIFICATION_OPTIONS.map(({ id }) => id);
|
||||
|
||||
export default function AddressFormNotifications() {
|
||||
return (
|
||||
<Grid templateColumns={{ base: 'repeat(2, max-content)', lg: 'repeat(3, max-content)' }} gap={{ base: '10px 24px', lg: '20px 24px' }}>
|
||||
{ NOTIFICATIONS.map((notification, index: number) => {
|
||||
const incomingFieldName = `notification_settings.${ notification }.incoming` as const;
|
||||
const outgoingFieldName = `notification_settings.${ notification }.outcoming` as const;
|
||||
{ NOTIFICATION_OPTIONS.map((notification) => {
|
||||
const incomingFieldName = `notification_settings.${ notification.id }.incoming` as Path<FormFields>;
|
||||
const outgoingFieldName = `notification_settings.${ notification.id }.outcoming` as Path<FormFields>;
|
||||
return (
|
||||
<React.Fragment key={ notification }>
|
||||
<React.Fragment key={ notification.id }>
|
||||
<GridItem
|
||||
gridColumnStart={{ base: 1, lg: 1 }}
|
||||
gridColumnEnd={{ base: 3, lg: 1 }}
|
||||
@@ -30,7 +39,7 @@ export default function AddressFormNotifications() {
|
||||
mt: { base: 3, lg: 0 },
|
||||
}}
|
||||
>
|
||||
{ NOTIFICATIONS_NAMES[index] }
|
||||
{ notification.label }
|
||||
</GridItem>
|
||||
<GridItem>
|
||||
<FormFieldCheckbox<FormFields, typeof incomingFieldName>
|
||||
|
||||
@@ -41,8 +41,8 @@ export const ZETA_NATIVE_TOKEN = {
|
||||
icon_url: '',
|
||||
symbol: config.chain.currency.symbol,
|
||||
address_hash: 'native',
|
||||
type: 'ERC-20' as const,
|
||||
} as TokenInfo<'ERC-20'>;
|
||||
type: 'ERC-20',
|
||||
} as TokenInfo;
|
||||
|
||||
// We can't implement multivalue search here, because we have different search params for native coin and for tokens.
|
||||
type Value = TokenInfo | null;
|
||||
|
||||
@@ -82,7 +82,7 @@ const ZetaChainFilterByColumn = ({ column, filters, columnName, handleFilterChan
|
||||
);
|
||||
}
|
||||
case 'asset': {
|
||||
let value: TokenInfo<'ERC-20'> | null = null;
|
||||
let value: TokenInfo | null = null;
|
||||
if (filters.coin_type && castArray(filters.coin_type).includes(ZETA_CHAIN_CCTX_COIN_TYPE_FILTER)) {
|
||||
value = ZETA_NATIVE_TOKEN;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ const ZetaChainFilterByColumn = ({ column, filters, columnName, handleFilterChan
|
||||
decimals: '18',
|
||||
total_supply: '0',
|
||||
icon_url: null,
|
||||
type: 'ERC-20' as const,
|
||||
type: 'ERC-20',
|
||||
holders_count: null,
|
||||
exchange_rate: null,
|
||||
circulating_market_cap: null,
|
||||
|
||||
Reference in New Issue
Block a user