UNPKG

@reservoir0x/relay-sdk

Version:

Relay is the Fastest and Cheapest Way to Bridge and Transact Across Chains.

287 lines 11.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.sendTransactionSafely = void 0; const logger_js_1 = require("./logger.js"); const axios_js_1 = require("../utils/axios.js"); const client_js_1 = require("../client.js"); const index_js_1 = require("../errors/index.js"); const repeatUntilOk_js_1 = require("../utils/repeatUntilOk.js"); const getTenderlyDetails_js_1 = require("../utils/getTenderlyDetails.js"); async function sendTransactionSafely(chainId, items, step, wallet, setTxHashes, setInternalTxHashes, request, headers, crossChainIntentChainId, isValidating, details) { const client = (0, client_js_1.getClient)(); try { await (0, repeatUntilOk_js_1.repeatUntilOk)(async () => { const walletChainId = await wallet.getChainId(); return walletChainId === chainId; }, 10, undefined, 250); } catch (e) { const walletChainId = await wallet.getChainId(); throw `Current chain id: ${walletChainId} does not match expected chain id: ${chainId} `; } let receipt; let transactionCancelled = false; let confirmationError = false; const pollingInterval = client.pollingInterval ?? 5000; const maximumAttempts = client.maxPollingAttemptsBeforeTimeout ?? (2.5 * 60 * 1000) / pollingInterval; let waitingForConfirmation = true; let attemptCount = 0; let txHash; const isBatchTransaction = Boolean(Array.isArray(items) && items.length > 1 && wallet.handleBatchTransactionStep); if (isBatchTransaction) { txHash = await wallet.handleBatchTransactionStep?.(chainId, items); } else { txHash = await wallet.handleSendTransactionStep(chainId, Array.isArray(items) ? items[0] : items, step); } if (txHash === 'null') { throw 'User rejected the request'; } const check = Array.isArray(items) ? items.find((item) => item.check)?.check : items.check; postTransactionToSolver({ txHash, chainId, step, request, headers }); if (!isBatchTransaction && !Array.isArray(items) && chainId === details?.currencyOut?.currency?.chainId) { postSameChainTransactionToSolver({ calldata: JSON.stringify({ ...items.data, txHash }), chainId, step, request, headers }); } if (!txHash) { throw Error('Transaction hash not returned from handleSendTransactionStep method'); } setTxHashes([ { txHash: txHash, chainId: chainId, isBatchTx: isBatchTransaction } ]); const validate = (res) => { (0, client_js_1.getClient)()?.log(['Execute Steps: Polling for confirmation', res], logger_js_1.LogLevel.Verbose); if (res.status === 200 && res.data && res.data.status === 'failure') { throw Error('Transaction failed'); } if (res.status === 200 && res.data && res.data.status === 'fallback') { throw Error('Transaction failed: Refunded'); } if (res.status === 200 && res.data && res.data.status === 'success') { if (txHash) { setInternalTxHashes([ { txHash: txHash, chainId: chainId, isBatchTx: isBatchTransaction } ]); } const chainTxHashes = res.data?.txHashes?.map((hash) => { return { txHash: hash, chainId: res?.data?.destinationChainId ?? crossChainIntentChainId }; }); setTxHashes(chainTxHashes); return true; } return false; }; const pollForConfirmation = async () => { isValidating?.(); while (waitingForConfirmation && attemptCount < maximumAttempts && !transactionCancelled && !confirmationError) { let res; if (check?.endpoint && !request?.data?.useExternalLiquidity) { res = await axios_js_1.axios.request({ url: `${request.baseURL}${check?.endpoint}`, method: check?.method, headers: headers }); } if (!res || validate(res)) { waitingForConfirmation = false; } else if (res) { if (res.data.status !== 'pending') { isValidating?.(res); attemptCount++; } await new Promise((resolve) => setTimeout(resolve, pollingInterval)); } } if (attemptCount >= maximumAttempts) { if (receipt) { throw new index_js_1.SolverStatusTimeoutError(txHash, attemptCount); } else { throw new index_js_1.DepositTransactionTimeoutError(txHash, attemptCount); } } if (transactionCancelled) { throw Error('Transaction was cancelled'); } return true; }; const waitForTransaction = () => { const controller = new AbortController(); const signal = controller.signal; return { promise: wallet .handleConfirmTransactionStep(txHash, chainId, (replacementTxHash) => { if (signal.aborted) { return; } setTxHashes([{ txHash: replacementTxHash, chainId: chainId }]); txHash = replacementTxHash; attemptCount = 0; (0, client_js_1.getClient)()?.log(['Transaction replaced', replacementTxHash], logger_js_1.LogLevel.Verbose); postTransactionToSolver({ txHash: replacementTxHash, chainId, step, request, headers }); if (!isBatchTransaction && !Array.isArray(items) && chainId === details?.currencyOut?.currency?.chainId) { postSameChainTransactionToSolver({ calldata: JSON.stringify({ ...items.data, replacementTxHash }), chainId, step, request, headers }); } }, () => { if (signal.aborted) { return; } transactionCancelled = true; (0, client_js_1.getClient)()?.log(['Transaction cancelled'], logger_js_1.LogLevel.Verbose); }) .then((data) => { if (signal.aborted) { return; } receipt = data; if (receipt && typeof receipt === 'object' && 'status' in receipt && receipt.status === 'reverted') { throw 'Transaction Reverted'; } (0, client_js_1.getClient)()?.log(['Transaction Receipt obtained', receipt], logger_js_1.LogLevel.Verbose); }) .catch(async (error) => { if (signal.aborted) { return; } let tenderlyError = null; if (receipt && receipt.transactionHash) { tenderlyError = await (0, getTenderlyDetails_js_1.getTenderlyDetails)(receipt.transactionHash); } (0, client_js_1.getClient)()?.log(['Error in handleConfirmTransactionStep', error], logger_js_1.LogLevel.Error); if (error.message === 'Transaction cancelled') { transactionCancelled = true; } else { confirmationError = true; throw new index_js_1.TransactionConfirmationError(error, receipt, tenderlyError); } }), controller }; }; if (chainId === 8253038) { return true; } if (isBatchTransaction) { await pollForConfirmation(); } else if (step.id === 'approve' || details?.currencyOut?.currency?.chainId === 8253038 || request?.data?.useExternalLiquidity) { await waitForTransaction().promise; if (details?.currencyOut?.currency?.chainId !== 8253038 && !request?.data?.useExternalLiquidity) { await pollForConfirmation(); } } else { const { promise: receiptPromise, controller: receiptController } = waitForTransaction(); const confirmationPromise = pollForConfirmation(); await Promise.race([receiptPromise, confirmationPromise]); if (waitingForConfirmation) { await confirmationPromise; } if (!receipt) { if (!check) { await receiptPromise; } else { receiptController.abort(); } } } return true; } exports.sendTransactionSafely = sendTransactionSafely; const postSameChainTransactionToSolver = async ({ calldata, chainId, request, headers, step }) => { if (calldata && step.requestId && chainId) { (0, client_js_1.getClient)()?.log(['Posting same chain transaction to notify the solver'], logger_js_1.LogLevel.Verbose); try { const triggerData = { tx: calldata, chainId: chainId.toString(), requestId: step.requestId }; axios_js_1.axios .request({ url: `${request.baseURL}/transactions/single`, method: 'POST', headers: headers, data: triggerData }) .then(() => { (0, client_js_1.getClient)()?.log(['Same chain transaction notified to the solver'], logger_js_1.LogLevel.Verbose); }); } catch (e) { (0, client_js_1.getClient)()?.log(['Failed to post same chain transaction to solver', e], logger_js_1.LogLevel.Warn); } } }; const postTransactionToSolver = async ({ txHash, chainId, request, headers, step }) => { if (step.id === 'deposit' && txHash) { (0, client_js_1.getClient)()?.log(['Posting transaction to notify the solver'], logger_js_1.LogLevel.Verbose); try { const triggerData = { txHash, chainId: chainId.toString() }; axios_js_1.axios .request({ url: `${request.baseURL}/transactions/index`, method: 'POST', headers: headers, data: triggerData }) .then(() => { (0, client_js_1.getClient)()?.log(['Transaction notified to the solver'], logger_js_1.LogLevel.Verbose); }); } catch (e) { (0, client_js_1.getClient)()?.log(['Failed to post transaction to solver', e], logger_js_1.LogLevel.Warn); } } }; //# sourceMappingURL=transaction.js.map