11import { Chain , Network } from "@wormhole-foundation/sdk-base" ;
2- import {
3- AxelarGMPRecoveryAPI ,
4- Environment ,
5- GMPStatusResponse ,
6- } from "@axelar-network/axelarjs-sdk" ;
72
8- // See: https://github.com/axelarnetwork/axelarjs-sdk/blob/main/src/constants/EvmChain.ts
3+ // Using direct API calls instead of importing the entire @axelar -network/axelarjs-sdk to stay lightweight
4+ // Axelar chains: https://github.com/axelarnetwork/axelarjs-sdk/blob/53a957deb1209325b1e3d109e0985a64db6d9901/src/constants/EvmChain.ts#L1
95export const axelarChains : Partial < Record < Chain , string > > = {
106 Ethereum : "ethereum" ,
117 Monad : "monad" ,
128 Sepolia : "ethereum-sepolia" ,
139 // add more as needed
1410} ;
1511
12+ // https://github.com/axelarnetwork/axelarjs-sdk/blob/53a957deb1209325b1e3d109e0985a64db6d9901/src/libs/TransactionRecoveryApi/AxelarRecoveryApi.ts#L16
13+ export enum GMPStatus {
14+ SRC_GATEWAY_CALLED = "source_gateway_called" ,
15+ DEST_GATEWAY_APPROVED = "destination_gateway_approved" ,
16+ DEST_EXECUTED = "destination_executed" ,
17+ EXPRESS_EXECUTED = "express_executed" ,
18+ DEST_EXECUTE_ERROR = "error" ,
19+ DEST_EXECUTING = "executing" ,
20+ APPROVING = "approving" ,
21+ FORECALLED = "forecalled" ,
22+ FORECALLED_WITHOUT_GAS_PAID = "forecalled_without_gas_paid" ,
23+ NOT_EXECUTED = "not_executed" ,
24+ NOT_EXECUTED_WITHOUT_GAS_PAID = "not_executed_without_gas_paid" ,
25+ INSUFFICIENT_FEE = "insufficient_fee" ,
26+ UNKNOWN_ERROR = "unknown_error" ,
27+ CANNOT_FETCH_STATUS = "cannot_fetch_status" ,
28+ SRC_GATEWAY_CONFIRMED = "confirmed" ,
29+ }
30+
31+ export interface GMPError {
32+ txHash : string ;
33+ chain : string ;
34+ message : string ;
35+ }
36+
1637export async function getAxelarGasFee (
1738 network : Network ,
1839 sourceChain : Chain ,
@@ -27,12 +48,14 @@ export async function getAxelarGasFee(
2748
2849 const axelarSourceChain = axelarChains [ sourceChain ] ;
2950 if ( ! axelarSourceChain ) {
30- throw new Error ( `Unsupported source chain: ${ sourceChain } ` ) ;
51+ throw new Error ( `Unsupported axelar source chain: ${ sourceChain } ` ) ;
3152 }
3253
3354 const axelarDestinationChain = axelarChains [ destinationChain ] ;
3455 if ( ! axelarDestinationChain ) {
35- throw new Error ( `Unsupported destination chain: ${ destinationChain } ` ) ;
56+ throw new Error (
57+ `Unsupported axelar destination chain: ${ destinationChain } `
58+ ) ;
3659 }
3760
3861 const controller = new AbortController ( ) ;
@@ -47,7 +70,6 @@ export async function getAxelarGasFee(
4770 body : JSON . stringify ( {
4871 sourceChain : axelarSourceChain ,
4972 destinationChain : axelarDestinationChain ,
50- sourceTokenAddress : "0x0000000000000000000000000000000000000000" ,
5173 gasMultiplier : "auto" ,
5274 gasLimit : gasLimit . toString ( ) ,
5375 } ) ,
@@ -71,14 +93,85 @@ export async function getAxelarGasFee(
7193
7294export async function getAxelarTransactionStatus (
7395 network : Network ,
74- txHash : string
75- ) : Promise < GMPStatusResponse > {
76- const api = new AxelarGMPRecoveryAPI ( {
77- environment :
78- network === "Mainnet" ? Environment . MAINNET : Environment . TESTNET ,
79- } ) ;
80- const status = await api . queryTransactionStatus ( txHash ) ;
81- return status ;
96+ sourceChain : Chain ,
97+ txHash : string ,
98+ timeoutMs = 10000
99+ ) : Promise < { status : GMPStatus | string ; error ?: GMPError } > {
100+ const baseUrl =
101+ network === "Mainnet"
102+ ? "https://api.axelarscan.io"
103+ : "https://testnet.api.axelarscan.io" ;
104+
105+ const axelarSourceChain = axelarChains [ sourceChain ] ;
106+ if ( ! axelarSourceChain ) {
107+ throw new Error ( `Unsupported axelar source chain: ${ sourceChain } ` ) ;
108+ }
109+
110+ const controller = new AbortController ( ) ;
111+ const timeoutId = setTimeout ( ( ) => controller . abort ( ) , timeoutMs ) ;
112+
113+ try {
114+ const response = await fetch ( `${ baseUrl } /gmp/searchGMP` , {
115+ method : "POST" ,
116+ headers : {
117+ "Content-Type" : "application/json" ,
118+ } ,
119+ body : JSON . stringify ( {
120+ sourceChain : axelarSourceChain ,
121+ txHash : txHash ,
122+ } ) ,
123+ signal : controller . signal ,
124+ } ) ;
125+
126+ if ( ! response . ok ) {
127+ const errorText = await response . text ( ) ;
128+ throw new Error (
129+ `Failed to get transaction status: ${ response . status } ${ errorText } `
130+ ) ;
131+ }
132+
133+ const result = await response . json ( ) ;
134+ if ( ! result . data || result . data . length === 0 ) {
135+ throw new Error ( "No transaction details found" ) ;
136+ }
137+
138+ const txDetails = result . data [ 0 ] ;
139+ return {
140+ status : parseGMPStatus ( txDetails ) ,
141+ error : parseGMPError ( txDetails ) ,
142+ } ;
143+ } finally {
144+ clearTimeout ( timeoutId ) ;
145+ }
146+ }
147+
148+ export function parseGMPStatus ( response : any ) : GMPStatus | string {
149+ const { error, status } = response ;
150+
151+ if ( status === "error" && error ) return GMPStatus . DEST_EXECUTE_ERROR ;
152+ else if ( status === "executed" ) return GMPStatus . DEST_EXECUTED ;
153+ else if ( status === "approved" ) return GMPStatus . DEST_GATEWAY_APPROVED ;
154+ else if ( status === "called" ) return GMPStatus . SRC_GATEWAY_CALLED ;
155+ else if ( status === "executing" ) return GMPStatus . DEST_EXECUTING ;
156+ else {
157+ return status ;
158+ }
159+ }
160+
161+ export function parseGMPError ( response : any ) : GMPError | undefined {
162+ if ( response . error ) {
163+ return {
164+ message : response . error . error . message ,
165+ txHash : response . error . sourceTransactionHash ,
166+ chain : response . error . chain ,
167+ } ;
168+ } else if ( response . is_insufficient_fee ) {
169+ return {
170+ message : "Insufficient gas" ,
171+ txHash : response . call . transaction . hash ,
172+ chain : response . call . chain ,
173+ } ;
174+ }
82175}
83176
84177export function getAxelarExplorerUrl ( network : Network , txHash : string ) : string {
0 commit comments