@shyft-to/solana-transaction-parser
Version:
Tool for parsing arbitrary Solana transactions with IDL/custom parsers
341 lines • 16.3 kB
JavaScript
import { AuthorityType, TOKEN_PROGRAM_ID, TokenInstruction, decodeApproveCheckedInstruction, decodeApproveInstruction, decodeBurnCheckedInstruction, decodeBurnInstruction, decodeCloseAccountInstruction, decodeFreezeAccountInstruction, decodeInitializeAccountInstruction, decodeInitializeMintInstructionUnchecked, decodeInitializeMultisigInstruction, decodeMintToCheckedInstruction, decodeMintToInstruction, decodeRevokeInstruction, decodeSetAuthorityInstruction, decodeThawAccountInstruction, decodeTransferCheckedInstruction, decodeTransferInstruction, decodeAmountToUiAmountInstruction, decodeInitializeAccount2Instruction, decodeInitializeAccount3Instruction, decodeInitializeMint2Instruction, decodeInitializeImmutableOwnerInstruction, decodeSyncNativeInstruction, decodeUiAmountToAmountInstruction, } from "@solana/spl-token";
import { BN } from "@coral-xyz/anchor";
import { PublicKey } from "@solana/web3.js";
function decodeTokenInstruction(instruction) {
let parsed = null;
const decoded = instruction.data[0];
switch (decoded) {
case TokenInstruction.InitializeMint: {
const decodedIx = decodeInitializeMintInstructionUnchecked(instruction);
parsed = {
name: "initializeMint",
accounts: [
{ name: "mint", isSigner: false, isWritable: true, pubkey: instruction.keys[0].pubkey },
{ name: "rent", isSigner: false, isWritable: false, pubkey: instruction.keys[1].pubkey },
],
args: { decimals: decodedIx.data.decimals, mintAuthority: decodedIx.data.mintAuthority, freezeAuthority: decodedIx.data.freezeAuthority },
};
break;
}
case TokenInstruction.InitializeAccount: {
const decodedIx = decodeInitializeAccountInstruction(instruction);
parsed = {
name: "initializeAccount",
accounts: [
{ name: "account", ...decodedIx.keys.account },
{ name: "mint", ...decodedIx.keys.mint },
{ name: "owner", ...decodedIx.keys.owner },
{ name: "rent", ...decodedIx.keys.rent },
],
args: {},
};
break;
}
case TokenInstruction.InitializeMultisig: {
const decodedIx = decodeInitializeMultisigInstruction(instruction);
const multisig = decodedIx.keys.signers.map((meta, idx) => ({ name: `signer_${idx}`, ...meta }));
parsed = {
name: "initializeMultisig",
accounts: [{ name: "multisig", ...decodedIx.keys.account }, { name: "rent", ...decodedIx.keys.rent }, ...multisig],
args: { m: decodedIx.data.m },
};
break;
}
case TokenInstruction.Transfer: {
const decodedIx = 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: "authority", ...decodedIx.keys.owner },
...multisig,
],
args: { amount: new BN(decodedIx.data.amount.toString()) },
};
break;
}
case TokenInstruction.Approve: {
const decodedIx = 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 TokenInstruction.Revoke: {
const decodedIx = 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 TokenInstruction.SetAuthority: {
const decodedIx = decodeSetAuthorityInstruction(instruction);
const authrorityTypeMap = {
[AuthorityType.AccountOwner]: { accountOwner: {} },
[AuthorityType.CloseAccount]: { closeAccount: {} },
[AuthorityType.FreezeAccount]: { freezeAccount: {} },
[AuthorityType.MintTokens]: { mintTokens: {} },
};
if (![AuthorityType.AccountOwner, AuthorityType.CloseAccount, AuthorityType.FreezeAccount, AuthorityType.MintTokens].includes(decodedIx.data.authorityType)) {
throw new Error("Unexpected authority type for token program");
}
const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta }));
parsed = {
name: "setAuthority",
accounts: [{ name: "owned", ...decodedIx.keys.account }, { name: "owner", ...decodedIx.keys.currentAuthority }, ...multisig],
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
args: { authorityType: authrorityTypeMap[decodedIx.data.authorityType], newAuthority: decodedIx.data.newAuthority },
};
break;
}
case TokenInstruction.MintTo: {
const decodedIx = decodeMintToInstruction(instruction);
const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta }));
parsed = {
name: "mintTo",
accounts: [
{ name: "mint", ...decodedIx.keys.mint },
{ name: "account", ...decodedIx.keys.destination },
{ name: "owner", ...decodedIx.keys.authority },
...multisig,
],
args: { amount: new BN(decodedIx.data.amount.toString()) },
};
break;
}
case TokenInstruction.Burn: {
const decodedIx = decodeBurnInstruction(instruction);
const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta }));
parsed = {
name: "burn",
accounts: [
{ name: "account", ...decodedIx.keys.account },
{ name: "mint", ...decodedIx.keys.mint },
{ name: "authority", ...decodedIx.keys.owner },
...multisig,
],
args: { amount: new BN(decodedIx.data.amount.toString()) },
};
break;
}
case TokenInstruction.CloseAccount: {
const decodedIx = 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 TokenInstruction.FreezeAccount: {
const decodedIx = decodeFreezeAccountInstruction(instruction);
const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta }));
parsed = {
name: "freezeAccount",
accounts: [
{ name: "account", ...decodedIx.keys.account },
{ name: "mint", ...decodedIx.keys.mint },
{ name: "owner", ...decodedIx.keys.authority },
...multisig,
],
args: {},
};
break;
}
case TokenInstruction.ThawAccount: {
const decodedIx = decodeThawAccountInstruction(instruction);
const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta }));
parsed = {
name: "thawAccount",
accounts: [
{ name: "account", ...decodedIx.keys.account },
{ name: "mint", ...decodedIx.keys.mint },
{ name: "owner", ...decodedIx.keys.authority },
...multisig,
],
args: {},
};
break;
}
case TokenInstruction.TransferChecked: {
const decodedIx = decodeTransferCheckedInstruction(instruction);
const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta }));
parsed = {
name: "transferChecked",
accounts: [
{ name: "source", ...decodedIx.keys.source },
{ name: "mint", ...decodedIx.keys.mint },
{ name: "destination", ...decodedIx.keys.destination },
{ name: "authority", ...decodedIx.keys.owner },
...multisig,
],
args: { amount: new BN(decodedIx.data.amount.toString()), decimals: decodedIx.data.decimals },
};
break;
}
case TokenInstruction.ApproveChecked: {
const decodedIx = decodeApproveCheckedInstruction(instruction);
const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta }));
parsed = {
name: "approveChecked",
accounts: [
{ name: "source", ...decodedIx.keys.account },
{ name: "mint", ...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 TokenInstruction.MintToChecked: {
const decodedIx = decodeMintToCheckedInstruction(instruction);
const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta }));
parsed = {
name: "mintToChecked",
accounts: [
{ name: "mint", ...decodedIx.keys.mint },
{ name: "account", ...decodedIx.keys.destination },
{ name: "owner", ...decodedIx.keys.authority },
...multisig,
],
args: { amount: new BN(decodedIx.data.amount.toString()), decimals: decodedIx.data.decimals },
};
break;
}
case TokenInstruction.BurnChecked: {
const decodedIx = decodeBurnCheckedInstruction(instruction);
const multisig = decodedIx.keys.multiSigners.map((meta, idx) => ({ name: `signer_${idx}`, ...meta }));
parsed = {
name: "burnChecked",
accounts: [
{ name: "account", ...decodedIx.keys.account },
{ name: "mint", ...decodedIx.keys.mint },
{ name: "authority", ...decodedIx.keys.owner },
...multisig,
],
args: { amount: new BN(decodedIx.data.amount.toString()), decimals: decodedIx.data.decimals },
};
break;
}
case TokenInstruction.InitializeAccount2: {
const decodedIx = decodeInitializeAccount2Instruction(instruction);
parsed = {
name: "initializeAccount2",
accounts: [
{ name: "account", ...decodedIx.keys.account },
{ name: "mint", ...decodedIx.keys.mint },
{ name: "rent", ...decodedIx.keys.rent },
],
args: { owner: new PublicKey(decodedIx.data.owner) },
};
break;
}
case TokenInstruction.SyncNative: {
const decodedIx = decodeSyncNativeInstruction(instruction);
parsed = {
name: "syncNative",
accounts: [{ name: "account", ...decodedIx.keys.account }],
args: {},
};
break;
}
case TokenInstruction.InitializeAccount3: {
const decodedIx = decodeInitializeAccount3Instruction(instruction);
parsed = {
name: "initializeAccount3",
accounts: [
{ name: "account", ...decodedIx.keys.account },
{ name: "mint", ...decodedIx.keys.mint },
],
args: { owner: new PublicKey(decodedIx.data.owner) },
};
break;
}
case 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 TokenInstruction.InitializeMint2: {
const decodedIx = decodeInitializeMint2Instruction(instruction);
const tokenMint = decodedIx.keys.mint;
if (!tokenMint)
throw new Error(`Failed to parse InitializeMint2 instruction`);
parsed = {
name: "initializeMint2",
accounts: [{ name: "mint", ...tokenMint }],
args: { decimals: decodedIx.data.decimals, mintAuthority: decodedIx.data.mintAuthority, freezeAuthority: decodedIx.data.freezeAuthority },
};
break;
}
case TokenInstruction.InitializeImmutableOwner: {
const decodedIx = decodeInitializeImmutableOwnerInstruction(instruction, instruction.programId);
parsed = {
name: "initializeImmutableOwner",
accounts: [{ name: "account", ...decodedIx.keys.account }],
args: {},
};
break;
}
case TokenInstruction.AmountToUiAmount: {
const decodedIx = decodeAmountToUiAmountInstruction(instruction);
parsed = {
name: "amountToUiAmount",
accounts: [{ name: "mint", ...decodedIx.keys.mint }],
args: { amount: new BN(decodedIx.data.amount.toString()) },
};
break;
}
case TokenInstruction.UiAmountToAmount: {
const decodedIx = decodeUiAmountToAmountInstruction(instruction);
parsed = {
name: "uiAmountToAmount",
accounts: [{ name: "mint", ...decodedIx.keys.mint }],
args: { uiAmount: new BN(decodedIx.data.amount).toString() },
};
break;
}
default: {
parsed = null;
}
}
return parsed
? {
...parsed,
programId: TOKEN_PROGRAM_ID,
}
: {
programId: TOKEN_PROGRAM_ID,
name: "unknown",
accounts: instruction.keys,
args: { unknown: instruction.data },
};
}
export { decodeTokenInstruction };
//# sourceMappingURL=token.js.map