@leda-mint-io/candymachine-client-sdk
Version:
Metaplex Candy Machine Client SDK
302 lines (301 loc) • 14.1 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sendTransactions = exports.SequenceType = exports.awaitTransactionSignatureConfirmation = exports.sendSignedTransaction = exports.sendTransactionWithRetryWithKeypair = void 0;
const web3_js_1 = require("@safecoin/web3.js");
const constants_1 = require("../constants");
const helpers_1 = require("./helpers");
/**
* Attempt to send a transaction to the network.
* @param connection The connection to the cluster.
* @param wallet The wallet to use for signing.
* @param instructions The instructions to sign.
* @param commitment The commitment to use for the transaction.
* @param block The blockhash to use for the transaction.
* @param beforeSend The function to call before sending the transaction.
* @returns The transaction signature.
*/
const sendTransactionWithRetryWithKeypair = (connection, wallet, instructions, commitment = 'singleGossip', block, beforeSend) => __awaiter(void 0, void 0, void 0, function* () {
const transaction = new web3_js_1.Transaction();
instructions.forEach((instruction) => transaction.add(instruction));
transaction.recentBlockhash = (block || (yield connection.getLatestBlockhash(commitment))).blockhash;
transaction.feePayer = wallet.publicKey;
yield wallet.signTransaction(transaction);
if (beforeSend) {
beforeSend();
}
const { txid, slot } = yield sendSignedTransaction({
connection,
signedTransaction: transaction,
});
return { txid, slot };
});
exports.sendTransactionWithRetryWithKeypair = sendTransactionWithRetryWithKeypair;
/**
* Attempt to send a signed transaction to the network.
* @param signedTransaction The signed transaction to send.
* @param connection The connection to the cluster.
* @returns The transaction id and slot.
*/
function sendSignedTransaction({ signedTransaction, connection, timeout = constants_1.DEFAULT_TIMEOUT, }) {
return __awaiter(this, void 0, void 0, function* () {
const rawTransaction = signedTransaction.serialize();
const startTime = (0, helpers_1.getUnixTs)();
let slot = 0;
let txid = yield connection.sendRawTransaction(rawTransaction, {
skipPreflight: true,
});
console.log('Started awaiting confirmation for', txid);
let done = false;
(() => __awaiter(this, void 0, void 0, function* () {
while (!done && (0, helpers_1.getUnixTs)() - startTime < timeout) {
connection.sendRawTransaction(rawTransaction, {
skipPreflight: true,
});
yield (0, helpers_1.sleep)(500);
}
}))();
try {
const confirmation = yield awaitTransactionSignatureConfirmation(txid, timeout, connection, 'confirmed', true);
if (!confirmation)
throw new Error('Timed out awaiting confirmation on transaction');
if (confirmation.err) {
console.error(confirmation.err);
throw new Error('Transaction failed: Custom instruction error');
}
slot = (confirmation === null || confirmation === void 0 ? void 0 : confirmation.slot) || 0;
}
catch (err) {
console.error('Timeout Error caught', err);
// @ts-ignore
if (err.timeout) {
throw new Error('Timed out awaiting confirmation on transaction');
}
let simulateResult = null;
try {
simulateResult = (yield simulateTransaction(connection, signedTransaction, 'single')).value;
}
catch (e) {
console.error('Simulate Transaction error', e);
}
if (simulateResult && simulateResult.err) {
if (simulateResult.logs) {
for (let i = simulateResult.logs.length - 1; i >= 0; --i) {
const line = simulateResult.logs[i];
if (line.startsWith('Program log: ')) {
throw new Error('Transaction failed: ' + line.slice('Program log: '.length));
}
}
}
throw new Error(JSON.stringify(simulateResult.err));
}
console.error('Got this far.');
txid = simulateResult === null || simulateResult === void 0 ? void 0 : simulateResult.err;
// throw new Error('Transaction failed');
}
finally {
done = true;
}
console.log('Latency (ms)', txid, (0, helpers_1.getUnixTs)() - startTime);
return { txid, slot };
});
}
exports.sendSignedTransaction = sendSignedTransaction;
/**
* Simualate a transaction.
* @param connection The connection to the cluster.
* @param transaction The transaction to simulate.
* @param commitment The commitment to use for the transaction.
* @returns The simulated transaction response.
*/
function simulateTransaction(connection, transaction, commitment) {
return __awaiter(this, void 0, void 0, function* () {
transaction.recentBlockhash = (yield connection.getLatestBlockhash()).blockhash;
const signData = transaction.serializeMessage();
const encodedTransaction = signData.toString('base64');
const config = { encoding: 'base64', commitment };
const args = [encodedTransaction, config];
// @ts-ignore
const res = yield connection._rpcRequest('simulateTransaction', args);
if (res.error) {
throw new Error('failed to simulate transaction: ' + res.error.message);
}
return res.result;
});
}
/**
* Wait for a transaction to be confirmed.
* @param txid The transaction id to await confirmation for.
* @param timeout The timeout in milliseconds.
* @param connection The connection to the cluster.
* @param commitment The commitment to use for the transaction.
* @param queryStatus Whether to query the status of the transaction.
* @returns The transaction signature.
*/
function awaitTransactionSignatureConfirmation(txid, timeout, connection, commitment = 'recent', queryStatus = false) {
return __awaiter(this, void 0, void 0, function* () {
let done = false;
let status = {
slot: 0,
confirmations: 0,
err: null,
};
let subId = 0;
// eslint-disable-next-line no-async-promise-executor
status = yield new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
setTimeout(() => {
if (done) {
return;
}
done = true;
console.warn('Rejecting for timeout...');
reject({ timeout: true });
}, timeout);
try {
subId = connection.onSignature(txid, (result, context) => {
done = true;
status = {
err: result.err,
slot: context.slot,
confirmations: 0,
};
if (result.err) {
console.warn('Rejected via websocket', result.err);
reject(status);
}
else {
console.log('Resolved via websocket', result);
resolve(status);
}
}, commitment);
}
catch (e) {
done = true;
console.error('WS error in setup', txid, e);
}
while (!done && queryStatus) {
// eslint-disable-next-line no-loop-func
;
(() => __awaiter(this, void 0, void 0, function* () {
try {
const signatureStatuses = yield connection.getSignatureStatuses([txid]);
status = signatureStatuses && signatureStatuses.value[0];
if (!done) {
if (!status) {
console.log('REST null result for', txid, status);
}
else if (status.err) {
console.error('REST error for', txid, status);
done = true;
reject(status.err);
}
else if (!status.confirmations) {
console.log('REST no confirmations for', txid, status);
}
else {
console.log('REST confirmation for', txid, status);
done = true;
resolve(status);
}
}
}
catch (e) {
if (!done) {
console.error('REST connection error: txid', txid, e);
}
}
}))();
yield (0, helpers_1.sleep)(2000);
}
}));
// //@ts-ignore
// if (connection._signatureSubscriptions[subId])
connection.removeSignatureListener(subId);
done = true;
console.log('Returning status', status);
return status;
});
}
exports.awaitTransactionSignatureConfirmation = awaitTransactionSignatureConfirmation;
var SequenceType;
(function (SequenceType) {
SequenceType[SequenceType["Sequential"] = 0] = "Sequential";
SequenceType[SequenceType["Parallel"] = 1] = "Parallel";
SequenceType[SequenceType["StopOnFailure"] = 2] = "StopOnFailure";
})(SequenceType = exports.SequenceType || (exports.SequenceType = {}));
/**
* Execute a sequence of transactions.
* @returns The transaction signature.
*/
const sendTransactions = (connection, wallet, instructionSet, signersSet, sequenceType = SequenceType.Parallel, commitment = 'singleGossip', successCallback = (txid, ind) => { }, failCallback = (txid, ind) => false, block, beforeTransactions = [], afterTransactions = []) => __awaiter(void 0, void 0, void 0, function* () {
if (!wallet.publicKey)
throw new Error('Wallet not connected');
const unsignedTxns = beforeTransactions;
if (!block) {
block = yield connection.getLatestBlockhash(commitment);
}
for (let i = 0; i < instructionSet.length; i++) {
const instructions = instructionSet[i];
const signers = signersSet[i];
if (instructions.length === 0) {
continue;
}
let transaction = new web3_js_1.Transaction();
instructions.forEach((instruction) => transaction.add(instruction));
transaction.recentBlockhash = block.blockhash;
transaction.setSigners(
// fee payed by the wallet owner
wallet.publicKey, ...signers.map((s) => s.publicKey));
if (signers.length > 0) {
transaction.partialSign(...signers);
}
unsignedTxns.push(transaction);
}
unsignedTxns.push(...afterTransactions);
const partiallySignedTransactions = unsignedTxns.filter((t) => t.signatures.find((sig) => sig.publicKey.equals(wallet.publicKey)));
const fullySignedTransactions = unsignedTxns.filter((t) => !t.signatures.find((sig) => sig.publicKey.equals(wallet.publicKey)));
let signedTxns = yield wallet.signAllTransactions(partiallySignedTransactions);
signedTxns = fullySignedTransactions.concat(signedTxns);
const pendingTxns = [];
console.log('Signed txns length', signedTxns.length, 'vs handed in length', instructionSet.length);
for (let i = 0; i < signedTxns.length; i++) {
const signedTxnPromise = sendSignedTransaction({
connection,
signedTransaction: signedTxns[i],
});
if (sequenceType !== SequenceType.Parallel) {
try {
yield signedTxnPromise.then(({ txid, slot }) => successCallback(txid, i));
pendingTxns.push(signedTxnPromise);
}
catch (e) {
console.log('Failed at txn index:', i);
console.log('Caught failure:', e);
failCallback(signedTxns[i], i);
if (sequenceType === SequenceType.StopOnFailure) {
return {
number: i,
txs: yield Promise.all(pendingTxns),
};
}
}
}
else {
pendingTxns.push(signedTxnPromise);
}
}
if (sequenceType !== SequenceType.Parallel) {
const result = yield Promise.all(pendingTxns);
return { number: signedTxns.length, txs: result };
}
return { number: signedTxns.length, txs: yield Promise.all(pendingTxns) };
});
exports.sendTransactions = sendTransactions;