@axelar-network/axelarjs-sdk
Version:
The JavaScript SDK for Axelar Network
951 lines • 70.4 kB
JavaScript
"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