UNPKG

@debridge-finance/solana-utils

Version:

Common utils package to power communication with Solana contracts at deBridge

219 lines 9.95 kB
import { __awaiter } from "tslib"; import { Buffer } from "buffer"; import { MessageV0, Transaction, VersionedTransaction, } from "@solana/web3.js"; import { FAKE_BLOCKHASH } from "./constants"; export function getAccountInfo(connection_1, account_1) { return __awaiter(this, arguments, void 0, function* (connection, account, commitment = "confirmed") { const info = yield connection.getAccountInfo(account, commitment); if (info) { // alchemy bug fix if (info.lamports === 0) return null; return info; } else { return null; } }); } export function hexToBuffer(data, align) { if (!data.startsWith("0x")) throw new Error("[hexToBuffer]: string must start with 0x"); data = data.slice(2); // remove 0x if (!data) return Buffer.from([]); const bufferLen = data.length / 2; data = data.length % 2 ? "0" + data : data; // append leading zero if data is not aligned, eg "fff" => "0f ff" align = align || 0; if (align && bufferLen < align) { return Buffer.concat([ Buffer.from(Uint8Array.from({ length: align - bufferLen }).fill(0)), Buffer.from(data, "hex"), ]); } return Buffer.from(data, "hex"); } export function bufferToHex(data) { return `0x${Buffer.from(data).toString("hex")}`; } export function sleep(milliSeconds) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { setTimeout(resolve, milliSeconds); }); }); } function isVersionedTx(arg) { return "version" in arg; } export class Wallet { constructor(keypair) { this.keypair = keypair; this.publicKey = keypair.publicKey; } signTransaction(tx, additionalSigners) { return __awaiter(this, void 0, void 0, function* () { if (!isVersionedTx(tx)) { tx.sign(this.keypair, ...(additionalSigners || [])); } else { tx.sign([this.keypair, ...(additionalSigners || [])]); } return Promise.resolve(tx); }); } signAllTransactions(txs, additionalSigners) { return __awaiter(this, void 0, void 0, function* () { const promises = []; for (const [id, tx] of txs.entries()) { promises.push(this.signTransaction(tx, additionalSigners && additionalSigners.length > id ? additionalSigners[id] : [])); } return Promise.all(promises); }); } } function sendWithRetries(connection_1, serialized_1, lastValidBlockHeight_1) { return __awaiter(this, arguments, void 0, function* (connection, serialized, lastValidBlockHeight, timesToSend = 5, blockhashCommitment = "finalized") { const txId = yield connection.sendRawTransaction(serialized, { skipPreflight: false, // never skip preflight preflightCommitment: blockhashCommitment, // use specific preflight commitment }); }); } function txToV0(transaction, payer, blockhash) { if (isVersionedTx(transaction)) { return transaction; } else { const v0Message = MessageV0.compile({ instructions: transaction.instructions, payerKey: transaction.feePayer || payer, recentBlockhash: transaction.recentBlockhash || blockhash, }); return new VersionedTransaction(v0Message); } } function setBlockhash(transaction, blockhash) { if (isVersionedTx(transaction)) { transaction.message.recentBlockhash = blockhash; } else { transaction.recentBlockhash = blockhash; } } function simulateTx(connection, tx, commitment, feePayer, convertToV0, alreadySigned) { return __awaiter(this, void 0, void 0, function* () { var _a; let convert = convertToV0 && !alreadySigned; let txCopy = isVersionedTx(tx) ? new VersionedTransaction(tx.message, tx.signatures) : new Transaction(tx); if (convert) { if (isVersionedTx(txCopy)) { txCopy.message.recentBlockhash = FAKE_BLOCKHASH; txCopy = tx; } else { txCopy = new VersionedTransaction(MessageV0.compile({ addressLookupTableAccounts: [], instructions: txCopy.instructions, recentBlockhash: FAKE_BLOCKHASH, payerKey: feePayer, })); } } let result; if (isVersionedTx(txCopy)) { result = yield connection.simulateTransaction(txCopy, { commitment, sigVerify: alreadySigned, replaceRecentBlockhash: !alreadySigned, }); } else { txCopy.recentBlockhash = FAKE_BLOCKHASH; result = yield connection.simulateTransaction(txCopy.compileMessage()); } if (result.value.err !== null) { throw new Error(`Tx simulation Error! Transaction message dump: ${Buffer.from(txCopy.serialize()).toString("base64")}\n Error: ${JSON.stringify(result.value.err)}, logs:\n ${(_a = result.value.logs) === null || _a === void 0 ? void 0 : _a.join("\n")}`); } }); } /** * * @param connection rpc connection * @param wallet wallet interface to sign transaction * @param transactions transaction(s) to send * @param options additional {@link SendAllOptions|options} * @returns */ export function sendAll(connection, wallet, transactions, options) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g; const skipSign = (_a = options === null || options === void 0 ? void 0 : options.skipSign) !== null && _a !== void 0 ? _a : false; const blockhashCommitment = (options === null || options === void 0 ? void 0 : options.blockhashCommitment) === undefined ? ((_b = connection.commitment) !== null && _b !== void 0 ? _b : "confirmed") : options.blockhashCommitment; const simulationCommitment = (_c = options === null || options === void 0 ? void 0 : options.simulationCommtiment) !== null && _c !== void 0 ? _c : blockhashCommitment; const useMinContextSlot = (_d = options === null || options === void 0 ? void 0 : options.useMinContextSlot) !== null && _d !== void 0 ? _d : false; const convertIntoTxV0 = (_e = options === null || options === void 0 ? void 0 : options.convertIntoTxV0) !== null && _e !== void 0 ? _e : true; const skipPreflight = (_f = options === null || options === void 0 ? void 0 : options.skipPreflight) !== null && _f !== void 0 ? _f : false; if (!Array.isArray(transactions)) transactions = [transactions]; if (convertIntoTxV0) { transactions = transactions.map((tx) => txToV0(tx, wallet.publicKey, FAKE_BLOCKHASH)); } let txIds = []; if ((options === null || options === void 0 ? void 0 : options.waitBetweenSend) === undefined || options.waitBetweenSend === 0) { if (!skipPreflight) { yield Promise.all(transactions.map((tx) => simulateTx(connection, tx, simulationCommitment, wallet.publicKey, convertIntoTxV0, skipSign))); } const blockhashResponse = yield connection.getLatestBlockhashAndContext({ commitment: blockhashCommitment, }); let signed = transactions; if (!skipSign) { // set real blockhash transactions.map((tx) => setBlockhash(tx, blockhashResponse.value.blockhash)); // sign after setting correct blockhashes signed = yield wallet.signAllTransactions(transactions, options === null || options === void 0 ? void 0 : options.signers); } txIds = yield Promise.all(signed.map((signedTx) => { var _a; const serialized = Buffer.from(signedTx.serialize()).toString("base64"); (_a = options === null || options === void 0 ? void 0 : options.logger) === null || _a === void 0 ? void 0 : _a.call(options, `Sending tx: ${serialized}`); return connection.sendEncodedTransaction(serialized, { preflightCommitment: simulationCommitment, skipPreflight: false, minContextSlot: useMinContextSlot ? blockhashResponse.context.slot + 1 : undefined, }); })); } else { for (let i = 0; i < transactions.length; i++) { const tx = transactions[i]; const signed = skipSign ? tx : yield wallet.signTransaction(tx, (options === null || options === void 0 ? void 0 : options.signers) && (options === null || options === void 0 ? void 0 : options.signers.length) > i ? options.signers[i] : undefined); if (!skipPreflight) { yield simulateTx(connection, tx, simulationCommitment, wallet.publicKey, convertIntoTxV0, skipSign); } const serialized = Buffer.from(signed.serialize()).toString("base64"); (_g = options === null || options === void 0 ? void 0 : options.logger) === null || _g === void 0 ? void 0 : _g.call(options, `Sending tx: ${serialized}`); txIds.push(yield connection.sendEncodedTransaction(serialized, { preflightCommitment: simulationCommitment, skipPreflight: false, })); yield sleep(options.waitBetweenSend); } } return txIds; }); } //# sourceMappingURL=helpers.js.map