Skip to content

Commit b649d04

Browse files
committed
refactor(mango): generic deprecated card + tx-history instruction naming
1 parent 9567679 commit b649d04

16 files changed

Lines changed: 255 additions & 102 deletions

File tree

app/features/instruction-program-mango/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

app/features/instruction-program-mango/ui/MangoDetailsCard.tsx

Lines changed: 0 additions & 41 deletions
This file was deleted.

app/features/transaction-history/model/use-resolved-instruction-summaries.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { type ProgramIdlNames, useInstructionNameResolvers } from '@entities/idl
22
import { resolveLighthouseInstructionName } from '@entities/lighthouse';
33
import { type InstructionNameLookup, type InstructionSummary } from '@entities/transaction-data';
44
import { resolveZkElGamalProofName } from '@entities/zk-elgamal-proof';
5+
import { resolveMangoInstructionName } from '@explorer/decoder-mango/detection';
56
import { useCluster } from '@providers/cluster';
67
import { useMemo } from 'react';
78

@@ -47,6 +48,7 @@ type NameSource = (lookup: InstructionNameLookup, idlNames: Map<string, ProgramI
4748
const NAME_SOURCES: NameSource[] = [
4849
({ programId, discriminator }) => resolveZkElGamalProofName(programId, discriminator),
4950
({ programId, discriminator }) => resolveLighthouseInstructionName(programId, discriminator),
51+
({ programId, discriminator }) => resolveMangoInstructionName(programId, discriminator),
5052
({ programId, discriminator }, idlNames) => idlNames.get(programId)?.resolveInstructionName?.(discriminator),
5153
];
5254

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { InstructionCard } from '@components/instruction/InstructionCard';
2+
import { useCluster } from '@providers/cluster';
3+
import { ParsedInstruction, SignatureResult, TransactionInstruction } from '@solana/web3.js';
4+
import { getProgramName } from '@utils/tx';
5+
import React from 'react';
6+
7+
// Card for instructions whose program we recognize but whose payload we don't decode; the caller supplies the resolved name.
8+
export function CommonInstructionDetailsCard({
9+
ix,
10+
index,
11+
result,
12+
innerCards,
13+
childIndex,
14+
instructionName,
15+
}: {
16+
ix: TransactionInstruction | ParsedInstruction;
17+
index: number;
18+
result: SignatureResult;
19+
innerCards?: React.ReactNode[];
20+
childIndex?: number;
21+
instructionName: string;
22+
}) {
23+
const { cluster } = useCluster();
24+
const programName = getProgramName(ix.programId.toBase58(), cluster);
25+
return (
26+
<InstructionCard
27+
ix={ix}
28+
index={index}
29+
result={result}
30+
title={`${programName}: ${instructionName}`}
31+
innerCards={innerCards}
32+
childIndex={childIndex}
33+
defaultRaw
34+
/>
35+
);
36+
}

app/features/transaction/ui/InstructionsSection.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import { ZkElGamalProofDetailsCard } from '@components/instruction/ZkElGamalProo
3131
import { useAnchorProgram } from '@entities/idl';
3232
import { isParsedInstruction, useInstructionParser } from '@entities/instruction-parser';
3333
import { isZkElGamalProofInstruction } from '@entities/zk-elgamal-proof';
34-
import { isMangoInstruction } from '@explorer/decoder-mango/detection';
34+
import { getMangoInstructionLabel, isMangoInstruction } from '@explorer/decoder-mango/detection';
3535
import { isLighthouseInstruction, LighthouseDetailsCard } from '@features/decode-instruction-lighthouse';
3636
import { MetaplexTokenMetadataDetailsCard } from '@features/mpl-token-metadata';
3737
import { isStakeInstruction, RawStakeDetailsCard, StakeDetailsCard } from '@features/stake';
@@ -53,18 +53,13 @@ import {
5353
import { Cluster } from '@utils/cluster';
5454
import { INNER_INSTRUCTIONS_START_SLOT, SignatureProps } from '@utils/index';
5555
import { intoTransactionInstruction } from '@utils/tx';
56-
import dynamic from 'next/dynamic';
5756
import React from 'react';
5857
import { ErrorBoundary } from 'react-error-boundary';
5958

6059
import { useProgramMetadataIdl } from '@/app/entities/program-metadata';
6160

6261
import { CollapsibleSection } from './CollapsibleSection';
63-
64-
const MangoDetailsCard = dynamic(
65-
() => import('@features/instruction-program-mango').then(mod => mod.MangoDetailsCard),
66-
{ ssr: false },
67-
);
62+
import { CommonInstructionDetailsCard } from './CommonInstructionDetailsCard';
6863

6964
export type InstructionDetailsProps = {
7065
tx: ParsedTransaction;
@@ -245,7 +240,13 @@ function InstructionCard({
245240
return <Ed25519DetailsCard key={key} {...props} tx={tx} />;
246241
}
247242
if (isMangoInstruction(transactionIx)) {
248-
return <MangoDetailsCard key={key} {...props} />;
243+
return (
244+
<CommonInstructionDetailsCard
245+
key={key}
246+
{...props}
247+
instructionName={getMangoInstructionLabel(transactionIx)}
248+
/>
249+
);
249250
}
250251
if (isSerumInstruction(transactionIx)) {
251252
return <SerumDetailsCard key={key} {...props} />;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { PublicKey, type TransactionInstruction } from '@solana/web3.js';
2+
import {
3+
nextjsParameters,
4+
withCluster,
5+
withMockTransactions,
6+
withScrollAnchor,
7+
withTokenInfoBatch,
8+
} from '@storybook-config/decorators';
9+
import type { Meta, StoryObj } from '@storybook-config/types';
10+
11+
import { CommonInstructionDetailsCard } from '../CommonInstructionDetailsCard';
12+
13+
// Renders "<program label>: <instruction>" over the raw instruction data — the label resolved from the program
14+
// registry, the instruction name supplied by the caller.
15+
function makeIx(programId: string, data: number[]): TransactionInstruction {
16+
return {
17+
data: Buffer.from(data),
18+
keys: [],
19+
programId: new PublicKey(programId),
20+
} as unknown as TransactionInstruction;
21+
}
22+
23+
const meta: Meta<typeof CommonInstructionDetailsCard> = {
24+
component: CommonInstructionDetailsCard,
25+
decorators: [withCluster, withScrollAnchor, withTokenInfoBatch, withMockTransactions],
26+
parameters: nextjsParameters,
27+
tags: ['autodocs', 'test'],
28+
title: 'Features/Transaction/CommonInstructionDetailsCard',
29+
};
30+
31+
export default meta;
32+
type Story = StoryObj<typeof meta>;
33+
34+
// A program the registry names.
35+
export const RecognizedProgram: Story = {
36+
args: {
37+
childIndex: undefined,
38+
index: 0,
39+
innerCards: undefined,
40+
instructionName: 'Advance Nonce Account',
41+
ix: makeIx('11111111111111111111111111111111', [4, 0, 0, 0]),
42+
result: { err: null },
43+
},
44+
};
45+
46+
// A program the registry doesn't know: the label falls back to "Unknown Program (address)".
47+
export const UnrecognizedProgram: Story = {
48+
args: {
49+
childIndex: undefined,
50+
index: 0,
51+
innerCards: undefined,
52+
instructionName: 'Deposit',
53+
ix: makeIx('So11111111111111111111111111111111111111112', [1, 0, 0, 0]),
54+
result: { err: null },
55+
},
56+
};

app/utils/instruction.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export function getTokenInstructionName(
9494

9595
if (transactionInstruction) {
9696
try {
97+
// Mango is intentionally absent: its resolver lives in the modern NAME_SOURCES chain; consolidating this legacy resolver is a follow-up.
9798
if (isSerumInstruction(transactionInstruction)) {
9899
return parseSerumInstructionTitle(transactionInstruction);
99100
} else if (isTokenSwapInstruction(transactionInstruction)) {

app/utils/programs.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { MANGO_V3_PROGRAM_LABEL } from '@explorer/decoder-mango';
12
import { TOKEN_PROGRAM_ADDRESS } from '@solana-program/token';
23
import { TOKEN_2022_PROGRAM_ADDRESS } from '@solana-program/token-2022';
34

@@ -61,9 +62,6 @@ export enum PROGRAM_NAMES {
6162
WORMHOLE_NFT = 'Wormhole NFT Bridge',
6263
OPENBOOK_DEX = 'OpenBook Dex',
6364

64-
// Mango (dead protocol; recognized but no longer decoded)
65-
MANGO = 'Mango (deprecated)',
66-
6765
// ZK Compression
6866
ZK_LIGHT_SYSTEM_PROGRAM = 'Light System Program',
6967
ZK_COMPRESSED_TOKEN_PROGRAM = 'ZK Compressed Token Program',
@@ -120,7 +118,7 @@ export const PROGRAM_INFO_BY_ID: { [address: string]: ProgramInfo } = {
120118
},
121119
'4skJ85cdxQAFVKbcGgfun8iZPL7BadVYXG3kGEGkufqA': {
122120
deployments: [Cluster.Devnet],
123-
name: PROGRAM_NAMES.MANGO,
121+
name: MANGO_V3_PROGRAM_LABEL,
124122
},
125123
'675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8': {
126124
deployments: [Cluster.MainnetBeta],
@@ -146,7 +144,7 @@ export const PROGRAM_INFO_BY_ID: { [address: string]: ProgramInfo } = {
146144
},
147145
BXhdkETgbHrr5QmVBT1xbz3JrMM28u5djbVtmTUfmFTH: {
148146
deployments: [Cluster.Testnet],
149-
name: PROGRAM_NAMES.MANGO,
147+
name: MANGO_V3_PROGRAM_LABEL,
150148
},
151149
ComputeBudget111111111111111111111111111111: {
152150
deployments: ALL_CLUSTERS,
@@ -306,7 +304,7 @@ export const PROGRAM_INFO_BY_ID: { [address: string]: ProgramInfo } = {
306304
},
307305
mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68: {
308306
deployments: [Cluster.MainnetBeta],
309-
name: PROGRAM_NAMES.MANGO,
307+
name: MANGO_V3_PROGRAM_LABEL,
310308
},
311309
namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX: {
312310
deployments: LIVE_CLUSTERS,

bench/BUILD.md

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,56 +4,56 @@
44
|------|-------|------|---------------|
55
| Static | `/` | 130 kB | 1.15 MB |
66
| Static | `/_not-found` | 0 B | 1.03 MB |
7-
| Dynamic | `/address/[address]` | 440 kB | 1.45 MB |
8-
| Dynamic | `/address/[address]/anchor-account` | 390 kB | 1.41 MB |
9-
| Dynamic | `/address/[address]/anchor-program` | 390 kB | 1.40 MB |
10-
| Dynamic | `/address/[address]/attestation` | 390 kB | 1.41 MB |
11-
| Dynamic | `/address/[address]/attributes` | 390 kB | 1.41 MB |
12-
| Dynamic | `/address/[address]/blockhashes` | 390 kB | 1.41 MB |
13-
| Dynamic | `/address/[address]/compression` | 390 kB | 1.41 MB |
14-
| Dynamic | `/address/[address]/concurrent-merkle-tree` | 390 kB | 1.41 MB |
15-
| Dynamic | `/address/[address]/domains` | 390 kB | 1.41 MB |
16-
| Dynamic | `/address/[address]/entries` | 390 kB | 1.41 MB |
17-
| Dynamic | `/address/[address]/feature-gate` | 390 kB | 1.40 MB |
18-
| Dynamic | `/address/[address]/idl` | 530 kB | 1.54 MB |
19-
| Dynamic | `/address/[address]/instructions` | 440 kB | 1.45 MB |
20-
| Dynamic | `/address/[address]/metadata` | 390 kB | 1.41 MB |
21-
| Dynamic | `/address/[address]/nftoken-collection-nfts` | 390 kB | 1.41 MB |
22-
| Dynamic | `/address/[address]/program-multisig` | 390 kB | 1.41 MB |
23-
| Dynamic | `/address/[address]/rewards` | 390 kB | 1.41 MB |
24-
| Dynamic | `/address/[address]/security` | 390 kB | 1.41 MB |
25-
| Dynamic | `/address/[address]/slot-hashes` | 390 kB | 1.41 MB |
26-
| Dynamic | `/address/[address]/stake-history` | 390 kB | 1.41 MB |
27-
| Dynamic | `/address/[address]/token-extensions` | 390 kB | 1.41 MB |
28-
| Dynamic | `/address/[address]/tokens` | 520 kB | 1.54 MB |
29-
| Dynamic | `/address/[address]/transfers` | 440 kB | 1.45 MB |
30-
| Dynamic | `/address/[address]/verified-build` | 390 kB | 1.41 MB |
31-
| Dynamic | `/address/[address]/vote-history` | 390 kB | 1.41 MB |
32-
| Dynamic | `/api/anchor` |||
7+
| Dynamic | `/address/[address]` | 380 kB | 1.40 MB |
8+
| Dynamic | `/address/[address]/anchor-account` | 370 kB | 1.39 MB |
9+
| Dynamic | `/address/[address]/anchor-program` | 370 kB | 1.39 MB |
10+
| Dynamic | `/address/[address]/attestation` | 370 kB | 1.39 MB |
11+
| Dynamic | `/address/[address]/attributes` | 370 kB | 1.39 MB |
12+
| Dynamic | `/address/[address]/blockhashes` | 370 kB | 1.39 MB |
13+
| Dynamic | `/address/[address]/compression` | 370 kB | 1.39 MB |
14+
| Dynamic | `/address/[address]/concurrent-merkle-tree` | 370 kB | 1.39 MB |
15+
| Dynamic | `/address/[address]/domains` | 370 kB | 1.39 MB |
16+
| Dynamic | `/address/[address]/entries` | 370 kB | 1.39 MB |
17+
| Dynamic | `/address/[address]/feature-gate` | 370 kB | 1.39 MB |
18+
| Dynamic | `/address/[address]/idl` | 510 kB | 1.53 MB |
19+
| Dynamic | `/address/[address]/instructions` | 420 kB | 1.44 MB |
20+
| Dynamic | `/address/[address]/metadata` | 370 kB | 1.39 MB |
21+
| Dynamic | `/address/[address]/nftoken-collection-nfts` | 370 kB | 1.39 MB |
22+
| Dynamic | `/address/[address]/program-multisig` | 370 kB | 1.39 MB |
23+
| Dynamic | `/address/[address]/rewards` | 370 kB | 1.39 MB |
24+
| Dynamic | `/address/[address]/security` | 380 kB | 1.39 MB |
25+
| Dynamic | `/address/[address]/slot-hashes` | 370 kB | 1.39 MB |
26+
| Dynamic | `/address/[address]/stake-history` | 370 kB | 1.39 MB |
27+
| Dynamic | `/address/[address]/token-extensions` | 380 kB | 1.39 MB |
28+
| Dynamic | `/address/[address]/tokens` | 430 kB | 1.44 MB |
29+
| Dynamic | `/address/[address]/transfers` | 420 kB | 1.44 MB |
30+
| Dynamic | `/address/[address]/verified-build` | 370 kB | 1.39 MB |
31+
| Dynamic | `/address/[address]/vote-history` | 370 kB | 1.39 MB |
3332
| Dynamic | `/api/ans-domains/[address]` |||
3433
| Dynamic | `/api/domain-info/[domain]` |||
3534
| Dynamic | `/api/geo-location` |||
35+
| Dynamic | `/api/idl-latest` |||
3636
| Dynamic | `/api/metadata/proxy` |||
3737
| Dynamic | `/api/ping/[network]` |||
38-
| Dynamic | `/api/program-metadata-idl` |||
3938
| Dynamic | `/api/receipt/price/[mintAddress]` |||
4039
| Dynamic | `/api/search` |||
40+
| Dynamic | `/api/security-txt` |||
4141
| Dynamic | `/api/sns-domains/[address]` |||
4242
| Dynamic | `/api/token-info` |||
4343
| Dynamic | `/api/verification/bluprynt/[mintAddress]` |||
4444
| Dynamic | `/api/verification/coingecko/[address]` |||
4545
| Dynamic | `/api/verification/jupiter/[mintAddress]` |||
4646
| Dynamic | `/api/verification/rugcheck/[mintAddress]` |||
4747
| Dynamic | `/block/[slot]` | 230 kB | 1.25 MB |
48-
| Dynamic | `/block/[slot]/accounts` | 220 kB | 1.24 MB |
49-
| Dynamic | `/block/[slot]/programs` | 220 kB | 1.24 MB |
50-
| Dynamic | `/block/[slot]/rewards` | 220 kB | 1.24 MB |
48+
| Dynamic | `/block/[slot]/accounts` | 230 kB | 1.25 MB |
49+
| Dynamic | `/block/[slot]/programs` | 230 kB | 1.25 MB |
50+
| Dynamic | `/block/[slot]/rewards` | 230 kB | 1.25 MB |
5151
| Dynamic | `/epoch/[epoch]` | 10 kB | 1.04 MB |
52-
| Static | `/feature-gates` | 40 kB | 1.07 MB |
52+
| Static | `/feature-gates` | 50 kB | 1.07 MB |
5353
| Dynamic | `/og/feature-gate/[address]` |||
5454
| Dynamic | `/og/receipt/[signature]` |||
5555
| Static | `/opengraph-image.png` |||
56-
| Static | `/tos` | 880 B | 1.03 MB |
57-
| Dynamic | `/tx/[signature]` | 520 kB | 1.53 MB |
58-
| Dynamic | `/tx/[signature]/inspect` | 390 kB | 1.41 MB |
59-
| Static | `/tx/inspector` | 390 kB | 1.41 MB |
56+
| Static | `/tos` | 890 B | 1.03 MB |
57+
| Dynamic | `/tx/[signature]` | 500 kB | 1.51 MB |
58+
| Dynamic | `/tx/[signature]/inspect` | 370 kB | 1.39 MB |
59+
| Static | `/tx/inspector` | 370 kB | 1.39 MB |

packages/decoder-mango/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"typecheck": "tsc --noEmit"
2424
},
2525
"devDependencies": {
26+
"@solana-program/token": "0.13.0",
2627
"typescript": "catalog:",
2728
"vitest": "catalog:"
2829
},

0 commit comments

Comments
 (0)