diff --git a/app/components/account/TokenAccountSection.tsx b/app/components/account/TokenAccountSection.tsx index 6efe44205..32007e72f 100644 --- a/app/components/account/TokenAccountSection.tsx +++ b/app/components/account/TokenAccountSection.tsx @@ -14,6 +14,7 @@ import { getCurrentTokenScaledUiAmountMultiplier } from '@utils/token-info'; import { addressLabel } from '@utils/tx'; import { MintAccountInfo, MultisigAccountInfo, TokenAccount, TokenAccountInfo } from '@validators/accounts/token'; import { + ConfidentialMintBurn, ConfidentialTransferAccount, ConfidentialTransferFeeAmount, ConfidentialTransferFeeConfig, @@ -556,6 +557,7 @@ function cmpExtension(a: TokenExtension, b: TokenExtension) { 'confidentialTransferFeeConfig', 'confidentialTransferFeeAmount', 'confidentialTransferMint', + 'confidentialMintBurn', 'interestBearingConfig', 'pausableConfig', 'scaledUiAmountConfig', @@ -723,6 +725,38 @@ export function TokenExtensionRow( ); } + case 'confidentialMintBurn': { + const extension = create(tokenExtension.state, ConfidentialMintBurn); + return ( + <> + {headerStyle === 'header' ? : null} + {extension.confidentialSupply && ( + + Confidential Supply + {extension.confidentialSupply} + + )} + {extension.decryptableSupply && ( + + Decryptable Supply + {extension.decryptableSupply} + + )} + {extension.supplyElgamalPubkey && ( + + Supply Elgamal Pubkey + {extension.supplyElgamalPubkey} + + )} + {extension.pendingBurn && ( + + Pending Burn + {extension.pendingBurn} + + )} + + ); + } case 'confidentialTransferFeeConfig': { const extension = create(tokenExtension.state, ConfidentialTransferFeeConfig); return ( diff --git a/app/components/account/__tests__/TokenExtensionRow.spec.tsx b/app/components/account/__tests__/TokenExtensionRow.spec.tsx index 3c2e4c6ef..382a0a0fb 100644 --- a/app/components/account/__tests__/TokenExtensionRow.spec.tsx +++ b/app/components/account/__tests__/TokenExtensionRow.spec.tsx @@ -123,6 +123,34 @@ describe('TokenExtensionRow', () => { expect(screen.getByText('auto')).toBeInTheDocument(); }); + test('should render confidentialMintBurn extension', async () => { + const data = { + extension: 'confidentialMintBurn', + state: { + confidentialSupply: 'confidential-supply', + decryptableSupply: 'decryptable-supply', + pendingBurn: 'pending-burn', + supplyElgamalPubkey: 'test-pubkey', + }, + } as TokenExtension; + + render( + + + + {TokenExtensionRow(data, undefined, 6, undefined)} + + + + ); + + expect(await screen.findByText('Confidential Mint/Burn')).toBeInTheDocument(); + expect(screen.getByText('Confidential Supply')).toBeInTheDocument(); + expect(screen.getByText('Decryptable Supply')).toBeInTheDocument(); + expect(screen.getByText('Supply Elgamal Pubkey')).toBeInTheDocument(); + expect(screen.getByText('Pending Burn')).toBeInTheDocument(); + }); + test('should render confidentialTransferFeeConfig extension', async () => { const data = { extension: 'confidentialTransferFeeConfig', diff --git a/app/features/metadata/mocks.ts b/app/features/metadata/mocks.ts index b869fb145..82a0e9130 100644 --- a/app/features/metadata/mocks.ts +++ b/app/features/metadata/mocks.ts @@ -155,6 +155,15 @@ export function getTokenExtension(): TokenExtension[] { autoApproveNewAccounts: false, }, }, + { + extension: 'confidentialMintBurn', + state: { + confidentialSupply: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==', + decryptableSupply: 'Ut1TB1Rk4QgH7Ddc3f62LeddYtQ+jlqpAV9YbgfwBVAGIUk8', + pendingBurn: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==', + supplyElgamalPubkey: '9o26anpyrZ+xCtVSPWHy3iARnCPovlneimCHKNQLVks=', + }, + }, { extension: 'confidentialTransferFeeConfig', state: { diff --git a/app/utils/token-extension.ts b/app/utils/token-extension.ts index 2cbd8d1c4..ca219b29e 100644 --- a/app/utils/token-extension.ts +++ b/app/utils/token-extension.ts @@ -176,6 +176,17 @@ export function populatePartialParsedTokenExtension( tooltip: description, }; } + case 'confidentialMintBurn': { + const description = + 'Allow token issuers to opt in to encrypted mint and burn operations, along with encrypted total supply'; + return { + description, + externalLinks: populateExternalLinks('https://spl.solana.com/confidential-token/quickstart'), + name: 'Confidential Mint/Burn', + status: 'active', + tooltip: description, + }; + } case 'interestBearingConfig': { const description = 'Allows the token balance to be displayed with accumulated interest'; return { diff --git a/app/validators/accounts/token-extension.ts b/app/validators/accounts/token-extension.ts index 1bf60e565..7fd1a7da9 100644 --- a/app/validators/accounts/token-extension.ts +++ b/app/validators/accounts/token-extension.ts @@ -8,6 +8,7 @@ const ExtensionType = enums([ 'mintCloseAuthority', 'confidentialTransferMint', 'confidentialTransferAccount', + 'confidentialMintBurn', 'defaultAccountState', 'immutableOwner', 'memoTransfer', @@ -92,6 +93,13 @@ export const ConfidentialTransferMint = type({ autoApproveNewAccounts: boolean(), }); +export const ConfidentialMintBurn = type({ + confidentialSupply: string(), + decryptableSupply: string(), + pendingBurn: string(), + supplyElgamalPubkey: string(), +}); + export const ConfidentialTransferFeeConfig = type({ authority: nullable(PublicKeyFromString), harvestToMintEnabled: boolean(), diff --git a/bench/BUILD.md b/bench/BUILD.md index e19d6c0f8..8e8facef2 100644 --- a/bench/BUILD.md +++ b/bench/BUILD.md @@ -1,32 +1,32 @@ | Type | Route | Size | First Load JS | |------|-------|------|---------------| -| Static | `/` | 15.8 kB | 1.01 MB | -| Static | `/_not-found` | 332 B | 161 kB | +| Static | `/` | 15.7 kB | 1.01 MB | +| Static | `/_not-found` | 334 B | 161 kB | | Dynamic | `/address/[address]` | 11.7 kB | 301 kB | | Dynamic | `/address/[address]/anchor-account` | 5.76 kB | 992 kB | -| Dynamic | `/address/[address]/anchor-program` | 332 B | 161 kB | +| Dynamic | `/address/[address]/anchor-program` | 333 B | 161 kB | | Dynamic | `/address/[address]/attestation` | 6.67 kB | 972 kB | | Dynamic | `/address/[address]/attributes` | 2.49 kB | 931 kB | | Dynamic | `/address/[address]/blockhashes` | 1.88 kB | 926 kB | -| Dynamic | `/address/[address]/compression` | 5.26 kB | 959 kB | +| Dynamic | `/address/[address]/compression` | 5.27 kB | 959 kB | | Dynamic | `/address/[address]/concurrent-merkle-tree` | 3.75 kB | 952 kB | -| Dynamic | `/address/[address]/domains` | 13.9 kB | 933 kB | -| Dynamic | `/address/[address]/entries` | 3.08 kB | 939 kB | +| Dynamic | `/address/[address]/domains` | 13.8 kB | 932 kB | +| Dynamic | `/address/[address]/entries` | 3.09 kB | 939 kB | | Dynamic | `/address/[address]/feature-gate` | 333 B | 161 kB | | Dynamic | `/address/[address]/idl` | 134 kB | 607 kB | | Dynamic | `/address/[address]/instructions` | 2.12 kB | 1.03 MB | | Dynamic | `/address/[address]/metadata` | 3.89 kB | 945 kB | | Dynamic | `/address/[address]/nftoken-collection-nfts` | 5.97 kB | 971 kB | -| Dynamic | `/address/[address]/program-multisig` | 3.4 kB | 995 kB | +| Dynamic | `/address/[address]/program-multisig` | 3.4 kB | 994 kB | | Dynamic | `/address/[address]/rewards` | 3.69 kB | 930 kB | | Dynamic | `/address/[address]/security` | 10.9 kB | 1.02 MB | -| Dynamic | `/address/[address]/slot-hashes` | 3.59 kB | 930 kB | -| Dynamic | `/address/[address]/stake-history` | 3.73 kB | 930 kB | -| Dynamic | `/address/[address]/token-extensions` | 8.67 kB | 990 kB | -| Dynamic | `/address/[address]/tokens` | 7.98 kB | 1.12 MB | +| Dynamic | `/address/[address]/slot-hashes` | 3.6 kB | 930 kB | +| Dynamic | `/address/[address]/stake-history` | 3.74 kB | 930 kB | +| Dynamic | `/address/[address]/token-extensions` | 8.67 kB | 991 kB | +| Dynamic | `/address/[address]/tokens` | 7.97 kB | 1.12 MB | | Dynamic | `/address/[address]/transfers` | 3.52 kB | 1.06 MB | | Dynamic | `/address/[address]/verified-build` | 5.94 kB | 997 kB | -| Dynamic | `/address/[address]/vote-history` | 3.6 kB | 930 kB | +| Dynamic | `/address/[address]/vote-history` | 3.61 kB | 930 kB | | Dynamic | `/api/anchor` | 0 B | 0 B | | Dynamic | `/api/domain-info/[domain]` | 0 B | 0 B | | Static | `/api/metadata/proxy` | 0 B | 0 B | @@ -34,16 +34,16 @@ | Dynamic | `/api/programMetadataIdl` | 0 B | 0 B | | Dynamic | `/api/verified-programs/list/[page]` | 0 B | 0 B | | Dynamic | `/api/verified-programs/metadata/[programId]` | 0 B | 0 B | -| Dynamic | `/block/[slot]` | 10.5 kB | 943 kB | -| Dynamic | `/block/[slot]/accounts` | 4.65 kB | 923 kB | -| Dynamic | `/block/[slot]/programs` | 5.25 kB | 924 kB | -| Dynamic | `/block/[slot]/rewards` | 5.17 kB | 929 kB | -| Dynamic | `/epoch/[epoch]` | 7 kB | 262 kB | -| Static | `/feature-gates` | 3.54 kB | 928 kB | +| Dynamic | `/block/[slot]` | 10.4 kB | 943 kB | +| Dynamic | `/block/[slot]/accounts` | 4.58 kB | 923 kB | +| Dynamic | `/block/[slot]/programs` | 5.16 kB | 924 kB | +| Dynamic | `/block/[slot]/rewards` | 5.09 kB | 928 kB | +| Dynamic | `/epoch/[epoch]` | 6.97 kB | 262 kB | +| Static | `/feature-gates` | 3.46 kB | 928 kB | | Static | `/opengraph-image.png` | 0 B | 0 B | -| Static | `/supply` | 7.08 kB | 930 kB | +| Static | `/supply` | 7 kB | 930 kB | | Static | `/tos` | 332 B | 161 kB | -| Dynamic | `/tx/[signature]` | 34.3 kB | 1.4 MB | -| Dynamic | `/tx/[signature]/inspect` | 610 B | 1.2 MB | -| Static | `/tx/inspector` | 623 B | 1.2 MB | -| Static | `/verified-programs` | 6.13 kB | 169 kB | \ No newline at end of file +| Dynamic | `/tx/[signature]` | 34.2 kB | 1.4 MB | +| Dynamic | `/tx/[signature]/inspect` | 611 B | 1.2 MB | +| Static | `/tx/inspector` | 624 B | 1.2 MB | +| Static | `/verified-programs` | 6.1 kB | 169 kB | \ No newline at end of file