Skip to content

Conversation

@kroist
Copy link

@kroist kroist commented Nov 18, 2025

Description and Related Issue(s)

This PR integrates ERC-7984 confidential tokens introduced for backend in blockscout/blockscout#13593 .

One main difference between ERC-20 and ERC-7984 is that token balances are not known to public (because of their encrypted nature), that's why we show "•••••" placeholder instead of the actual value (in future improvements confidential protocol integrations, these values might be decrypted with the wallet having a decryption access).

Proposed Changes

A new type of token, ERC-7984, which mostly works as ERC-20 excluding encrypted balances, which was described earlier.

Additional Information

ERC-7984 readings:


Note

Medium Risk
Touches many token/transfer UI surfaces and realtime socket update wiring; while mostly additive, regressions could affect token balances/transfers rendering and pagination/socket refresh behavior.

Overview
Adds ERC-7984 (confidential token) as a first-class token type, including socket event typing (updated_token_balances_erc_7984) and token-type helpers so ERC-7984 participates in token-type labels and transfer-value handling.

Extends the Address Tokens UI with a dedicated ${tokenStandard}-7984 tab and new list/table components, wires ERC-7984 into token fetching + socket updates, and updates token selectors/grouping/sorting to include the new type.

Updates multiple transfer/holders views (token transfers tables/lists/snippets, advanced filter amount column, token holders) to render masked placeholders via new ConfidentialValue/ConfidentialTokenValue components when ERC-7984 amounts are unavailable, and adds a new quick-search category for confidential tokens.

Written by Cursor Bugbot for commit 8ea5a5b. This will update automatically on new commits. Configure here.

@kroist kroist marked this pull request as ready for review November 18, 2025 23:39
@tom2drum tom2drum added the triage Issues and PRs that are needed triage label Dec 23, 2025
@tom2drum
Copy link
Collaborator

tom2drum commented Feb 9, 2026

bugbot run

@tom2drum tom2drum removed the triage Issues and PRs that are needed triage label Feb 9, 2026
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

This PR is being reviewed by Cursor Bugbot

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

decimals={ token.decimals ?? '0' }
loading={ isLoading }
/>
) }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holder percentage computed from confidential encrypted balance

High Severity

For ERC-7984 tokens, the "Quantity" column correctly renders ConfidentialValue (encrypted placeholder), but the "Percentage" section still computes BigNumber(holder.value).div(BigNumber(token.total_supply)). Since holder.value is confidential/encrypted for ERC-7984, this calculation will produce NaN or nonsensical results. The condition token.total_supply && token.type !== 'ERC-404' needs to also exclude 'ERC-7984' in both TokenHoldersTableItem and TokenHoldersListItem.

Additional Locations (1)

Fix in Cursor Fix in Web

type TokenGroup = [string, TokenSelectDataItem];

const NFT_TOKEN_GROUPS_ORDER = [ 'ERC-721', 'ERC-1155', 'ERC-404' ] as const;
const NFT_TOKEN_GROUPS_ORDER = [ 'ERC-721', 'ERC-1155', 'ERC-404', 'ERC-7984' ] as const;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ERC-7984 misplaced in NFT token groups constant

Low Severity

ERC-7984 is a confidential fungible token (akin to ERC-20), but it's been added to NFT_TOKEN_GROUPS_ORDER alongside ERC-721, ERC-1155, and ERC-404. While the sort order works correctly, this naming is misleading — the constant's name implies it contains only NFT types, which makes future maintenance confusing.

Fix in Cursor Fix in Web

flexGrow={ 1 }
/>
</Flex>
) }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate value rows rendered for ERC-7984 transfers

Medium Severity

The ERC-7984 ConfidentialValue block at line 91 renders unconditionally for ERC-7984 tokens (token && token.type === 'ERC-7984'), while the existing AssetValue block at line 67 also matches ERC-7984 because hasTokenTransferValue returns true for it and doesn't check total.value !== null. Both blocks render simultaneously, producing duplicate "Value" rows. The shared version in ui/shared/TokenTransfer/TokenTransferListItem.tsx correctly uses mutually exclusive conditions (total.value !== null vs !total || total.value === null).

Additional Locations (1)

Fix in Cursor Fix in Web

Comment on lines +47 to +50
} |
{
token: TokenInfo | null;
total: Erc20TotalPayload | null;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This object is already part of the union. Is this change intentional?

Comment on lines +69 to +80
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 }),
},
});

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we can add this token type to erc20Query without needing to create a separate tab in the UI. The table structure for these token types is similar.

tokensQuery: QueryWithPagesResult<'general:address_tokens'>;
};

const ERC7984Tokens = ({ tokensQuery }: Props) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume we can re-use the component for ERC20 tokens here.

Comment on lines +26 to +28
<Skeleton loading={ loading } display="inline-block">
•••••
</Skeleton>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<Skeleton loading={ loading } display="inline-block">
•••••
</Skeleton>
<ConfidentialValue loading={ loading }>

Comment on lines +95 to +101
<ConfidentialValue loading={ false } wordBreak="break-word"/>
<TokenEntity
token={{ ...data.token, name: data.token.symbol || data.token.name }}
noCopy
noSymbol
w="auto"
/>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<ConfidentialValue loading={ false } wordBreak="break-word"/>
<TokenEntity
token={{ ...data.token, name: data.token.symbol || data.token.name }}
noCopy
noSymbol
w="auto"
/>
<ConfidentialTokenValue .../>

alignItems={ layout === 'vertical' ? 'flex-end' : 'center' }
{ ...rest }
>
<Flex alignItems="center">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this wrapper is not needed.

Comment on lines 88 to 93
<TokenValue
amount={ item.total.value }
token={ item.token }
decimals={ item.total.decimals || '0' }
loading={ isLoading }
/>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{ isErc20 ? <ConfidentialTokenValue ... /> : <TokenValue .../>


export const TOKEN_TYPES: Record<string, string> = {
'ERC-20': `${ tokenStandardName }-20`,
'ERC-7984': `${ tokenStandardName }-7984`,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed internally, we would like this token type to appear only in certain instances or chains. Therefore, let's remove the hard-coded value and pass it as an additional token type via the NEXT_PUBLIC_NETWORK_ADDITIONAL_TOKEN_TYPES run-time environment variable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants