UNPKG

@leda-mint-io/candymachine-client-sdk

Version:

Metaplex Candy Machine Client SDK

302 lines (301 loc) 14.1 kB
"use strict"; 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;