@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
JavaScript
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] },