@reservoir0x/relay-sdk
Version:
Relay is the Fastest and Cheapest Way to Bridge and Transact Across Chains.
398 lines • 20.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeSteps = void 0;
const pollApi_js_1 = require("./pollApi.js");
const index_js_1 = require("../utils/index.js");
const client_js_1 = require("../client.js");
const logger_js_1 = require("../utils/logger.js");
const transaction_js_1 = require("./transaction.js");
const prepareBatchTransaction_js_1 = require("./prepareBatchTransaction.js");
async function executeSteps(chainId, request = {}, wallet, setState, newJson, stepOptions) {
const client = (0, client_js_1.getClient)();
if (client?.baseApiUrl) {
request.baseURL = client.baseApiUrl;
}
const pollingInterval = client.pollingInterval ?? 5000;
const maximumAttempts = client.maxPollingAttemptsBeforeTimeout ??
(2.5 * 60 * 1000) / pollingInterval;
const chain = client.chains.find((chain) => chain.id === chainId);
if (!chain) {
throw `Unable to find chain: Chain id ${chainId}`;
}
let json = newJson;
let isAtomicBatchSupported = false;
try {
if (!json) {
client.log(['Execute Steps: Fetching Steps', request], logger_js_1.LogLevel.Verbose);
const res = await index_js_1.axios.request(request);
json = res.data;
if (res.status !== 200)
throw json;
client.log(['Execute Steps: Steps retrieved', json], logger_js_1.LogLevel.Verbose);
}
if (json.error || !json.steps)
throw json;
if ((0, prepareBatchTransaction_js_1.canBatchTransactions)(json.steps)) {
isAtomicBatchSupported = Boolean(wallet?.supportsAtomicBatch &&
(await wallet?.supportsAtomicBatch(chainId)));
if (isAtomicBatchSupported) {
const batchedStep = (0, prepareBatchTransaction_js_1.prepareBatchTransaction)(json.steps);
json.steps = [batchedStep];
}
}
setState({
steps: [...json?.steps],
fees: { ...json?.fees },
breakdown: json?.breakdown,
details: json?.details
});
let incompleteStepIndex = -1;
let incompleteStepItemIndex = -1;
json.steps.find((step, i) => {
if (!step.items) {
return false;
}
incompleteStepItemIndex = step.items.findIndex((item) => item.status == 'incomplete');
if (incompleteStepItemIndex !== -1) {
incompleteStepIndex = i;
return true;
}
});
if (incompleteStepIndex === -1) {
client.log(['Execute Steps: all steps complete'], logger_js_1.LogLevel.Verbose);
return json;
}
const step = json.steps[incompleteStepIndex];
if (stepOptions && stepOptions[step.id]) {
const currentStepOptions = stepOptions[step.id];
step.items?.forEach((stepItem) => {
if (currentStepOptions.gasLimit) {
stepItem.data.gas = currentStepOptions.gasLimit;
}
});
}
let stepItems = json.steps[incompleteStepIndex].items;
if (!stepItems) {
client.log(['Execute Steps: skipping step, no items in step'], logger_js_1.LogLevel.Verbose);
return json;
}
let { kind } = step;
let stepItem = stepItems[incompleteStepItemIndex];
if (!stepItem.data) {
client.log(['Execute Steps: step item data is missing, begin polling'], logger_js_1.LogLevel.Verbose);
json = (await (0, pollApi_js_1.pollUntilHasData)(request, (json) => {
client.log(['Execute Steps: step item data is missing, polling', json], logger_js_1.LogLevel.Verbose);
const data = json;
return data?.steps?.[incompleteStepIndex].items?.[incompleteStepItemIndex].data ||
data?.steps?.[incompleteStepIndex].items?.[incompleteStepItemIndex]
.status === 'complete'
? true
: false;
}));
if (!json.steps || !json.steps[incompleteStepIndex].items)
throw json;
const items = json.steps[incompleteStepIndex].items;
if (!items ||
!items[incompleteStepItemIndex] ||
!items[incompleteStepItemIndex].data) {
throw json;
}
stepItems = items;
stepItem = items[incompleteStepItemIndex];
setState({
steps: [...json?.steps],
fees: { ...json?.fees },
breakdown: json?.breakdown,
details: json?.details
});
}
client.log([`Execute Steps: Begin processing step items for: ${step.action}`], logger_js_1.LogLevel.Verbose);
const promises = stepItems
.filter((stepItem) => stepItem.status === 'incomplete')
.map((stepItem) => {
return new Promise(async (resolve, reject) => {
try {
const stepData = stepItem.data;
if (!json) {
return;
}
switch (kind) {
case 'transaction': {
try {
client.log([
'Execute Steps: Begin transaction step for, sending transaction'
], logger_js_1.LogLevel.Verbose);
const transactionChainId = stepItem?.data?.chainId ?? chainId;
const crossChainIntentChainId = chainId;
stepItem.progressState = 'confirming';
setState({
steps: [...json.steps],
fees: { ...json?.fees },
breakdown: json?.breakdown,
details: json?.details
});
const transactionStepItems = isAtomicBatchSupported && incompleteStepItemIndex === 0
? stepItems
: stepItem;
await (0, transaction_js_1.sendTransactionSafely)(transactionChainId, transactionStepItems, step, wallet, (txHashes) => {
client.log([
'Execute Steps: Transaction step, got transactions',
txHashes
], logger_js_1.LogLevel.Verbose);
stepItem.txHashes = txHashes;
if (json) {
setState({
steps: [...json.steps],
fees: { ...json?.fees },
breakdown: json?.breakdown,
details: json?.details
});
}
}, (internalTxHashes) => {
stepItem.internalTxHashes = internalTxHashes;
if (json) {
setState({
steps: [...json.steps],
fees: { ...json?.fees },
breakdown: json?.breakdown,
details: json?.details
});
}
}, request, undefined, crossChainIntentChainId, (res) => {
if (res && res.data.status === 'delayed') {
stepItem.progressState = 'validating_delayed';
}
else {
stepItem.progressState = 'validating';
}
if (json) {
setState({
steps: [...json.steps],
fees: { ...json?.fees },
breakdown: json?.breakdown,
details: json?.details
});
}
}, json?.details);
}
catch (e) {
throw e;
}
break;
}
case 'signature': {
let signature;
const signData = stepData['sign'];
const postData = stepData['post'];
client.log(['Execute Steps: Begin signature step'], logger_js_1.LogLevel.Verbose);
if (signData) {
stepItem.progressState = 'signing';
setState({
steps: [...json.steps],
fees: { ...json?.fees },
breakdown: json?.breakdown,
details: json?.details
});
signature = await wallet.handleSignMessageStep(stepItem, step);
if (signature) {
request.params = {
...request.params,
signature
};
}
}
if (postData) {
client.log(['Execute Steps: Posting order'], logger_js_1.LogLevel.Verbose);
stepItem.progressState = 'posting';
setState({
steps: [...json.steps],
fees: { ...json?.fees },
breakdown: json?.breakdown,
details: json?.details
});
const postOrderUrl = new URL(`${request.baseURL}${postData.endpoint}`);
const headers = {
'Content-Type': 'application/json'
};
try {
const getData = async function () {
let response = await index_js_1.axios.request({
url: postOrderUrl.href,
data: postData.body
? JSON.stringify(postData.body)
: undefined,
method: postData.method,
params: request.params,
headers
});
return response;
};
const res = await getData();
if (res.data &&
res.data.steps &&
Array.isArray(res.data.steps)) {
json.steps = [...json.steps, ...res.data.steps];
setState({
steps: [...json.steps, ...res.data.steps],
fees: { ...json.fees },
breakdown: json.breakdown,
details: json.details
});
client.log([
`Execute Steps: New steps appended from ${postData.endpoint}`,
res.data.steps
], logger_js_1.LogLevel.Verbose);
break;
}
if (stepItem?.check) {
stepItem.progressState = 'validating';
setState({
steps: [...json.steps],
fees: { ...json?.fees },
breakdown: json?.breakdown,
details: json?.details
});
stepItem.isValidatingSignature = true;
setState({
steps: [...json?.steps],
fees: { ...json?.fees },
breakdown: json?.breakdown,
details: json?.details
});
await (0, pollApi_js_1.pollUntilOk)({
url: `${request.baseURL}${stepItem?.check.endpoint}`,
method: stepItem?.check.method,
headers
}, (res) => {
client.log([
`Execute Steps: Polling execute status to check if indexed`,
res
], logger_js_1.LogLevel.Verbose);
if (res?.data?.status === 'success' &&
res?.data?.txHashes) {
const chainTxHashes = res.data?.txHashes?.map((hash) => {
return {
txHash: hash,
chainId: res.data.destinationChainId ?? chain?.id
};
});
if (res?.data?.inTxHashes) {
const chainInTxHashes = res.data?.inTxHashes?.map((hash) => {
return {
txHash: hash,
chainId: chain?.id ?? res.data.originChainId
};
});
stepItem.internalTxHashes = chainInTxHashes;
}
stepItem.txHashes = chainTxHashes;
return true;
}
else if (res?.data?.status === 'failure') {
throw Error(res?.data?.details || 'Transaction failed');
}
else if (res?.data?.status === 'delayed') {
stepItem.progressState = 'validating_delayed';
}
return false;
}, maximumAttempts, 0, pollingInterval);
}
if (res.status > 299 || res.status < 200)
throw res.data;
if (res.data.results) {
stepItem.orderData = res.data.results;
}
else if (res.data && res.data.orderId) {
stepItem.orderData = [
{
orderId: res.data.orderId,
crossPostingOrderId: res.data.crossPostingOrderId,
orderIndex: res.data.orderIndex || 0
}
];
}
setState({
steps: [...json?.steps],
fees: { ...json?.fees },
breakdown: json?.breakdown,
details: json?.details
});
}
catch (err) {
throw err;
}
}
break;
}
default:
break;
}
stepItem.status = 'complete';
stepItem.progressState = 'complete';
stepItem.isValidatingSignature = false;
setState({
steps: [...json?.steps],
fees: { ...json?.fees },
breakdown: json?.breakdown,
details: json?.details
});
resolve(stepItem);
}
catch (e) {
const error = e;
const errorMessage = error
? error.message
: 'Error: something went wrong';
if (error && json?.steps) {
json.steps[incompleteStepIndex].error = errorMessage;
stepItem.error = errorMessage;
stepItem.errorData = e?.response?.data || e;
stepItem.isValidatingSignature = false;
setState({
steps: [...json?.steps],
fees: { ...json?.fees },
breakdown: json?.breakdown,
details: json?.details
});
}
reject(error);
}
});
});
await Promise.all(promises);
return await executeSteps(chainId, request, wallet, setState, json, stepOptions);
}
catch (err) {
client.log(['Execute Steps: An error occurred', err], logger_js_1.LogLevel.Error);
const error = err && err?.response?.data ? err.response.data : err;
let refunded = false;
if (error && error.message) {
refunded = error.message.includes('Refunded');
}
else if (error && error.includes) {
refunded = error.includes('Refunded');
}
if (json) {
json.error = error;
setState({
steps: json.steps ? [...json.steps] : [{}],
fees: { ...json?.fees },
breakdown: json?.breakdown,
details: json?.details,
refunded: refunded,
error
});
}
else {
json = {
error,
steps: [],
refunded
};
setState(json);
}
throw err;
}
}
exports.executeSteps = executeSteps;
//# sourceMappingURL=executeSteps.js.map