UNPKG

@solana-developers/helpers

Version:
282 lines 12.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getIdlByPath = getIdlByPath; exports.getIdlByProgramId = getIdlByProgramId; exports.createProviderForConnection = createProviderForConnection; exports.getIdlParsedAccountData = getIdlParsedAccountData; exports.parseAnchorTransactionEvents = parseAnchorTransactionEvents; exports.decodeAnchorTransaction = decodeAnchorTransaction; const web3_js_1 = require("@solana/web3.js"); const anchor_1 = require("@coral-xyz/anchor"); const bn_js_1 = __importDefault(require("bn.js")); const convertLegacyIdl_1 = require("./convertLegacyIdl"); /** * Loads an Anchor IDL from a local file path * * @param idlPath - Path to the IDL JSON file * @returns The parsed IDL * * @example * ```typescript * const idl = await getIdlByPath("./idl/program.json"); * ``` */ async function getIdlByPath(idlPath) { const fs = await Promise.resolve().then(() => __importStar(require("node:fs"))); const path = await Promise.resolve().then(() => __importStar(require("node:path"))); // Load and parse IDL file const idlFile = fs.readFileSync(path.resolve(idlPath), "utf8"); const idl = JSON.parse(idlFile); return idl; } /** * Fetches an Anchor IDL from a program on-chain * * @param programId - Public key of the program * @param provider - Anchor Provider instance (you can use createProviderForConnection to create) * @returns The fetched IDL * @throws If IDL cannot be found for the program * * @example * ```typescript * const idl = await getIdlByProgramId( * new PublicKey("Foo1111111111111111111111111111111111111"), * connection * ); * ``` */ async function getIdlByProgramId(programId, provider) { var idl = await anchor_1.Program.fetchIdl(programId, provider); if (!idl) throw new Error(`IDL not found for program ${programId.toString()}`); return idl; } /** * Creates an Anchor provider for a given connection * * @param connection - The Solana connection object * @param keypair - Optional keypair to use for the provider (defaults to a new random keypair) * @param options - Optional configuration options for the provider * @returns An Anchor provider instance * * @example * ```typescript * const provider = createProviderForConnection(connection); * ``` */ function createProviderForConnection(connection, keypair = new web3_js_1.Keypair(), options = { commitment: "confirmed", }) { return new anchor_1.AnchorProvider(connection, new anchor_1.Wallet(keypair), options); } /** * Fetches and parses an account's data using an Anchor IDL * * @param idl - The Anchor IDL (use getIdlByProgramId or getIdlByPath to obtain) * @param accountName - The name of the account as defined in the IDL * @param accountAddress - The public key of the account to fetch * @param provider - Anchor Provider instance (you can use createProviderForConnection to create) * @param programId - Optional program ID needed for legacy IDLs * @returns The decoded account data * * @example * ```typescript * const idl = await getIdlByProgramId(programId, connection); * const data = await getIdlParsedAccountData(idl, "counter", accountAddress); * ``` */ async function getIdlParsedAccountData(idl, accountName, accountAddress, provider, programId) { const program = new anchor_1.Program((0, convertLegacyIdl_1.formatIdl)(idl, programId?.toString()), provider); const accountInfo = await provider.connection.getAccountInfo(accountAddress); if (!accountInfo) { throw new Error(`Account ${accountAddress.toString()} not found`); } return program.coder.accounts.decode(accountName, accountInfo.data); } /** * Parses Anchor events from a transaction * * @param idl - The Anchor IDL (use getIdlByProgramId or getIdlByPath to obtain) * @param signature - Transaction signature to parse events from * @param provider - Anchor Provider instance (you can use createProviderForConnection to create) * @param programId - Optional program ID needed for legacy IDLs * @returns Array of parsed events with their name and data * * @example * ```typescript * const idl = await getIdlByPath("./idl/program.json"); * const events = await parseAnchorTransactionEvents(idl, signature); * ``` */ async function parseAnchorTransactionEvents(idl, signature, provider, programId) { const program = new anchor_1.Program((0, convertLegacyIdl_1.formatIdl)(idl, programId?.toString()), provider); const parser = new anchor_1.EventParser(program.programId, program.coder); const transaction = await provider.connection.getTransaction(signature, { commitment: "confirmed", maxSupportedTransactionVersion: 0, }); if (!transaction?.meta?.logMessages) { return []; } const events = []; for (const event of parser.parseLogs(transaction.meta.logMessages)) { events.push({ name: event.name, data: event.data, }); } return events; } /** * Decodes all Anchor instructions and their involved accounts in a transaction * * @param idl - The Anchor IDL (use getIdlByProgramId or getIdlByPath to obtain) * @param signature - Transaction signature to decode * @param connection - Optional connection object (uses default provider if not specified) * @param programId - Optional program ID needed for legacy IDLs * @returns Decoded transaction with instructions and accounts * * @example * ```typescript * const idl = await getIdlByProgramId(programId, connection); * const decoded = await decodeAnchorTransaction(idl, signature); * ``` */ async function decodeAnchorTransaction(idl, signature, provider, programId) { const program = new anchor_1.Program((0, convertLegacyIdl_1.formatIdl)(idl, programId?.toString()), provider); const accountsCoder = new anchor_1.BorshAccountsCoder(program.idl); const instructionCoder = new anchor_1.BorshInstructionCoder(program.idl); const transaction = await provider.connection.getTransaction(signature, { commitment: "confirmed", maxSupportedTransactionVersion: 0, }); if (!transaction) { throw new Error(`Transaction ${signature} not found`); } const decodedInstructions = []; const message = transaction.transaction.message; const instructions = "addressTableLookups" in message ? message.compiledInstructions : message.instructions; const accountKeys = message.getAccountKeys(); for (const ix of instructions) { const programId = accountKeys.get("programIdIndex" in ix ? ix.programIdIndex : ix.programId); if (!programId) continue; if (programId.equals(program.programId)) { try { const decoded = instructionCoder.decode(Buffer.from(ix.data)); if (decoded) { const ixType = idl.instructions.find((i) => i.name === decoded.name); const accountIndices = "accounts" in ix ? ix.accounts : ix.accountKeyIndexes; // Get all accounts involved in this instruction const accounts = await Promise.all(accountIndices.map(async (index, i) => { const pubkey = accountKeys.get(index); if (!pubkey) return null; const accountMeta = ixType?.accounts[i]; const accountInfo = await provider.connection.getAccountInfo(pubkey); let accountData; if (accountInfo?.owner.equals(program.programId)) { try { const accountType = idl.accounts?.find((acc) => accountInfo.data .slice(0, 8) .equals(accountsCoder.accountDiscriminator(acc.name.toLowerCase()))); if (accountType) { accountData = accountsCoder.decode(accountType.name.toLowerCase(), accountInfo.data); } } catch (e) { console.log(`Failed to decode account data: ${e}`); } } return { name: accountMeta?.name || `account_${i}`, pubkey: pubkey.toString(), isSigner: message.staticAccountKeys.findIndex((k) => k.equals(pubkey)) < message.header.numRequiredSignatures || false, isWritable: message.isAccountWritable(index), ...(accountData && { data: accountData }), }; })); decodedInstructions.push({ name: decoded.name, type: ixType ? JSON.stringify(ixType.args) : "unknown", data: decoded.data, accounts, toString: function () { let output = `\nInstruction: ${this.name}\n`; output += `├─ Arguments: ${JSON.stringify(formatData(this.data))}\n`; output += `└─ Accounts:\n`; this.accounts.forEach((acc) => { output += ` ├─ ${acc.name}:\n`; output += ` │ ├─ Address: ${acc.pubkey}\n`; output += ` │ ├─ Signer: ${acc.isSigner}\n`; output += ` │ ├─ Writable: ${acc.isWritable}\n`; if (acc.data) { output += ` │ └─ Data: ${JSON.stringify(formatData(acc.data))}\n`; } }); return output; }, }); } } catch (e) { console.log(`Failed to decode instruction: ${e}`); } } } return { instructions: decodedInstructions, toString: function () { let output = "\n=== Decoded Transaction ===\n"; this.instructions.forEach((ix, index) => { output += `\nInstruction ${index + 1}:${ix.toString()}`; }); return output; }, }; } // Helper function to format data function formatData(data) { if (data instanceof bn_js_1.default) { return `<BN: ${data.toString()}>`; } if (Array.isArray(data)) { return data.map(formatData); } if (typeof data === "object" && data !== null) { return Object.fromEntries(Object.entries(data).map(([k, v]) => [k, formatData(v)])); } return data; } //# sourceMappingURL=idl.js.map