UNPKG

@axelar-network/axelarjs-sdk

Version:
951 lines 70.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); 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()); }); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AxelarGMPRecoveryAPI = exports.getCosmosSigner = exports.RouteDir = exports.GMPErrorMap = void 0; const types_1 = require("../types"); const AxelarRecoveryApi_1 = require("./AxelarRecoveryApi"); const EVMClient_1 = __importDefault(require("./client/EVMClient")); const IAxelarExecutable_1 = __importDefault(require("../abi/IAxelarExecutable")); const ethers_1 = require("ethers"); const AxelarQueryAPI_1 = require("../AxelarQueryAPI"); const chain_1 = __importDefault(require("./constants/chain")); const contractEventHelper_1 = require("./helpers/contractEventHelper"); const erc20Abi_json_1 = __importDefault(require("../abi/erc20Abi.json")); const AxelarGateway_1 = require("../AxelarGateway"); const utils_1 = require("@mysten/sui/utils"); const transactions_1 = require("@mysten/sui/transactions"); const bcs_1 = require("@mysten/sui/bcs"); const error_1 = require("./constants/error"); const helpers_1 = require("./helpers"); const utils_2 = require("../../utils"); const query_1 = require("@axelar-network/axelarjs-types/axelar/evm/v1beta1/query"); const types_2 = require("@axelar-network/axelarjs-types/axelar/evm/v1beta1/types"); const utils_3 = require("ethers/lib/utils"); const bs58_1 = __importDefault(require("bs58")); const web3_js_1 = require("@solana/web3.js"); const s3_1 = __importDefault(require("./constants/s3")); const stargate_1 = require("@cosmjs/stargate"); const cosmosGasReceiverOptions_1 = require("./constants/cosmosGasReceiverOptions"); const chains_1 = require("../../chains"); const StellarSdk = __importStar(require("@stellar/stellar-sdk")); const stellarHelper_1 = require("./helpers/stellarHelper"); const xrpl_1 = require("xrpl"); const xrplHelper_1 = require("./helpers/xrplHelper"); exports.GMPErrorMap = { [AxelarRecoveryApi_1.GMPStatus.CANNOT_FETCH_STATUS]: types_1.ApproveGatewayError.FETCHING_STATUS_FAILED, [AxelarRecoveryApi_1.GMPStatus.DEST_EXECUTED]: types_1.ApproveGatewayError.ALREADY_EXECUTED, [AxelarRecoveryApi_1.GMPStatus.DEST_GATEWAY_APPROVED]: types_1.ApproveGatewayError.ALREADY_APPROVED, }; var RouteDir; (function (RouteDir) { RouteDir["COSMOS_TO_EVM"] = "cosmos_to_evm"; RouteDir["EVM_TO_COSMOS"] = "evm_to_cosmos"; RouteDir["EVM_TO_EVM"] = "evm_to_evm"; RouteDir["COSMOS_TO_COSMOS"] = "cosmos_to_cosmos"; })(RouteDir || (exports.RouteDir = RouteDir = {})); const getCosmosSigner = (rpcUrl, offlineDirectSigner) => __awaiter(void 0, void 0, void 0, function* () { return stargate_1.SigningStargateClient.connectWithSigner(rpcUrl, offlineDirectSigner); }); exports.getCosmosSigner = getCosmosSigner; function matchesOriginalTokenPayment(token, denomOnSrcChain) { return token === "autocalculate" || (token === null || token === void 0 ? void 0 : token.denom) === denomOnSrcChain; } function getIBCDenomOnSrcChain(denomOnAxelar, selectedChain, chainConfigs) { const asset = chainConfigs["assets"][denomOnAxelar !== null && denomOnAxelar !== void 0 ? denomOnAxelar : "uaxl"]; const assetOnSrcChain = asset["chain_aliases"][selectedChain.chainName.toLowerCase()]; const ibcDenom = assetOnSrcChain === null || assetOnSrcChain === void 0 ? void 0 : assetOnSrcChain.ibcDenom; if (!ibcDenom) { throw new Error("cannot find token that matches original gas payment"); } return ibcDenom; } class AxelarGMPRecoveryAPI extends AxelarRecoveryApi_1.AxelarRecoveryApi { constructor(config) { super(config); this.axelarQueryApi = new AxelarQueryAPI_1.AxelarQueryAPI({ environment: config.environment, axelarRpcUrl: this.axelarRpcUrl, axelarLcdUrl: this.axelarLcdUrl, }); } getCidFromSrcTxHash(destChainId, messageId, eventIndex) { var _a; const chainId = (_a = chain_1.default[this.environment].networkInfo[destChainId.toLowerCase()]) === null || _a === void 0 ? void 0 : _a.chainId; return (0, helpers_1.getCommandId)(messageId, eventIndex, chainId); } doesTxMeetConfirmHt(chain, txHash, provider) { return __awaiter(this, void 0, void 0, function* () { const confirmations = yield this.getSigner(chain, { useWindowEthereum: false, provider }) .provider.getTransactionReceipt(txHash) .then((receipt) => __awaiter(this, void 0, void 0, function* () { if (!receipt) { const gmpTx = yield this.fetchGMPTransaction(txHash); const currentBlock = yield this.getSigner(chain, { useWindowEthereum: false, provider, }).provider.getBlockNumber(); return currentBlock - gmpTx.call.blockNumber; } return receipt.confirmations; })); return this.axelarQueryApi .getConfirmationHeight(chain) .then((minConfirmHeight) => minConfirmHeight <= confirmations) .catch(() => undefined); }); } isEVMEventFailed(eventResponse) { var _a; if (!eventResponse) return undefined; return [types_2.Event_Status.STATUS_FAILED, types_2.Event_Status.STATUS_UNSPECIFIED].includes((_a = eventResponse.event) === null || _a === void 0 ? void 0 : _a.status); } isEVMEventConfirmed(eventResponse) { var _a; if (!eventResponse) return undefined; return ((_a = eventResponse.event) === null || _a === void 0 ? void 0 : _a.status) === types_2.Event_Status.STATUS_CONFIRMED; } isEVMEventCompleted(eventResponse) { var _a; if (!eventResponse) return undefined; return ((_a = eventResponse.event) === null || _a === void 0 ? void 0 : _a.status) === types_2.Event_Status.STATUS_COMPLETED; } isManualRelayOptions(value) { if (!value || typeof value !== "object") { return false; } return ("selfSigning" in value || "escapeAfterConfirm" in value || "messageId" in value || "evmWalletDetails" in value); } normalizeManualRelayArgs(evmWalletDetailsOrOptions, escapeAfterConfirm = true, messageId, cosmosWalletDetails, useSelfSigning = false) { var _a; if (!this.isManualRelayOptions(evmWalletDetailsOrOptions)) { return { evmWalletDetails: evmWalletDetailsOrOptions || { useWindowEthereum: true }, escapeAfterConfirm, messageId, cosmosWalletDetails, useSelfSigning, }; } const options = evmWalletDetailsOrOptions; const resolvedSelfSigning = this.resolveSelfSigningOptions(options.selfSigning); if (options.evmWalletDetails && resolvedSelfSigning.evmWalletDetails) { console.warn("[recovery manual relay evm wallet precedence]", { reason: "both evmWalletDetails and selfSigning.evmWalletDetails were provided", using: "evmWalletDetails", }); } return { evmWalletDetails: options.evmWalletDetails || resolvedSelfSigning.evmWalletDetails || { useWindowEthereum: true }, escapeAfterConfirm: (_a = options.escapeAfterConfirm) !== null && _a !== void 0 ? _a : true, messageId: options.messageId, cosmosWalletDetails: resolvedSelfSigning.cosmosWalletDetails, useSelfSigning: resolvedSelfSigning.shouldSelfSign, }; } buildSelfSigningContext(cosmosWalletDetails, useSelfSigning = false) { if (!useSelfSigning) { return {}; } const cosmosSignerDetails = this.getCosmosSignerDetails(cosmosWalletDetails); if (!cosmosSignerDetails) { return { error: "Recovery self-signing requires a cosmos wallet signer" }; } return { selfSigningOptions: { cosmosWalletDetails: cosmosSignerDetails } }; } normalizeTxResponse(tx) { if (tx && typeof tx === "object" && "hash" in tx && !("transactionHash" in tx)) { return Object.assign(Object.assign({}, tx), { transactionHash: tx.hash }); } return tx; } getEvmEvent(srcChainId, destChainId, srcTxHash, srcTxEventIndex, evmWalletDetails) { return __awaiter(this, void 0, void 0, function* () { var _a; const eventIndex = srcTxEventIndex !== null && srcTxEventIndex !== void 0 ? srcTxEventIndex : (yield this.getEventIndex(srcChainId, srcTxHash, evmWalletDetails) .then((index) => index) .catch(() => -1)); if (eventIndex === -1) { return { success: false, errorMessage: `getEvmEvent(): could not find event index for ${srcTxHash}`, commandId: "", eventResponse: query_1.EventResponse.create({}), infoLog: "", }; } const eventResponse = yield this.axelarQueryApi.getEVMEvent(srcChainId, srcTxHash, eventIndex); const isCallContract = ((_a = eventResponse === null || eventResponse === void 0 ? void 0 : eventResponse.event) === null || _a === void 0 ? void 0 : _a.contractCall) ? true : false; const messageId = isCallContract ? `${srcTxHash}-${eventIndex}` : srcTxHash; const commandId = this.getCidFromSrcTxHash(destChainId, messageId, eventIndex); if (!eventResponse || this.isEVMEventFailed(eventResponse)) { const errorMessage = this.isEVMEventFailed(eventResponse) ? `getEvmEvent(): event on source chain is not successful for: ${srcTxHash}` : `getEvmEvent(): could not determine status of event: ${srcTxHash}`; return { success: false, errorMessage, commandId, eventResponse: query_1.EventResponse.create({}), infoLog: `srcTxHash: ${srcTxHash}, generated commandId: ${commandId}`, }; } return { success: true, commandId, eventResponse, errorMessage: "", infoLog: `${srcTxHash} correspondes to command ID: ${commandId}`, }; }); } findEventAndConfirmIfNeeded(srcChain, destChain, txHash, txEventIndex, evmWalletDetails, selfSigningOptions) { return __awaiter(this, void 0, void 0, function* () { if (this.debugMode) console.debug(`confirmation: checking whether ${txHash} needs to be confirmed on Axelar`); const evmEvent = yield this.getEvmEvent(srcChain, destChain, txHash, txEventIndex); const { infoLog: getEvmEventInfoLog } = evmEvent; if (this.debugMode) console.debug(`confirmation: ${getEvmEventInfoLog}`); if (this.isEVMEventCompleted(evmEvent.eventResponse) || this.isEVMEventConfirmed(evmEvent.eventResponse)) { return { success: true, commandId: evmEvent.commandId, eventResponse: evmEvent.eventResponse, infoLogs: [ `confirmation: event for ${txHash} was already detected on the network and did not need to be confirmed`, ], }; } else { const isConfirmFinalized = yield this.doesTxMeetConfirmHt(srcChain, txHash, evmWalletDetails.provider); if (!isConfirmFinalized) { const minConfirmLevel = yield this.axelarQueryApi.getConfirmationHeight(srcChain); return { success: false, commandId: evmEvent.commandId, eventResponse: evmEvent.eventResponse, infoLogs: [], errorMessage: `findEventAndConfirmIfNeeded(): ${txHash} is not confirmed on ${srcChain}. The minimum confirmation height is ${minConfirmLevel}`, }; } const confirmTx = yield this.confirmGatewayTx(txHash, srcChain, selfSigningOptions).catch(() => undefined); if (!confirmTx) { return { success: false, commandId: evmEvent.commandId, eventResponse: evmEvent.eventResponse, infoLogs: [], errorMessage: `findEventAndConfirmIfNeeded(): could not confirm transaction on Axelar`, }; } const updatedEvent = yield this.getEvmEvent(srcChain, destChain, txHash, txEventIndex, evmWalletDetails); if (this.isEVMEventCompleted(updatedEvent === null || updatedEvent === void 0 ? void 0 : updatedEvent.eventResponse)) { return { success: true, commandId: updatedEvent.commandId, eventResponse: updatedEvent.eventResponse, infoLogs: [ `confirmation: successfully confirmed ${txHash} on Axelar; confirmed event was finalized`, getEvmEventInfoLog, ], }; } else { return { success: false, eventResponse: evmEvent.eventResponse, commandId: updatedEvent.commandId, errorMessage: `findEventAndConfirmIfNeeded(): could not confirm and finalize event successfully: ${txHash};. Your transaction may not have enough confirmations yet.`, infoLogs: [ `confirmation: successfully confirmed ${txHash} on Axelar; confirmed event was unable to be finalized`, getEvmEventInfoLog, ], }; } } }); } findBatchAndSignIfNeeded(commandId, destChainId, options) { return __awaiter(this, void 0, void 0, function* () { let signTxLog = ""; try { const batchData = yield (0, utils_2.retry)(() => this.fetchBatchData(destChainId, commandId), 10, 3000); if (batchData) { signTxLog = `signing: batch data exists so do not need to sign. commandId: ${commandId}, batchId: ${batchData.batch_id}`; if (this.debugMode) console.debug(signTxLog); return { success: true, infoLogs: [signTxLog], }; } else { const signCommandTx = yield this.signCommands(destChainId, options); signTxLog = `signing: signed batch for commandId (${commandId}) in tx: ${signCommandTx.transactionHash}`; if (this.debugMode) console.debug(signTxLog); return { success: true, signCommandTx, infoLogs: [signTxLog], }; } } catch (e) { return { success: false, errorMessage: `findBatchAndSignIfNeeded(): issue retrieving and signing command data: ${commandId}`, infoLogs: [signTxLog], }; } }); } findBatchAndApproveGateway(commandId_1, destChainId_1, wallet_1) { return __awaiter(this, arguments, void 0, function* (commandId, destChainId, wallet, useSelfSigning = false) { if (this.debugMode) console.debug(`broadcasting: checking for command ID: ${commandId} to broadcast`); return (0, utils_2.retry)(() => __awaiter(this, void 0, void 0, function* () { const batchData = yield this.fetchBatchData(destChainId, commandId); if (!batchData) { return Promise.reject(`findBatchAndApproveGateway(): unable to retrieve batch data for ${commandId}`); } const commandData = batchData.command_ids.find((t) => t === commandId); if (!commandData) { return Promise.reject(`findBatchAndApproveGateway(): unable to retrieve command ID (${commandId}) in batch data`); } if (batchData.status === "BATCHED_COMMANDS_STATUS_SIGNING") { return Promise.reject(`findBatchAndApproveGateway(): batch ID ${batchData.batch_id} signing in process`); } else if (batchData.status === "BATCHED_COMMANDS_STATUS_SIGNED") { if (!useSelfSigning) { const approveTx = yield this.sendApproveTx(destChainId, batchData.execute_data, wallet); return { success: true, approveTx, infoLogs: [ `broadcasting: batch ID ${batchData.batch_id} broadcasted to ${destChainId}`, ], }; } let approveTx; try { approveTx = yield this.broadcastEvmTx(destChainId, batchData.execute_data, wallet); } catch (error) { console.error("[recovery self-sign failed]", { action_type: "send_evm_tx", use_self_signing: true, command_id: commandId, destination_chain: destChainId, batch_id: batchData.batch_id, error, }); return Promise.reject(new Error("findBatchAndApproveGateway(): self-signing failed")); } return { success: true, approveTx: this.normalizeTxResponse(approveTx), infoLogs: [ `broadcasting: batch ID ${batchData.batch_id} broadcasted to ${destChainId}`, ], }; } else { return Promise.reject(`findBatchAndApproveGateway(): batch ID ${batchData.batch_id} is in an unknown state for command data: ${commandId}`); } }), 3, 10).catch((error) => { return { success: false, errorMessage: error.message || // error can be both a string or an object with a message property error || `findBatchAndApproveGatewayIfNeeded(): issue retrieving and broadcasting command data: ${commandId}`, infoLogs: [], }; }); }); } manualRelayToDestChain(txHash_1, txLogIndex_1, txEventIndex_1, evmWalletDetailsOrOptions_1) { return __awaiter(this, arguments, void 0, function* (txHash, txLogIndex, txEventIndex, evmWalletDetailsOrOptions, escapeAfterConfirm = true, messageId, cosmosWalletDetails, useSelfSigning = false) { const normalizedArgs = this.normalizeManualRelayArgs(evmWalletDetailsOrOptions, escapeAfterConfirm, messageId, cosmosWalletDetails, useSelfSigning); const { selfSigningOptions, error: selfSigningError } = this.buildSelfSigningContext(normalizedArgs.cosmosWalletDetails, normalizedArgs.useSelfSigning); if (selfSigningError) { return { success: false, error: selfSigningError }; } const { callTx, status } = yield this.queryTransactionStatus(txHash, txLogIndex); /**first check if transaction is already executed */ if (exports.GMPErrorMap[status]) return { success: false, error: exports.GMPErrorMap[status], }; const srcChain = callTx.chain; const destChain = callTx.returnValues.destinationChain; const eventIndex = txEventIndex !== null && txEventIndex !== void 0 ? txEventIndex : callTx._logIndex; const srcChainInfo = yield this.getChainInfo(srcChain); const destChainInfo = yield this.getChainInfo(destChain); const routeDir = this.getRouteDir(srcChainInfo, destChainInfo); if (routeDir === RouteDir.COSMOS_TO_EVM) { return this.recoverCosmosToEvmTx(txHash, normalizedArgs.evmWalletDetails, normalizedArgs.messageId, selfSigningOptions); } else if (routeDir === RouteDir.EVM_TO_COSMOS) { return this.recoverEvmToCosmosTx(srcChain, txHash, eventIndex, normalizedArgs.evmWalletDetails, selfSigningOptions); } else if (routeDir === RouteDir.COSMOS_TO_COSMOS) { return this.recoverCosmosToCosmosTx(txHash, selfSigningOptions); } else { return this.recoverEvmToEvmTx(srcChain, destChain, txHash, eventIndex, normalizedArgs.evmWalletDetails, normalizedArgs.escapeAfterConfirm, selfSigningOptions); } }); } getRouteDir(srcChain, destChain) { if (srcChain.module === "axelarnet" && destChain.module === "evm") { return RouteDir.COSMOS_TO_EVM; } else if (srcChain.module === "evm" && destChain.module === "axelarnet") { return RouteDir.EVM_TO_COSMOS; } else if (srcChain.module === "axelarnet" && destChain.module === "axelarnet") { return RouteDir.COSMOS_TO_COSMOS; } else { return RouteDir.EVM_TO_EVM; } } recoverCosmosToCosmosTx(txHash, selfSigningOptions) { return __awaiter(this, void 0, void 0, function* () { const gmpTx = yield this.fetchGMPTransaction(txHash); // Fetch all necessary data to send the route message tx const payload = gmpTx.call.returnValues.payload; const messageId = gmpTx.call.id; // Send the route message tx const routeMessageTx = yield this.routeMessageRequest(messageId, payload, -1, selfSigningOptions).catch(() => undefined); // If the `success` flag is false, return the error response if (!routeMessageTx) { return { success: false, error: "Failed to send RouteMessage to Axelar", }; } // Return the success response return { success: true, routeMessageTx, infoLogs: [`Successfully sent RouteMessage tx for given tx hash ${txHash}`], }; }); } recoverEvmToCosmosTx(srcChain, txHash, txEventIndex, evmWalletDetails, selfSigningOptions) { return __awaiter(this, void 0, void 0, function* () { // Check if the tx is confirmed on the source chain const isConfirmed = yield this.doesTxMeetConfirmHt(srcChain, txHash, evmWalletDetails === null || evmWalletDetails === void 0 ? void 0 : evmWalletDetails.provider); if (!isConfirmed) { const minConfirmLevel = yield this.axelarQueryApi.getConfirmationHeight(srcChain); return { success: false, error: `${txHash} is not confirmed on ${srcChain}. The minimum confirmation height is ${minConfirmLevel}`, }; } // ConfirmGatewayTx and check if it is successfully executed const confirmTx = yield this.confirmGatewayTx(txHash, srcChain, selfSigningOptions).catch(() => undefined); if (!confirmTx) { return { success: false, error: "Failed to send ConfirmGatewayTx to Axelar", }; } // Fetch all necessary data to send the route message tx const payload = yield this.fetchGMPTransaction(txHash).then((data) => data.call.returnValues.payload); const eventIndex = txEventIndex !== null && txEventIndex !== void 0 ? txEventIndex : (yield this.getEventIndex(srcChain, txHash)); // Send the route message tx const routeMessageTx = yield this.routeMessageRequest(txHash, payload, eventIndex, selfSigningOptions).catch(() => undefined); // If the `success` flag is false, return the error response if (!routeMessageTx) { return { success: false, error: "Failed to send RouteMessage to Axelar", }; } // Return the success response return { success: true, confirmTx, routeMessageTx, infoLogs: [ `Successfully sent ConfirmGatewayTx tx for given tx hash ${txHash}`, `Successfully sent RouteMessage tx for given tx hash ${txHash}`, ], }; }); } recoverCosmosToEvmTx(txHash, evmWalletDetails, msgIdParam, selfSigningOptions) { return __awaiter(this, void 0, void 0, function* () { const txDetails = yield this.fetchGMPTransaction(txHash); const { messageId: msgIdFromAxelarscan, payload, destinationChain, } = txDetails.call.returnValues; const { command_id: commandId } = txDetails; const messageId = msgIdParam !== null && msgIdParam !== void 0 ? msgIdParam : msgIdFromAxelarscan; // Send RouteMessageTx const routeMessageTx = yield this.routeMessageRequest(messageId, payload, -1, selfSigningOptions).catch(() => undefined); if (!routeMessageTx) { return { success: false, error: "Failed to send RouteMessage to Axelar", }; } // Dispatch a SignCommand transaction and an Approve transaction to the Gateway contract. const response = yield this.signAndApproveGateway(commandId, destinationChain, evmWalletDetails, selfSigningOptions); // If the response.success is false, we will return the error response if (!response.success) { return { success: false, error: response.error, }; } // Otherwise, we will return the success response const { signCommandTx, infoLogs: signTxLogs } = response; return { success: true, routeMessageTx, signCommandTx, infoLogs: [`Successfully sent RouteMessage tx for given ${txHash}`, ...signTxLogs], }; }); } recoverEvmToEvmTx(srcChain_1, destChain_1, txHash_1, txEventIndex_1, evmWalletDetails_1) { return __awaiter(this, arguments, void 0, function* (srcChain, destChain, txHash, txEventIndex, evmWalletDetails, escapeAfterConfirm = true, selfSigningOptions) { try { // ConfirmGatewayTx and check if it is successfully executed const confirmTxRequest = yield this.findEventAndConfirmIfNeeded(srcChain, destChain, txHash, txEventIndex, evmWalletDetails, selfSigningOptions); // If the `success` flag is false, we will return the error response if (!(confirmTxRequest === null || confirmTxRequest === void 0 ? void 0 : confirmTxRequest.success)) { return { success: false, error: confirmTxRequest.errorMessage || types_1.ApproveGatewayError.ERROR_BATCHED_COMMAND, }; } const { infoLogs: confirmTxLogs, commandId, confirmTx } = confirmTxRequest; // If the `escapeAfterConfirm` flag is set to true, we will return the `confirmTx` and `infoLogs` immediately without signing the batch. if (confirmTx && escapeAfterConfirm) { return { success: true, confirmTx, infoLogs: confirmTxLogs, }; } // Find the batch and sign it const response = yield this.signAndApproveGateway(commandId, destChain, evmWalletDetails, selfSigningOptions); // If the response.success is false, we will return the error response if (!response.success) { return { success: false, error: response.error, }; } // Otherwise, we will return the success response const { signCommandTx, approveTx, infoLogs: signTxLogs } = response; return { success: true, confirmTx, signCommandTx, approveTx, infoLogs: [...confirmTxLogs, ...signTxLogs], }; // If more code is required here, you can add it below. } catch (e) { return { success: false, error: e.message || types_1.ApproveGatewayError.CONFIRM_COMMAND_FAILED, }; } }); } signAndApproveGateway(commandId, destChain, evmWalletDetails, options) { return __awaiter(this, void 0, void 0, function* () { try { const { shouldSelfSign } = this.resolveSelfSigningOptions(options); const signTxRequest = yield this.findBatchAndSignIfNeeded(commandId, destChain, options); if (!(signTxRequest === null || signTxRequest === void 0 ? void 0 : signTxRequest.success)) { return { success: false, error: signTxRequest.errorMessage || types_1.ApproveGatewayError.SIGN_COMMAND_FAILED, }; } const broadcastTxRequest = yield this.findBatchAndApproveGateway(commandId, destChain, evmWalletDetails, shouldSelfSign); if (!(broadcastTxRequest === null || broadcastTxRequest === void 0 ? void 0 : broadcastTxRequest.success)) { return { success: false, error: broadcastTxRequest.errorMessage || types_1.ApproveGatewayError.ERROR_BROADCAST_EVENT, }; } return { success: true, signCommandTx: signTxRequest.signCommandTx, approveTx: broadcastTxRequest.approveTx, infoLogs: [...(signTxRequest.infoLogs || []), ...(broadcastTxRequest.infoLogs || [])], }; } catch (e) { return { success: false, error: e.message || `Error signing and approving gateway for commandId: ${commandId}`, }; } }); } /** * Check if given transaction is already executed. * @param txHash string - transaction hash * @returns Promise<boolean> - true if transaction is already executed */ isExecuted(txHash) { return __awaiter(this, void 0, void 0, function* () { const txStatus = yield this.queryTransactionStatus(txHash).catch(() => undefined); return (txStatus === null || txStatus === void 0 ? void 0 : txStatus.status) === AxelarRecoveryApi_1.GMPStatus.DEST_EXECUTED; }); } /** * Check if given transaction is already confirmed. * @param txHash string - transaction hash * @returns Promise<boolean> - true if transaction is already confirmed */ isConfirmed(txHash) { return __awaiter(this, void 0, void 0, function* () { const txStatus = yield this.queryTransactionStatus(txHash).catch(() => undefined); return [AxelarRecoveryApi_1.GMPStatus.SRC_GATEWAY_CONFIRMED, AxelarRecoveryApi_1.GMPStatus.DEST_GATEWAY_APPROVED].includes(this.parseGMPStatus(txStatus === null || txStatus === void 0 ? void 0 : txStatus.status)); }); } /** * Calculate the gas fee in native token for executing a transaction at the destination chain using the source chain's gas price. * @param txHash string - transaction hash * @param sourceChain EVMChain - source chain * @param destinationChain EVMChain - destination chain * @param gasTokenSymbol string - gas token symbol * @param options QueryGasFeeOptions - options * @returns Promise<string> - The gas fee to be paid at source chain */ calculateNativeGasFee(txHash, sourceChain, destinationChain, estimatedGasUsed, options) { return __awaiter(this, void 0, void 0, function* () { yield (0, utils_2.throwIfInvalidChainIds)([sourceChain, destinationChain], this.environment); const hasTxBeenConfirmed = (yield this.isConfirmed(txHash)) || false; options.shouldSubtractBaseFee = hasTxBeenConfirmed; return this.subtractGasFee(sourceChain, destinationChain, estimatedGasUsed, options); }); } /** * Calculate the gas fee in an ERC-20 tokens for executing a transaction at the destination chain using the source chain's gas price. * @param txHash string - transaction hash * @param sourceChain EVMChain - source chain * @param destinationChain EVMChain - destination chain * @param gasTokenSymbol string - gas token symbol * @param options QueryGasFeeOptions - options * @returns Promise<string> - The gas fee to be paid at source chain */ calculateGasFee(sourceChain, destinationChain, estimatedGasUsed, options) { return __awaiter(this, void 0, void 0, function* () { yield (0, utils_2.throwIfInvalidChainIds)([sourceChain, destinationChain], this.environment); return this.subtractGasFee(sourceChain, destinationChain, estimatedGasUsed, options); }); } getEventIndex(chain, txHash, evmWalletDetails) { return __awaiter(this, void 0, void 0, function* () { const signer = this.getSigner(chain, evmWalletDetails || { useWindowEthereum: false }); const receipt = yield signer.provider.getTransactionReceipt(txHash).catch(() => undefined); if (!receipt) { const gmpTx = yield this.fetchGMPTransaction(txHash).catch(() => undefined); if (!gmpTx) return -1; return parseInt(gmpTx.call._logIndex); } else { const eventIndex = (0, contractEventHelper_1.getEventIndexFromTxReceipt)(receipt); return eventIndex; } }); } addGasToSuiChain(params) { return __awaiter(this, void 0, void 0, function* () { const { amount, messageId, gasParams, refundAddress } = params; const chains = yield (0, chains_1.importS3Config)(this.environment); const suiKey = Object.keys(chains.chains).find((chainName) => chainName.includes("sui")); if (!suiKey) throw new Error("Cannot find sui chain config"); const suiConfig = chains.chains[suiKey]; const gasServiceContract = suiConfig.config.contracts.GasService; const gasAmount = amount ? BigInt(amount) : (0, utils_3.parseUnits)("0.01", 9).toBigInt(); const tx = new transactions_1.Transaction(); const [gas] = tx.splitCoins(tx.gas, [tx.pure.u64(gasAmount)]); tx.moveCall({ target: `${gasServiceContract.address}::gas_service::add_gas`, typeArguments: [utils_1.SUI_TYPE_ARG], arguments: [ tx.object(gasServiceContract.objects.GasService), gas, tx.pure(bcs_1.bcs.string().serialize(messageId).toBytes()), tx.pure.address(refundAddress), tx.pure(bcs_1.bcs.vector(bcs_1.bcs.u8()).serialize((0, utils_3.arrayify)(gasParams)).toBytes()), ], }); return tx; }); } addGasToSolanaChain(params) { return __awaiter(this, void 0, void 0, function* () { const { messageId, gasFeeAmount, sender, refundAddress, programId, configPda } = params; // Validate the always-present addresses up front (0, helpers_1.validateSolanaAddress)(sender, "sender address"); const refundAddressPublicKey = (0, helpers_1.validateSolanaAddress)(refundAddress, "refund address"); // Resolve programId + treasury (configPda): // - If the caller supplied both, use them directly and skip the S3 lookup. // - If the caller supplied exactly one, fail loudly — they're tightly // coupled (the treasury is a PDA of the program) and a partial override // is almost certainly a misconfiguration. // - Otherwise, look up the program ID from the chain config and derive // the treasury PDA locally from the program's seed. if ((programId && !configPda) || (!programId && configPda)) { throw new Error("addGasToSolanaChain: programId and configPda must be supplied together — " + `got programId=${programId !== null && programId !== void 0 ? programId : "undefined"}, configPda=${configPda !== null && configPda !== void 0 ? configPda : "undefined"}`); } let gasServiceProgramIdPublicKey; let treasuryPda; if (programId && configPda) { gasServiceProgramIdPublicKey = (0, helpers_1.validateSolanaAddress)(programId, "program ID"); treasuryPda = (0, helpers_1.validateSolanaAddress)(configPda, "config PDA"); } else { const chains = yield (0, chains_1.importS3Config)(this.environment); const solanaKey = Object.keys(chains.chains).find((chainName) => chainName.includes("solana")); if (!solanaKey) throw new Error("Cannot find Solana chain config"); const resolvedProgramId = chains.chains[solanaKey].config.contracts.AxelarGasService.address; gasServiceProgramIdPublicKey = (0, helpers_1.validateSolanaAddress)(resolvedProgramId, "program ID"); treasuryPda = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("gas-service")], gasServiceProgramIdPublicKey)[0]; } // Event authority PDA is always derived from the program ID const gasServiceEventAuthority = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("__event_authority")], gasServiceProgramIdPublicKey)[0]; // Validate and parse messageId (format: "txHash-topLevelIndex.innerIndex") if (!messageId || typeof messageId !== "string") { throw new Error(`Invalid messageId: must be a non-empty string, got ${messageId}`); } const messageIdParts = messageId.split("-"); if (messageIdParts.length !== 2) { throw new Error(`Invalid messageId format: expected "txHash-topLevelLogIndex.innerLogIndex", got "${messageId}"`); } const [txHash, logIndexStr] = messageIdParts; const logIndexParts = logIndexStr.split("."); if (logIndexParts.length !== 2) { throw new Error(`Invalid messageId format: expected "txHash-topLevelLogIndex.innerLogIndex", got "${messageId}"`); } const [topLevelLogIndexStr, innerLogIndexStr] = logIndexParts; // The relayer parses these indices as u8, and Solana's 1232-byte tx size // cap rules out anything larger in practice — enforce u8 here for parity. // Digits-only first, since parseInt/Number would silently accept "3abc". if (!/^\d+$/.test(topLevelLogIndexStr) || Number(topLevelLogIndexStr) > 255) { throw new Error(`Invalid topLevelLogIndex in messageId: must be an integer in [0, 255], got "${topLevelLogIndexStr}"`); } if (!/^\d+$/.test(innerLogIndexStr) || Number(innerLogIndexStr) > 255) { throw new Error(`Invalid innerLogIndex in messageId: must be an integer in [0, 255], got "${innerLogIndexStr}"`); } // Validate gasFeeAmount can be converted to BigInt and is non-negative let gasFeeAmountBigInt; try { gasFeeAmountBigInt = BigInt(gasFeeAmount); } catch (error) { throw new Error(`Invalid gasFeeAmount: must be a valid numeric string, got "${gasFeeAmount}". ${error instanceof Error ? error.message : "Unknown error"}`); } if (gasFeeAmountBigInt <= BigInt(0)) { throw new Error(`Invalid gasFeeAmount: must be positive, got ${gasFeeAmount}`); } // Validate the txHash portion of the messageId is a 64-byte base58 Solana signature try { const decoded = bs58_1.default.decode(txHash); if (decoded.length !== 64) { throw new Error(`Solana transaction signature must be 64 bytes when decoded, got ${decoded.length} bytes`); } } catch (error) { if (error instanceof Error && error.message.includes("64 bytes")) { throw error; } throw new Error(`Failed to decode Solana transaction signature: ${txHash}. ${error instanceof Error ? error.message : "Unknown error"}`); } const instructionId = (0, helpers_1.anchorInstructionDiscriminator)("add_gas"); // Manual Borsh serialization const data = (0, helpers_1.concatU8)([ // instruction id instructionId, // message_id (0, helpers_1.encodeStringBorsh)(messageId), // amount (0, helpers_1.encodeU64LE)(gasFeeAmountBigInt), // refund_address refundAddressPublicKey.toBuffer(), ]); // Create the transaction instruction const instruction = new web3_js_1.TransactionInstruction({ keys: [ { pubkey: new web3_js_1.PublicKey(sender), // sender (signer, writable) - the account paying for gas isSigner: true, isWritable: true, }, { pubkey: new web3_js_1.PublicKey(treasuryPda), // treasury (writable) - receives the lamports isSigner: false, isWritable: true, }, { pubkey: new web3_js_1.PublicKey("11111111111111111111111111111111"), // system_program isSigner: false, isWritable: false, }, { pubkey: gasServiceEventAuthority, isSigner: false, isWritable: false, }, { pubkey: gasServiceProgramIdPublicKey, isSigner: false, isWritable: false, }, ], programId: gasServiceProgramIdPublicKey, data: data, }); // Create and return the transaction const transaction = new web3_js_1.Transaction().add(instruction); return transaction; }); } addGasToXrplChain(params) { return __awaiter(this, void 0, void 0, function* () { const { senderAddress, messageId, tokenSymbol, amount, rpcUrl } = params; const symbol = tokenSymbol || "XRP"; const chainInfo = yield this.findChainInfo("xrpl"); if (!chainInfo) throw new Error("XRPL chain config not found"); const { config: chainConfig } = chainInfo; const contractId = chainConfig.contracts.AxelarGateway.address; const rpc = rpcUrl || chainConfig.rpc[0]; const wssUrl = (0, xrplHelper_1.convertRpcUrltoWssUrl)(rpc); const client = new xrpl_1.Client(wssUrl); yield client.connect(); const args = { TransactionType: "Payment", Account: senderAddress, Destination: contractId, Amount: (0, xrplHelper_1.parseToken)(symbol === "XRP" ? "XRP" : `${symbol}.${contractId}`, amount), Memos: [ { Memo: { MemoType: (0, xrplHelper_1.hex)("type"), MemoData: (0, xrplHelper_1.hex)("add_gas") }, }, { Memo: { MemoType: (0, xrplHelper_1.hex)("msg_id"), MemoData: (0, xrplHelper_1.hex)(messageId.toLowerCase().replace("0x", "")), }, }, ], }; try { // Autofill transaction details (like sequence number) return yield client.autofill(args); } catch (e) { console.log(e); throw e; } finally { yield client.disconnect(); } }); } /** * Builds an XDR transaction to add gas payment to the Axelar Gas Service contract. * * This function creates a Stellar transaction that adds gas payment to the Axelar Gas Service. * The payment is made in native XLM token by default and is used to cover the execution costs of * cross-chain messages. * * @example * ```typescript * const xdr = await sdk.addGasToStellarChain{ * senderAddress: 'GCXXX...', // The address that sent the cross-chain message via the `axelar_gateway` * messageId: 'tx-123', * amount: '10000000', // the token amount to pay for the gas fee * spender: 'GXXX...' // The spender pays for the gas fee. * }); * * // Sign with Freighter wallet * const signedXDR = await window.freighter.signTransaction(xdr); * ``` * * @param {AddGasStellarParams} params - The parameters for the ad