UNPKG

@debridge-finance/solana-utils

Version:

Common utils package to power communication with Solana contracts at deBridge

150 lines 5.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.simulateTransaction = simulateTransaction; exports.getTransactionSize = getTransactionSize; exports.getErrorData = getErrorData; const web3_js_1 = require("@solana/web3.js"); const solana_transaction_parser_1 = require("@debridge-finance/solana-transaction-parser"); const constants_1 = require("./constants"); const accounts_1 = require("./accounts"); const spl_1 = require("./spl"); const interfaces_1 = require("./interfaces"); async function simulateTransaction(connection, tx, simulateConfig) { const addressesToGetDiffFor = (simulateConfig.accounts?.addresses ?? []).map((a) => new web3_js_1.PublicKey(a)); const accountsToFetch = addressesToGetDiffFor.length > 0 ? await connection.getMultipleAccountsInfo(addressesToGetDiffFor) : []; const result = await connection.simulateTransaction(tx, simulateConfig); const diffMap = new Map(); for (const [idx, simulatedAccountInfo] of (result.value.accounts ?? []).entries()) { const accountAddress = addressesToGetDiffFor[idx]; const oldAccountData = accountsToFetch.at(idx) ?? null; const oldParsedMaybeSpl = oldAccountData?.owner.equals(accounts_1.TOKEN_PROGRAM_ID) ? (0, spl_1.parseSplAccount)(oldAccountData) : null; const oldSolValue = oldAccountData?.lamports ?? 0; const newSolValue = simulatedAccountInfo?.lamports ?? 0; const solDiff = oldSolValue === newSolValue ? null : { before: oldSolValue, after: newSolValue, }; let newParsedMaybeSpl = null; if (simulatedAccountInfo) { const [rawData, encoding] = simulatedAccountInfo.data; if (encoding !== "base64") throw new Error("Expected bs64 account data"); const artificialAccountInfo = { ...simulatedAccountInfo, owner: new web3_js_1.PublicKey(simulatedAccountInfo.owner), data: Buffer.from(rawData, "base64"), }; newParsedMaybeSpl = artificialAccountInfo.owner.equals(accounts_1.TOKEN_PROGRAM_ID) ? (0, spl_1.parseSplAccount)(artificialAccountInfo) : null; } let splDiff = null; // old state is spl token account OR new state is spl token account if (oldParsedMaybeSpl !== null || newParsedMaybeSpl !== null) { if (oldParsedMaybeSpl !== newParsedMaybeSpl) { const token = newParsedMaybeSpl?.mint || oldParsedMaybeSpl?.mint; if (token === undefined) throw new Error("Failed to get token from the spl token account"); const owner = newParsedMaybeSpl?.owner || oldParsedMaybeSpl?.owner; if (owner === undefined) throw new Error("Failed to get owner from the spl token account"); splDiff = { before: oldParsedMaybeSpl?.amount ?? BigInt(0), after: newParsedMaybeSpl?.amount ?? BigInt(0), token, owner, }; } } if (splDiff !== null || solDiff !== null) { diffMap.set(accountAddress.toBase58(), { native: solDiff, spl: splDiff, }); } } return { ...result.value, diff: diffMap, }; } /** * Serializes transaction and returns serialized size * @param transaction tx to get length * @param feePayer optional fee payer * @returns transaction size */ function getTransactionSize(tx) { try { if ("version" in tx) { const txCopy = new web3_js_1.VersionedTransaction(tx.message); txCopy.message.recentBlockhash = constants_1.FAKE_BLOCKHASH; return txCopy.serialize().length; } else { const txCopy = new web3_js_1.Transaction(tx); txCopy.feePayer = web3_js_1.Keypair.generate().publicKey; txCopy.recentBlockhash = constants_1.FAKE_BLOCKHASH; return tx.serialize({ verifySignatures: false }).length; } } catch (e) { return null; } } function getParsedError(log) { if (/insufficient lamports/.test(log)) { return interfaces_1.ContractErrors.InsufficientLamports; } else if (/insufficient funds/.test(log)) { return interfaces_1.ContractErrors.InsufficientFunds; } else { return undefined; } } /** * Parse logs * @param logs array of strings * @param err object or string * @returns parsed logs */ function getErrorData(logs, err) { let parsedError; const parsed = (0, solana_transaction_parser_1.parseLogs)(logs).filter((context) => context.errors.length); const errorData = parsed.map((context) => ({ logMessages: context.logMessages, errors: context.errors, instructionIndex: context.instructionIndex, })); const message = errorData.reduce((string, context) => { if (context.errors.length) { string += `errors: ${JSON.stringify(context.errors)}, `; } if (context.logMessages.length) { string += `messages: ${JSON.stringify(context.logMessages)}, `; } string += `instruction index: ${context.instructionIndex}\n`; return string; }, ""); if (message) { parsedError = getParsedError(message); } else { if (typeof err === "string") { parsedError = interfaces_1.RpcErrors[err] ?? err; } else if (typeof err === "object" && Object.keys(err).length) { const key = Object.keys(err)[0]; parsedError = interfaces_1.RpcErrors[key] ?? key; } } return { message, parsedError }; } //# sourceMappingURL=transaction.js.map