@debridge-finance/solana-utils
Version:
Common utils package to power communication with Solana contracts at deBridge
219 lines • 9.95 kB
JavaScript
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