UNPKG

@shyft.to/solana-transaction-parser

Version:

Tool for parsing arbitrary Solana transactions with IDL/custom parsers

1,022 lines (1,021 loc) 85.6 kB
import { Buffer } from "buffer"; import { PublicKey, SystemInstruction, SystemProgram, Transaction, StakeInstruction, StakeProgram, } from "@solana/web3.js"; import * as spl from "@solana/spl-token"; import { BN, BorshInstructionCoder } from "@project-serum/anchor"; import { blob, struct, u8 } from "@solana/buffer-layout"; import { splDiscriminate } from "@solana/spl-type-length-value"; import { compiledInstructionToInstruction, flattenParsedTransaction, flattenTransactionResponse, parsedInstructionToInstruction, parseTransactionAccounts, } from "./helpers"; import { decodeSetTransferFeeInstruction, emitLayout, getAccountDataSizeLayout, metadataLayout, removeKeyLayout, updateAuthorityLayout, updateMetadataLayout, } from "./programs/token-extensions"; const MEMO_PROGRAM_V1 = "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo"; const MEMO_PROGRAM_V2 = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"; function decodeSystemInstruction(instruction) { const ixType = SystemInstruction.decodeInstructionType(instruction); let parsed; switch (ixType) { case "AdvanceNonceAccount": { const decoded = SystemInstruction.decodeNonceAdvance(instruction); parsed = { name: "advanceNonceAccount", accounts: [ { name: "nonce", pubkey: decoded.noncePubkey, isSigner: false, isWritable: true }, { name: "recentBlockhashSysvar", ...instruction.keys[1] }, { name: "nonceAuthority", pubkey: decoded.authorizedPubkey, isSigner: true, isWritable: false }, ], args: {}, }; break; } case "Allocate": { const decoded = SystemInstruction.decodeAllocate(instruction); parsed = { name: "allocate", accounts: [{ name: "newAccount", pubkey: decoded.accountPubkey, isSigner: true, isWritable: true }], args: { space: new BN(decoded.space) }, }; break; } case "AllocateWithSeed": { const decoded = SystemInstruction.decodeAllocateWithSeed(instruction); parsed = { name: "allocateWithSeed", accounts: [ { name: "newAccount", pubkey: decoded.accountPubkey, isSigner: false, isWritable: true }, { name: "base", pubkey: decoded.basePubkey, isSigner: true, isWritable: false }, ], args: { seed: decoded.seed, space: new BN(decoded.space), owner: decoded.programId, base: decoded.basePubkey, }, }; break; } case "Assign": { const decoded = SystemInstruction.decodeAssign(instruction); parsed = { name: "assign", accounts: [{ name: "assignedAccount", pubkey: decoded.accountPubkey, isSigner: true, isWritable: true }], args: { owner: decoded.programId }, }; break; } case "AssignWithSeed": { const decoded = SystemInstruction.decodeAssignWithSeed(instruction); parsed = { name: "assignWithSeed", accounts: [ { name: "assigned", pubkey: decoded.accountPubkey, isSigner: false, isWritable: true }, { name: "base", pubkey: decoded.basePubkey, isSigner: true, isWritable: false }, ], args: { seed: decoded.seed, // string owner: decoded.programId, base: decoded.basePubkey, }, }; break; } case "AuthorizeNonceAccount": { const decoded = SystemInstruction.decodeNonceAuthorize(instruction); parsed = { name: "authorizeNonceAccount", accounts: [ { name: "nonce", isSigner: false, isWritable: true, pubkey: decoded.noncePubkey }, { name: "nonceAuthority", isSigner: true, isWritable: false, pubkey: decoded.authorizedPubkey }, ], args: { authorized: decoded.newAuthorizedPubkey }, }; break; } case "Create": { const decoded = SystemInstruction.decodeCreateAccount(instruction); parsed = { name: "createAccount", accounts: [ { name: "payer", pubkey: decoded.fromPubkey, isSigner: true, isWritable: true }, { name: "newAccount", pubkey: decoded.newAccountPubkey, isSigner: true, isWritable: true }, ], args: { lamports: new BN(decoded.lamports), owner: decoded.programId, space: new BN(decoded.space) }, }; break; } case "CreateWithSeed": { const decoded = SystemInstruction.decodeCreateWithSeed(instruction); parsed = { name: "createAccountWithSeed", accounts: [ { name: "payer", pubkey: decoded.fromPubkey, isSigner: true, isWritable: true }, { name: "created", pubkey: decoded.newAccountPubkey, isSigner: false, isWritable: true }, { name: "base", pubkey: decoded.basePubkey, isSigner: true, isWritable: false }, ], args: { lamports: new BN(decoded.lamports), owner: decoded.programId, space: new BN(decoded.space), seed: decoded.seed, base: decoded.basePubkey, }, }; break; } case "InitializeNonceAccount": { const decoded = SystemInstruction.decodeNonceInitialize(instruction); parsed = { name: "initializeNonceAccount", accounts: [ { name: "nonce", pubkey: decoded.noncePubkey, isSigner: false, isWritable: true }, { name: "recentBlockhashSysvar", ...instruction.keys[1] }, { name: "rentSysvar", ...instruction.keys[2] }, ], args: { authorized: decoded.authorizedPubkey }, }; break; } case "Transfer": { const decoded = SystemInstruction.decodeTransfer(instruction); parsed = { name: "transfer", accounts: [ { name: "sender", pubkey: decoded.fromPubkey, isSigner: true, isWritable: true }, { name: "receiver", pubkey: decoded.toPubkey, isWritable: true, isSigner: false }, ], args: { lamports: new BN(decoded.lamports.toString()) }, }; break; } case "TransferWithSeed": { const decoded = SystemInstruction.decodeTransferWithSeed(instruction); parsed = { name: "transferWithSeed", accounts: [ { name: "sender", pubkey: decoded.fromPubkey, isSigner: false, isWritable: true }, { name: "base", pubkey: decoded.basePubkey, isSigner: true, isWritable: false }, { name: "receiver", pubkey: decoded.toPubkey, isSigner: false, isWritable: true }, ], args: { owner: decoded.programId, lamports: new BN(decoded.lamports.toString()), seed: decoded.seed }, }; break; } case "WithdrawNonceAccount": { const decoded = SystemInstruction.decodeNonceWithdraw(instruction); parsed = { name: "withdrawNonceAccount", accounts: [ { name: "nonce", pubkey: decoded.noncePubkey, isSigner: false, isWritable: true }, { name: "recepient", pubkey: decoded.toPubkey, isSigner: false, isWritable: true }, { name: "recentBlockhashSysvar", ...instruction.keys[2] }, { name: "rentSysvar", ...instruction.keys[3] }, { name: "nonceAuthority", pubkey: decoded.noncePubkey, isSigner: true, isWritable: false }, ], args: { lamports: new BN(decoded.lamports) }, }; break; } default: { parsed = null; } } return parsed ? { ...parsed, programId: SystemProgram.programId, } : { programId: SystemProgram.programId, name: "unknown", accounts: instruction.keys, args: { unknown: instruction.data }, }; } function decodeTokenInstruction(instruction) { let parsed; const decoded = u8().decode(instruction.data); switch (decoded) { case spl.TokenInstruction.InitializeMint: { const decodedIx = spl.decodeInitializeMintInstruction(instruction); parsed = { name: "initializeMint", accounts: [ { name: "tokenMint", ...decodedIx.keys.mint }, { name: "rentSysvar", ...decodedIx.keys.rent }, ], args: { decimals: decodedIx.data.decimals, mintAuthority: decodedIx.data.mintAuthority, freezeAuthority: decodedIx.data.freezeAuthority }, }; break; } case spl.TokenInstruction.InitializeAccount: { const decodedIx = spl.decodeInitializeAccountInstruction(instruction); parsed = { name: "initializeAccount", accounts: [ { name: "newAccount", ...decodedIx.keys.account }, { name: "tokenMint", ...decodedIx.keys.mint }, { name: "owner", ...decodedIx.keys.owner }, { name: "rentSysvar", ...decodedIx.keys.rent }, ], args: {}, }; break; } case spl.TokenInstruction.InitializeMultisig: { const decodedIx = spl.decodeInitializeMultisigInstruction(instruction); const multisig = decodedIx.keys.signers.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "initializeMultisig", accounts: [{ name: "multisig", ...decodedIx.keys.account }, { name: "rentSysvar", ...decodedIx.keys.rent }, ...multisig], args: { m: decodedIx.data.m }, }; break; } case spl.TokenInstruction.Transfer: { const decodedIx = spl.decodeTransferInstruction(instruction); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "transfer", accounts: [ { name: "source", ...decodedIx.keys.source }, { name: "destination", ...decodedIx.keys.destination }, { name: "owner", ...decodedIx.keys.owner }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()) }, }; break; } case spl.TokenInstruction.Approve: { const decodedIx = spl.decodeApproveInstruction(instruction); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "approve", accounts: [ { name: "source", ...decodedIx.keys.account }, { name: "delegate", ...decodedIx.keys.delegate }, { name: "owner", ...decodedIx.keys.owner }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()) }, }; break; } case spl.TokenInstruction.Revoke: { const decodedIx = spl.decodeRevokeInstruction(instruction); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "revoke", accounts: [{ name: "source", ...decodedIx.keys.account }, { name: "owner", ...decodedIx.keys.owner }, ...multisig], args: {}, }; break; } case spl.TokenInstruction.SetAuthority: { const decodedIx = spl.decodeSetAuthorityInstruction(instruction); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "setAuthority", accounts: [{ name: "account", ...decodedIx.keys.account }, { name: "currentAuthority", ...decodedIx.keys.currentAuthority }, ...multisig], args: { authorityType: decodedIx.data.authorityType, newAuthority: decodedIx.data.newAuthority }, }; break; } case spl.TokenInstruction.MintTo: { const decodedIx = spl.decodeMintToInstruction(instruction); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "mintTo", accounts: [ { name: "tokenMint", ...decodedIx.keys.mint }, { name: "mintTo", ...decodedIx.keys.destination }, { name: "authority", ...decodedIx.keys.authority }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()) }, }; break; } case spl.TokenInstruction.Burn: { const decodedIx = spl.decodeBurnInstruction(instruction); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "burn", accounts: [ { name: "burnFrom", ...decodedIx.keys.account }, { name: "tokenMint", ...decodedIx.keys.mint }, { name: "owner", ...decodedIx.keys.owner }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()) }, }; break; } case spl.TokenInstruction.CloseAccount: { const decodedIx = spl.decodeCloseAccountInstruction(instruction); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "closeAccount", accounts: [ { name: "account", ...decodedIx.keys.account }, { name: "destination", ...decodedIx.keys.destination }, { name: "owner", ...decodedIx.keys.authority }, ...multisig, ], args: {}, }; break; } case spl.TokenInstruction.FreezeAccount: { const decodedIx = spl.decodeFreezeAccountInstruction(instruction); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "freezeAccount", accounts: [ { name: "account", ...decodedIx.keys.account }, { name: "tokenMint", ...decodedIx.keys.mint }, { name: "authority", ...decodedIx.keys.authority }, ...multisig, ], args: {}, }; break; } case spl.TokenInstruction.ThawAccount: { const decodedIx = spl.decodeThawAccountInstruction(instruction); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "thawAccount", accounts: [ { name: "account", ...decodedIx.keys.account }, { name: "tokenMint", ...decodedIx.keys.mint }, { name: "authority", ...decodedIx.keys.authority }, ...multisig, ], args: {}, }; break; } case spl.TokenInstruction.TransferChecked: { const decodedIx = spl.decodeTransferCheckedInstruction(instruction); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "transferChecked", accounts: [ { name: "source", ...decodedIx.keys.source }, { name: "tokenMint", ...decodedIx.keys.mint }, { name: "destination", ...decodedIx.keys.destination }, { name: "owner", ...decodedIx.keys.owner }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()), decimals: decodedIx.data.decimals }, }; break; } case spl.TokenInstruction.ApproveChecked: { const decodedIx = spl.decodeApproveCheckedInstruction(instruction); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "approveChecked", accounts: [ { name: "source", ...decodedIx.keys.account }, { name: "tokenMint", ...decodedIx.keys.mint }, { name: "delegate", ...decodedIx.keys.delegate }, { name: "owner", ...decodedIx.keys.owner }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()), decimals: decodedIx.data.decimals }, }; break; } case spl.TokenInstruction.MintToChecked: { const decodedIx = spl.decodeMintToCheckedInstruction(instruction); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "mintToChecked", accounts: [ { name: "tokenMint", ...decodedIx.keys.mint }, { name: "mintTo", ...decodedIx.keys.destination }, { name: "authority", ...decodedIx.keys.authority }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()), decimals: decodedIx.data.decimals }, }; break; } case spl.TokenInstruction.BurnChecked: { const decodedIx = spl.decodeBurnCheckedInstruction(instruction); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "burnChecked", accounts: [ { name: "burnFrom", ...decodedIx.keys.account }, { name: "tokenMint", ...decodedIx.keys.mint }, { name: "owner", ...decodedIx.keys.owner }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()), decimals: decodedIx.data.decimals }, }; break; } case spl.TokenInstruction.InitializeAccount2: { const initializeAccount2InstructionData = struct([u8("instruction"), blob(32, "owner")]); const decodedIx = initializeAccount2InstructionData.decode(instruction.data); parsed = { name: "initializeAccount2", accounts: [ { name: "newAccount", ...instruction.keys[0] }, { name: "tokenMint", ...instruction.keys[1] }, { name: "rentSysvar", ...instruction.keys[2] }, ], args: { authority: new PublicKey(decodedIx.owner) }, }; break; } case spl.TokenInstruction.SyncNative: { parsed = { name: "syncNative", accounts: [{ name: "account", ...instruction.keys[0] }], args: {}, }; break; } case spl.TokenInstruction.InitializeAccount3: { const initializeAccount3InstructionData = struct([u8("instruction"), blob(32, "owner")]); const decodedIx = initializeAccount3InstructionData.decode(instruction.data); parsed = { name: "initializeAccount3", accounts: [ { name: "newAccount", ...instruction.keys[0] }, { name: "tokenMint", ...instruction.keys[1] }, ], args: { authority: new PublicKey(decodedIx.owner) }, }; break; } case spl.TokenInstruction.InitializeMultisig2: { const multisig = instruction.keys.slice(1).map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "initializeMultisig2", accounts: [{ name: "multisig", ...instruction.keys[0] }, ...multisig], args: { m: instruction.data[1] }, }; break; } case spl.TokenInstruction.InitializeMint2: { const decodedIx = spl.decodeInitializeMintInstructionUnchecked(instruction); const tokenMint = decodedIx.keys.mint; if (!tokenMint) throw new Error(`Failed to parse InitializeMint2 instruction`); parsed = { name: "initializeMint2", accounts: [{ name: "tokenMint", ...decodedIx.keys.mint }], args: { decimals: decodedIx.data.decimals, mintAuthority: decodedIx.data.mintAuthority, freezeAuthority: decodedIx.data.freezeAuthority }, }; break; } default: { parsed = null; } } return parsed ? { ...parsed, programId: spl.TOKEN_PROGRAM_ID, } : { programId: spl.TOKEN_PROGRAM_ID, name: "unknown", accounts: instruction.keys, args: { unknown: instruction.data }, }; } function decodeToken2022Instruction(instruction) { let parsed; const decoded = u8().decode(instruction.data); switch (decoded) { case spl.TokenInstruction.InitializeMint: { const decodedIx = spl.decodeInitializeMintInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); parsed = { name: "initializeMint", accounts: [ { name: "tokenMint", ...decodedIx.keys.mint }, { name: "rentSysvar", ...decodedIx.keys.rent }, ], args: { decimals: decodedIx.data.decimals, mintAuthority: decodedIx.data.mintAuthority, freezeAuthority: decodedIx.data.freezeAuthority }, }; break; } case spl.TokenInstruction.InitializeAccount: { const decodedIx = spl.decodeInitializeAccountInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); parsed = { name: "initializeAccount", accounts: [ { name: "newAccount", ...decodedIx.keys.account }, { name: "tokenMint", ...decodedIx.keys.mint }, { name: "owner", ...decodedIx.keys.owner }, { name: "rentSysvar", ...decodedIx.keys.rent }, ], args: {}, }; break; } case spl.TokenInstruction.InitializeMultisig: { const decodedIx = spl.decodeInitializeMultisigInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const multisig = decodedIx.keys.signers.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "initializeMultisig", accounts: [{ name: "multisig", ...decodedIx.keys.account }, { name: "rentSysvar", ...decodedIx.keys.rent }, ...multisig], args: { m: decodedIx.data.m }, }; break; } case spl.TokenInstruction.Transfer: { const decodedIx = spl.decodeTransferInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "transfer", accounts: [ { name: "source", ...decodedIx.keys.source }, { name: "destination", ...decodedIx.keys.destination }, { name: "owner", ...decodedIx.keys.owner }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()) }, }; break; } case spl.TokenInstruction.Approve: { const decodedIx = spl.decodeApproveInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "approve", accounts: [ { name: "source", ...decodedIx.keys.account }, { name: "delegate", ...decodedIx.keys.delegate }, { name: "owner", ...decodedIx.keys.owner }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()) }, }; break; } case spl.TokenInstruction.Revoke: { const decodedIx = spl.decodeRevokeInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "revoke", accounts: [{ name: "source", ...decodedIx.keys.account }, { name: "owner", ...decodedIx.keys.owner }, ...multisig], args: {}, }; break; } case spl.TokenInstruction.SetAuthority: { const decodedIx = spl.decodeSetAuthorityInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "setAuthority", accounts: [{ name: "account", ...decodedIx.keys.account }, { name: "currentAuthority", ...decodedIx.keys.currentAuthority }, ...multisig], args: { authorityType: Number(decodedIx.data.authorityType), newAuthority: decodedIx.data.newAuthority }, programId: spl.TOKEN_2022_PROGRAM_ID, }; break; } case spl.TokenInstruction.MintTo: { const decodedIx = spl.decodeMintToInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "mintTo", accounts: [ { name: "tokenMint", ...decodedIx.keys.mint }, { name: "mintTo", ...decodedIx.keys.destination }, { name: "authority", ...decodedIx.keys.authority }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()) }, }; break; } case spl.TokenInstruction.Burn: { const decodedIx = spl.decodeBurnInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "burn", accounts: [ { name: "burnFrom", ...decodedIx.keys.account }, { name: "tokenMint", ...decodedIx.keys.mint }, { name: "owner", ...decodedIx.keys.owner }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()) }, }; break; } case spl.TokenInstruction.CloseAccount: { const decodedIx = spl.decodeCloseAccountInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "closeAccount", accounts: [ { name: "account", ...decodedIx.keys.account }, { name: "destination", ...decodedIx.keys.destination }, { name: "owner", ...decodedIx.keys.authority }, ...multisig, ], args: {}, }; break; } case spl.TokenInstruction.FreezeAccount: { const decodedIx = spl.decodeFreezeAccountInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "freezeAccount", accounts: [ { name: "account", ...decodedIx.keys.account }, { name: "tokenMint", ...decodedIx.keys.mint }, { name: "authority", ...decodedIx.keys.authority }, ...multisig, ], args: {}, }; break; } case spl.TokenInstruction.ThawAccount: { const decodedIx = spl.decodeThawAccountInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "thawAccount", accounts: [ { name: "account", ...decodedIx.keys.account }, { name: "tokenMint", ...decodedIx.keys.mint }, { name: "authority", ...decodedIx.keys.authority }, ...multisig, ], args: {}, }; break; } case spl.TokenInstruction.TransferChecked: { const decodedIx = spl.decodeTransferCheckedInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "transferChecked", accounts: [ { name: "source", ...decodedIx.keys.source }, { name: "tokenMint", ...decodedIx.keys.mint }, { name: "destination", ...decodedIx.keys.destination }, { name: "owner", ...decodedIx.keys.owner }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()), decimals: decodedIx.data.decimals }, }; break; } case spl.TokenInstruction.ApproveChecked: { const decodedIx = spl.decodeApproveCheckedInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "approveChecked", accounts: [ { name: "source", ...decodedIx.keys.account }, { name: "tokenMint", ...decodedIx.keys.mint }, { name: "delegate", ...decodedIx.keys.delegate }, { name: "owner", ...decodedIx.keys.owner }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()), decimals: decodedIx.data.decimals }, }; break; } case spl.TokenInstruction.MintToChecked: { const decodedIx = spl.decodeMintToCheckedInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "mintToChecked", accounts: [ { name: "tokenMint", ...decodedIx.keys.mint }, { name: "mintTo", ...decodedIx.keys.destination }, { name: "authority", ...decodedIx.keys.authority }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()), decimals: decodedIx.data.decimals }, }; break; } case spl.TokenInstruction.BurnChecked: { const decodedIx = spl.decodeBurnCheckedInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "burnChecked", accounts: [ { name: "burnFrom", ...decodedIx.keys.account }, { name: "tokenMint", ...decodedIx.keys.mint }, { name: "owner", ...decodedIx.keys.owner }, ...multisig, ], args: { amount: new BN(decodedIx.data.amount.toString()), decimals: decodedIx.data.decimals }, }; break; } case spl.TokenInstruction.InitializeAccount2: { const initializeAccount2InstructionData = struct([u8("instruction"), blob(32, "owner")]); const decodedIx = initializeAccount2InstructionData.decode(instruction.data); parsed = { name: "initializeAccount2", accounts: [ { name: "newAccount", ...instruction.keys[0] }, { name: "tokenMint", ...instruction.keys[1] }, { name: "rentSysvar", ...instruction.keys[2] }, ], args: { owner: new PublicKey(decodedIx.owner) }, }; break; } case spl.TokenInstruction.SyncNative: { parsed = { name: "syncNative", accounts: [{ name: "account", ...instruction.keys[0] }], args: {}, }; break; } case spl.TokenInstruction.InitializeAccount3: { const initializeAccount3InstructionData = struct([u8("instruction"), blob(32, "owner")]); const decodedIx = initializeAccount3InstructionData.decode(instruction.data); parsed = { name: "initializeAccount3", accounts: [ { name: "newAccount", ...instruction.keys[0] }, { name: "tokenMint", ...instruction.keys[1] }, ], args: { owner: new PublicKey(decodedIx.owner) }, }; break; } case spl.TokenInstruction.InitializeMultisig2: { const multisig = instruction.keys.slice(1).map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed = { name: "initializeMultisig2", accounts: [{ name: "multisig", ...instruction.keys[0] }, ...multisig], args: { m: instruction.data[1] }, }; break; } case spl.TokenInstruction.InitializeMint2: { const decodedIx = spl.decodeInitializeMintInstructionUnchecked(instruction); const tokenMint = decodedIx.keys.mint; if (!tokenMint) throw new Error(`Failed to parse InitializeMint2 instruction`); parsed = { name: "initializeMint2", accounts: [{ name: "tokenMint", ...decodedIx.keys.mint }], args: { decimals: decodedIx.data.decimals, mintAuthority: decodedIx.data.mintAuthority, freezeAuthority: decodedIx.data.freezeAuthority }, }; break; } case spl.TokenInstruction.GetAccountDataSize: { const tokenMint = instruction.keys[0].pubkey; if (!tokenMint) throw new Error(`Failed to parse GetAccountDataSize instruction`); const instructionData = getAccountDataSizeLayout.decode(instruction.data); parsed = { name: "getAccountDataSize", accounts: [{ name: "mint", ...instruction.keys[0] }], args: { extensionTypes: instructionData.extensions.map((ext) => spl.ExtensionType[ext]) }, }; break; } case spl.TokenInstruction.InitializeImmutableOwner: { const decodedIx = spl.decodeInitializeImmutableOwnerInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const account = decodedIx.keys.account; if (!account) throw new Error(`Failed to parse InitializeImmutableOwner instruction`); parsed = { name: "initializeImmutableOwner", accounts: [{ name: "account", ...decodedIx.keys.account }], args: {}, }; break; } case spl.TokenInstruction.AmountToUiAmount: { const decodedIx = spl.decodeAmountToUiAmountInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const tokenMint = decodedIx.keys.mint; if (!tokenMint) throw new Error(`Failed to parse AmountToUiAmount instruction`); parsed = { name: "amountToUiAmount", accounts: [{ name: "mint", ...decodedIx.keys.mint }], args: { amount: new BN(decodedIx.data.amount.toString()) }, }; break; } case spl.TokenInstruction.UiAmountToAmount: { const decodedIx = spl.decodeUiAmountToAmountInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const tokenMint = decodedIx.keys.mint; if (!tokenMint) throw new Error(`Failed to parse UiAmountToAmount instruction`); parsed = { name: "uiAmountToAmount", accounts: [{ name: "mint", ...decodedIx.keys.mint }], args: { uiAmount: decodedIx.data.amount }, }; break; } case spl.TokenInstruction.InitializeMintCloseAuthority: { const decodedIx = spl.decodeInitializeMintCloseAuthorityInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const tokenMint = decodedIx.keys.mint; if (!tokenMint) throw new Error(`Failed to parse InitializeMintCloseAuthority instruction`); parsed = { name: "initializeMintCloseAuthority", accounts: [{ name: "mint", ...decodedIx.keys.mint }], args: { closeAuthority: decodedIx.data.closeAuthority }, }; break; } case spl.TokenInstruction.TransferFeeExtension: { const discriminator = u8().decode(instruction.data.slice(1)); switch (discriminator) { case spl.TransferFeeInstruction.InitializeTransferFeeConfig: { const decodedIx = spl.decodeInitializeTransferFeeConfigInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const tokenMint = decodedIx.keys.mint; if (!tokenMint) throw new Error(`Failed to parse InitializeTransferFeeConfig instruction`); parsed = { name: "initializeTransferFeeConfig", accounts: [{ name: "mint", ...decodedIx.keys.mint }], args: { transferFeeConfigAuthority: decodedIx.data.transferFeeConfigAuthority, withdrawWithheldAuthority: decodedIx.data.withdrawWithheldAuthority, transferFeeBasisPoints: decodedIx.data.transferFeeBasisPoints, maximumFee: decodedIx.data.maximumFee, }, }; break; } case spl.TransferFeeInstruction.TransferCheckedWithFee: { const decodedIx = spl.decodeTransferCheckedWithFeeInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const tokenMint = decodedIx.keys.mint; if (!tokenMint) throw new Error(`Failed to parse TransferCheckedWithFee instruction`); parsed = { name: "transferCheckedWithFee", accounts: [ { name: "source", ...decodedIx.keys.source }, { name: "mint", ...decodedIx.keys.mint }, { name: "destination", ...decodedIx.keys.destination }, { name: "authority", ...decodedIx.keys.authority }, ], args: { amount: decodedIx.data.amount, decimals: decodedIx.data.decimals, fee: decodedIx.data.fee, }, }; if (decodedIx.keys.signers) { const multisig = decodedIx.keys.signers.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed.accounts.push(...multisig); } break; } case spl.TransferFeeInstruction.WithdrawWithheldTokensFromMint: { const decodedIx = spl.decodeWithdrawWithheldTokensFromMintInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const tokenMint = decodedIx.keys.mint; if (!tokenMint) throw new Error(`Failed to parse WithdrawWithheldTokensFromMint instruction`); parsed = { name: "withdrawWithheldTokensFromMint", accounts: [ { name: "mint", ...decodedIx.keys.mint }, { name: "destination", ...decodedIx.keys.destination }, { name: "authority", ...decodedIx.keys.authority }, ], args: {}, }; if (decodedIx.keys.signers) { const multisig = decodedIx.keys.signers.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed.accounts.push(...multisig); } break; } case spl.TransferFeeInstruction.WithdrawWithheldTokensFromAccounts: { const decodedIx = spl.decodeWithdrawWithheldTokensFromAccountsInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const tokenMint = decodedIx.keys.mint; if (!tokenMint) throw new Error(`Failed to parse WithdrawWithheldTokensFromAccounts instruction`); parsed = { name: "withdrawWithheldTokensFromAccounts", accounts: [ { name: "mint", ...decodedIx.keys.mint }, { name: "destination", ...decodedIx.keys.destination }, { name: "authority", ...decodedIx.keys.authority }, ], args: {}, }; if (decodedIx.keys.signers) { const multisig = decodedIx.keys.signers.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed.accounts.push(...multisig); } if (decodedIx.keys.sources) { const multisig = decodedIx.keys.sources.map((meta, idx) => ({ name: `source_${idx}`, ...meta })); parsed.accounts.push(...multisig); } break; } case spl.TransferFeeInstruction.HarvestWithheldTokensToMint: { const decodedIx = spl.decodeHarvestWithheldTokensToMintInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const tokenMint = decodedIx.keys.mint; if (!tokenMint) throw new Error(`Failed to parse HarvestWithheldTokensToMint instruction`); parsed = { name: "harvestWithheldTokensToMint", accounts: [{ name: "mint", ...decodedIx.keys.mint }], args: {}, }; if (decodedIx.keys.sources) { const multisig = decodedIx.keys.sources.map((meta, idx) => ({ name: `source_${idx}`, ...meta })); parsed.accounts.push(...multisig); } break; } case spl.TransferFeeInstruction.SetTransferFee: { const decodedIx = decodeSetTransferFeeInstruction(instruction, spl.TOKEN_2022_PROGRAM_ID); const tokenMint = decodedIx.keys.mint; if (!tokenMint) throw new Error(`Failed to parse SetTransferFee instruction`); parsed = { name: "setTransferFee", accounts: [ { name: "mint", ...decodedIx.keys.mint }, { name: "authority", ...decodedIx.keys.authority }, ], args: { transferFeeBasisPoints: decodedIx.data.transferFeeBasisPoints, maximumFee: decodedIx.data.maximumFee }, }; if (decodedIx.keys.signers) { const multisig = decodedIx.keys.signers.map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); parsed.accounts.push(...multisig); } break; } default: { parsed = null; break; } } break; } case spl.TokenInstruction.DefaultAccountStateExtension: { const discriminator = u8().decode(instruction.data.slice(1)); switch (discriminator) { case spl.DefaultAccountStateInstruction.Initialize: { const tokenMint = instruction.keys[0].pubkey; if (!tokenMint) throw new Error(`Failed to parse InitializeDefaultAccountState instruction`); const instructionData = spl.defaultAccountStateInstructionData.decode(instruction.data); parsed = { name: "initializeDefaultAccountState", accounts: [{ name: "mint", ...instruction.keys[0] }], args: { accountState: spl.AccountState[instructionData.accountState] }, }; break; } case spl.DefaultAccountStateInstruction.Update: { const tokenMint = instruction.keys[0].pubkey; if (!tokenMint) throw new Error(`Failed to parse UpdateDefaultAccountState instruction`); const multisig = instruction.keys.slice(2).map((meta, idx) => ({ name: `signer_${idx}`, ...meta })); const instructionData = spl.defaultAccountStateInstructionData.decode(instruction.data); parsed = { name: "updateDefaultAccountState", accounts: [{ name: "mint", ...instruction.keys[0] }, { name: "freezeAuthority", ...instruction.keys[1] }, { ...multisig }], args: { accountState: spl.AccountState[instructionData.accountState] }, }; break; } default: { parsed = null; break; } } break; } case spl.TokenInstruction.MemoTransferExtension: { const account = instruction.keys[0].pubkey; if (!account) throw new Error(`Failed to parse MemoTransfersInstruction instruction`); const instructionData = spl.memoTransferInstructionData.decode(instruction.data); parsed = { name: "memoTransfersInstruction", accounts: [{ name: "account", ...instruction.keys[0] }, { name: "authority", ...instruction.keys[1] }, { ...instruction.keys.slice(2) }], args: { memoTransferInstruction: spl.MemoTransferInstruction[instructionData.memoTransferInstruction] }, }; break; } case spl.TokenInstruction.CreateNativeMint: { const payer = instruction.keys[0].pubkey; if (!payer) throw new Error(`Failed to parse CreateNativeMint instruction`); parsed = { name: "createNativeMint", accounts: [ { name: "payer", ...instruction.keys[0] }, { name: "nativeMintId", ...instruction.keys[1] }, { name: "systemProgram", ...instruction.keys[2] },