Skip to content

Commit

Permalink
Merge pull request #1320 from blockscout/nft-address
Browse files Browse the repository at this point in the history
Nft address
  • Loading branch information
isstuev authored Dec 5, 2023
2 parents 208183b + 6e80e85 commit 452c77f
Show file tree
Hide file tree
Showing 50 changed files with 755 additions and 504 deletions.
3 changes: 3 additions & 0 deletions icons/collection.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 21 additions & 2 deletions lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import type {
AddressTokensFilter,
AddressTokensResponse,
AddressWithdrawalsResponse,
AddressNFTsResponse,
AddressCollectionsResponse,
AddressNFTTokensFilter,
} from 'types/api/address';
import type { AddressesResponse } from 'types/api/addresses';
import type { BlocksResponse, BlockTransactionsResponse, Block, BlockFilters, BlockWithdrawalsResponse } from 'types/api/block';
Expand All @@ -51,6 +54,7 @@ import type {
TokenInstance,
TokenInstanceTransfersCount,
TokenVerifiedInfo,
TokenInventoryFilters,
} from 'types/api/token';
import type { TokensResponse, TokensFilters, TokensSorting, TokenInstanceTransferResponse, TokensBridgedFilters } from 'types/api/tokens';
import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer';
Expand Down Expand Up @@ -305,6 +309,16 @@ export const RESOURCES = {
pathParams: [ 'hash' as const ],
filterFields: [ 'type' as const ],
},
address_nfts: {
path: '/api/v2/addresses/:hash/nft',
pathParams: [ 'hash' as const ],
filterFields: [ 'type' as const ],
},
address_collections: {
path: '/api/v2/addresses/:hash/nft/collections',
pathParams: [ 'hash' as const ],
filterFields: [ 'type' as const ],
},
address_withdrawals: {
path: '/api/v2/addresses/:hash/withdrawals',
pathParams: [ 'hash' as const ],
Expand Down Expand Up @@ -384,7 +398,7 @@ export const RESOURCES = {
token_inventory: {
path: '/api/v2/tokens/:hash/instances',
pathParams: [ 'hash' as const ],
filterFields: [],
filterFields: [ 'holder_address_hash' as const ],
},
tokens: {
path: '/api/v2/tokens',
Expand Down Expand Up @@ -580,7 +594,7 @@ export type PaginatedResources = 'blocks' | 'block_txs' |
'addresses' |
'address_txs' | 'address_internal_txs' | 'address_token_transfers' | 'address_blocks_validated' | 'address_coin_balance' |
'search' |
'address_logs' | 'address_tokens' |
'address_logs' | 'address_tokens' | 'address_nfts' | 'address_collections' |
'token_transfers' | 'token_holders' | 'token_inventory' | 'tokens' | 'tokens_bridged' |
'token_instance_transfers' | 'token_instance_holders' |
'verified_contracts' |
Expand Down Expand Up @@ -642,6 +656,8 @@ Q extends 'address_coin_balance' ? AddressCoinBalanceHistoryResponse :
Q extends 'address_coin_balance_chart' ? AddressCoinBalanceHistoryChart :
Q extends 'address_logs' ? LogsResponseAddress :
Q extends 'address_tokens' ? AddressTokensResponse :
Q extends 'address_nfts' ? AddressNFTsResponse :
Q extends 'address_collections' ? AddressCollectionsResponse :
Q extends 'address_withdrawals' ? AddressWithdrawalsResponse :
Q extends 'token' ? TokenInfo :
Q extends 'token_verified_info' ? TokenVerifiedInfo :
Expand Down Expand Up @@ -695,7 +711,10 @@ Q extends 'token_transfers' ? TokenTransferFilters :
Q extends 'address_txs' | 'address_internal_txs' ? AddressTxsFilters :
Q extends 'address_token_transfers' ? AddressTokenTransferFilters :
Q extends 'address_tokens' ? AddressTokensFilter :
Q extends 'address_nfts' ? AddressNFTTokensFilter :
Q extends 'address_collections' ? AddressNFTTokensFilter :
Q extends 'search' ? SearchResultFilters :
Q extends 'token_inventory' ? TokenInventoryFilters :
Q extends 'tokens' ? TokensFilters :
Q extends 'tokens_bridged' ? TokensBridgedFilters :
Q extends 'verified_contracts' ? VerifiedContractsFilters :
Expand Down
1 change: 1 addition & 0 deletions lib/cookies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum NAMES {
INDEXING_ALERT='indexing_alert',
ADBLOCK_DETECTED='adblock_detected',
MIXPANEL_DEBUG='_mixpanel_debug',
ADDRESS_NFT_DISPLAY_TYPE='address_nft_display_type'
}

export function get(name?: NAMES | undefined | null, serverCookie?: string) {
Expand Down
11 changes: 8 additions & 3 deletions lib/token/tokenTypes.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import type { TokenType } from 'types/api/token';
import type { NFTTokenType, TokenType } from 'types/api/token';

export const TOKEN_TYPES: Array<{ title: string; id: TokenType }> = [
{ title: 'ERC-20', id: 'ERC-20' },
export const NFT_TOKEN_TYPES: Array<{ title: string; id: NFTTokenType }> = [
{ title: 'ERC-721', id: 'ERC-721' },
{ title: 'ERC-1155', id: 'ERC-1155' },
];

export const TOKEN_TYPES: Array<{ title: string; id: TokenType }> = [
{ title: 'ERC-20', id: 'ERC-20' },
...NFT_TOKEN_TYPES,
];

export const NFT_TOKEN_TYPE_IDS = NFT_TOKEN_TYPES.map(i => i.id);
export const TOKEN_TYPE_IDS = TOKEN_TYPES.map(i => i.id);
50 changes: 49 additions & 1 deletion mocks/address/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AddressTokenBalance } from 'types/api/address';
import type { AddressCollectionsResponse, AddressNFTsResponse, AddressTokenBalance } from 'types/api/address';

import * as tokens from 'mocks/tokens/tokenInfo';
import * as tokenInstance from 'mocks/tokens/tokenInstance';
Expand Down Expand Up @@ -117,3 +117,51 @@ export const erc1155List = {
erc1155b,
],
};

export const nfts: AddressNFTsResponse = {
items: [
{
...tokenInstance.base,
token: tokens.tokenInfoERC1155a,
token_type: 'ERC-1155',
value: '11',
},
{
...tokenInstance.unique,
token: tokens.tokenInfoERC721a,
token_type: 'ERC-721',
value: '1',
},
],
next_page_params: null,
};

const nftInstance = {
...tokenInstance.base,
token_type: 'ERC-1155',
value: '11',
};

export const collections: AddressCollectionsResponse = {
items: [
{
token: tokens.tokenInfoERC1155a,
amount: '100',
token_instances: Array(5).fill(nftInstance),
},
{
token: tokens.tokenInfoERC20LongSymbol,
amount: '100',
token_instances: Array(5).fill(nftInstance),
},
{
token: tokens.tokenInfoERC1155WithoutName,
amount: '1',
token_instances: [ nftInstance ],
},
],
next_page_params: {
token_contract_address_hash: '123',
token_type: 'ERC-1155',
},
};
32 changes: 23 additions & 9 deletions stubs/address.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import type { Address, AddressCoinBalanceHistoryItem, AddressCounters, AddressTabsCounters, AddressTokenBalance } from 'types/api/address';
import type {
Address,
AddressCoinBalanceHistoryItem,
AddressCollection,
AddressCounters,
AddressNFT,
AddressTabsCounters,
AddressTokenBalance,
} from 'types/api/address';
import type { AddressesItem } from 'types/api/addresses';

import { ADDRESS_HASH } from './addressParams';
Expand Down Expand Up @@ -80,16 +88,22 @@ export const ADDRESS_TOKEN_BALANCE_ERC_20: AddressTokenBalance = {
value: '1000000000000000000000000',
};

export const ADDRESS_TOKEN_BALANCE_ERC_721: AddressTokenBalance = {
export const ADDRESS_NFT_721: AddressNFT = {
token_type: 'ERC-721',
token: TOKEN_INFO_ERC_721,
token_id: null,
token_instance: null,
value: '176',
value: '1',
...TOKEN_INSTANCE,
};

export const ADDRESS_NFT_1155: AddressNFT = {
token_type: 'ERC-1155',
token: TOKEN_INFO_ERC_1155,
value: '10',
...TOKEN_INSTANCE,
};

export const ADDRESS_TOKEN_BALANCE_ERC_1155: AddressTokenBalance = {
export const ADDRESS_COLLECTION: AddressCollection = {
token: TOKEN_INFO_ERC_1155,
token_id: '188882',
token_instance: TOKEN_INSTANCE,
value: '176',
amount: '4',
token_instances: Array(4).fill(TOKEN_INSTANCE),
};
38 changes: 36 additions & 2 deletions types/api/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Transaction } from 'types/api/transaction';
import type { UserTags } from './addressParams';
import type { Block } from './block';
import type { InternalTransaction } from './internalTransaction';
import type { TokenInfo, TokenInstance, TokenType } from './token';
import type { NFTTokenType, TokenInfo, TokenInstance, TokenType } from './token';
import type { TokenTransfer, TokenTransferPagination } from './tokenTransfer';

export interface Address extends UserTags {
Expand Down Expand Up @@ -49,17 +49,47 @@ export interface AddressTokenBalance {
token_instance: TokenInstance | null;
}

export type AddressNFT = TokenInstance & {
token: TokenInfo;
token_type: Omit<TokenType, 'ERC-20'>;
value: string;
}

export type AddressCollection = {
token: TokenInfo;
amount: string;
token_instances: Array<Omit<AddressNFT, 'token'>>;
}

export interface AddressTokensResponse {
items: Array<AddressTokenBalance>;
next_page_params: {
items_count: number;
token_name: 'string' | null;
token_name: string | null;
token_type: TokenType;
value: number;
fiat_value: string | null;
} | null;
}

export interface AddressNFTsResponse {
items: Array<AddressNFT>;
next_page_params: {
items_count: number;
token_id: string;
token_type: TokenType;
token_contract_address_hash: string;
} | null;
}

export interface AddressCollectionsResponse {
items: Array<AddressCollection>;
next_page_params: {
token_contract_address_hash: string;
token_type: TokenType;
} | null;
}

export interface AddressTokensBalancesSocketMessage {
overflow: boolean;
token_balances: Array<AddressTokenBalance>;
Expand Down Expand Up @@ -97,6 +127,10 @@ export type AddressTokensFilter = {
type: TokenType;
}

export type AddressNFTTokensFilter = {
type: Array<NFTTokenType> | undefined;
}

export interface AddressCoinBalanceHistoryItem {
block_number: number;
block_timestamp: string;
Expand Down
7 changes: 6 additions & 1 deletion types/api/token.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { TokenInfoApplication } from './account';
import type { AddressParam } from './addressParams';

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

export interface TokenInfo<T extends TokenType = TokenType> {
address: string;
Expand Down Expand Up @@ -77,3 +78,7 @@ export type TokenInventoryPagination = {
}

export type TokenVerifiedInfo = Omit<TokenInfoApplication, 'id' | 'status'>;

export type TokenInventoryFilters = {
holder_address_hash?: string;
}
21 changes: 3 additions & 18 deletions ui/address/AddressTokenTransfers.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Flex, Hide, Icon, Show, Text, Tooltip, useColorModeValue } from '@chakra-ui/react';
import { Flex, Hide, Show, Text } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react';
Expand All @@ -9,7 +9,6 @@ import type { AddressFromToFilter, AddressTokenTransferResponse } from 'types/ap
import type { TokenType } from 'types/api/token';
import type { TokenTransfer } from 'types/api/tokenTransfer';

import crossIcon from 'icons/cross.svg';
import { getResourceKey } from 'lib/api/useApiQuery';
import getFilterValueFromQuery from 'lib/getFilterValueFromQuery';
import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery';
Expand All @@ -26,6 +25,7 @@ import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import HashStringShorten from 'ui/shared/HashStringShorten';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import ResetIconButton from 'ui/shared/ResetIconButton';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter';
import TokenTransferList from 'ui/shared/TokenTransfer/TokenTransferList';
Expand Down Expand Up @@ -115,9 +115,6 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Pr
onFilterChange({});
}, [ onFilterChange ]);

const resetTokenIconColor = useColorModeValue('blue.600', 'blue.300');
const resetTokenIconHoverColor = useColorModeValue('blue.400', 'blue.200');

const handleNewSocketMessage: SocketMessage.AddressTokenTransfer['handler'] = (payload) => {
setSocketAlert('');

Expand Down Expand Up @@ -235,19 +232,7 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Pr
<Flex alignItems="center" py={ 1 }>
<TokenEntity.Icon token={ tokenData } isLoading={ isPlaceholderData }/>
{ isMobile ? <HashStringShorten hash={ tokenFilter }/> : tokenFilter }
<Tooltip label="Reset filter">
<Flex>
<Icon
as={ crossIcon }
boxSize={ 5 }
ml={ 1 }
color={ resetTokenIconColor }
cursor="pointer"
_hover={{ color: resetTokenIconHoverColor }}
onClick={ resetTokenFilter }
/>
</Flex>
</Tooltip>
<ResetIconButton onClick={ resetTokenFilter }/>
</Flex>
</Flex>
);
Expand Down
Loading

0 comments on commit 452c77f

Please sign in to comment.