UNPKG

@debridge-finance/solana-utils

Version:

Common utils package to power communication with Solana contracts at deBridge

143 lines 6.95 kB
import { __awaiter } from "tslib"; import { Keypair, PublicKey, Transaction, VersionedTransaction, } from "@solana/web3.js"; import { parseLogs, } from "@debridge-finance/solana-transaction-parser"; import { FAKE_BLOCKHASH } from "./constants"; import { TOKEN_PROGRAM_ID } from "./accounts"; import { parseSplAccount } from "./spl"; import { ContractErrors, RpcErrors } from "./interfaces"; export function simulateTransaction(connection, tx, simulateConfig) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g, _h; const addressesToGetDiffFor = ((_b = (_a = simulateConfig.accounts) === null || _a === void 0 ? void 0 : _a.addresses) !== null && _b !== void 0 ? _b : []).map((a) => new PublicKey(a)); const accountsToFetch = addressesToGetDiffFor.length > 0 ? yield connection.getMultipleAccountsInfo(addressesToGetDiffFor) : []; const result = yield connection.simulateTransaction(tx, simulateConfig); const diffMap = new Map(); for (const [idx, simulatedAccountInfo] of ((_c = result.value.accounts) !== null && _c !== void 0 ? _c : []).entries()) { const accountAddress = addressesToGetDiffFor[idx]; const oldAccountData = (_d = accountsToFetch.at(idx)) !== null && _d !== void 0 ? _d : null; const oldParsedMaybeSpl = (oldAccountData === null || oldAccountData === void 0 ? void 0 : oldAccountData.owner.equals(TOKEN_PROGRAM_ID)) ? parseSplAccount(oldAccountData) : null; const oldSolValue = (_e = oldAccountData === null || oldAccountData === void 0 ? void 0 : oldAccountData.lamports) !== null && _e !== void 0 ? _e : 0; const newSolValue = (_f = simulatedAccountInfo === null || simulatedAccountInfo === void 0 ? void 0 : simulatedAccountInfo.lamports) !== null && _f !== void 0 ? _f : 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 = Object.assign(Object.assign({}, simulatedAccountInfo), { owner: new PublicKey(simulatedAccountInfo.owner), data: Buffer.from(rawData, "base64") }); newParsedMaybeSpl = artificialAccountInfo.owner.equals(TOKEN_PROGRAM_ID) ? 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 === null || newParsedMaybeSpl === void 0 ? void 0 : newParsedMaybeSpl.mint) || (oldParsedMaybeSpl === null || oldParsedMaybeSpl === void 0 ? void 0 : oldParsedMaybeSpl.mint); if (token === undefined) throw new Error("Failed to get token from the spl token account"); const owner = (newParsedMaybeSpl === null || newParsedMaybeSpl === void 0 ? void 0 : newParsedMaybeSpl.owner) || (oldParsedMaybeSpl === null || oldParsedMaybeSpl === void 0 ? void 0 : oldParsedMaybeSpl.owner); if (owner === undefined) throw new Error("Failed to get owner from the spl token account"); splDiff = { before: (_g = oldParsedMaybeSpl === null || oldParsedMaybeSpl === void 0 ? void 0 : oldParsedMaybeSpl.amount) !== null && _g !== void 0 ? _g : BigInt(0), after: (_h = newParsedMaybeSpl === null || newParsedMaybeSpl === void 0 ? void 0 : newParsedMaybeSpl.amount) !== null && _h !== void 0 ? _h : BigInt(0), token, owner, }; } } if (splDiff !== null || solDiff !== null) { diffMap.set(accountAddress.toBase58(), { native: solDiff, spl: splDiff, }); } } return Object.assign(Object.assign({}, result.value), { diff: diffMap }); }); } /** * Serializes transaction and returns serialized size * @param transaction tx to get length * @param feePayer optional fee payer * @returns transaction size */ export function getTransactionSize(tx) { try { if ("version" in tx) { const txCopy = new VersionedTransaction(tx.message); txCopy.message.recentBlockhash = FAKE_BLOCKHASH; return txCopy.serialize().length; } else { const txCopy = new Transaction(tx); txCopy.feePayer = Keypair.generate().publicKey; txCopy.recentBlockhash = FAKE_BLOCKHASH; return tx.serialize({ verifySignatures: false }).length; } } catch (e) { return null; } } function getParsedError(log) { if (/insufficient lamports/.test(log)) { return ContractErrors.InsufficientLamports; } else if (/insufficient funds/.test(log)) { return ContractErrors.InsufficientFunds; } else { return undefined; } } /** * Parse logs * @param logs array of strings * @param err object or string * @returns parsed logs */ export function getErrorData(logs, err) { var _a, _b; let parsedError; const parsed = 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 = (_a = RpcErrors[err]) !== null && _a !== void 0 ? _a : err; } else if (typeof err === "object" && Object.keys(err).length) { const key = Object.keys(err)[0]; parsedError = (_b = RpcErrors[key]) !== null && _b !== void 0 ? _b : key; } } return { message, parsedError }; } //# sourceMappingURL=transaction.js.map