hak-axelar-plugin 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +170 -0
- package/dist/index.cjs +614 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +43 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +605 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
import { BaseTool, handleTransaction } from '@hashgraph/hedera-agent-kit';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { ContractId, ContractExecuteTransaction, Hbar, ContractFunctionParameters } from '@hiero-ledger/sdk';
|
|
4
|
+
import 'long';
|
|
5
|
+
|
|
6
|
+
// src/tools/get-message-fee.ts
|
|
7
|
+
|
|
8
|
+
// src/networks.ts
|
|
9
|
+
var AXELAR_MAINNET = {
|
|
10
|
+
gatewayAddress: "0xe432150cce91c13a887f7D836923d5597adD8E31",
|
|
11
|
+
gasServiceAddress: "0x2d5d7d31F671F86C782533cc367F14109a082712",
|
|
12
|
+
itsAddress: "0xB5FB4BE02232B1bBA4dC8f81dc24C26980dE9e3C",
|
|
13
|
+
itsFactoryAddress: "0x83a93500d23Fbc3e82B410aD07A6a9F7A0670D66",
|
|
14
|
+
whbarAddress: "0xb1F616b8134F602c3Bb465fB5b5e6565cCAd37Ed",
|
|
15
|
+
chainName: "hedera",
|
|
16
|
+
chainId: 295,
|
|
17
|
+
rpcUrl: "https://mainnet.hashio.io/api",
|
|
18
|
+
apiBaseUrl: "https://api.axelarscan.io",
|
|
19
|
+
gmpApiBaseUrl: "https://api.gmp.axelarscan.io",
|
|
20
|
+
nestServerUrl: "https://nest-server-mainnet.axelar.dev"
|
|
21
|
+
};
|
|
22
|
+
var AXELAR_TESTNET = {
|
|
23
|
+
gatewayAddress: "0xe432150cce91c13a887f7D836923d5597adD8E31",
|
|
24
|
+
gasServiceAddress: "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6",
|
|
25
|
+
itsAddress: "0xB5FB4BE02232B1bBA4dC8f81dc24C26980dE9e3C",
|
|
26
|
+
itsFactoryAddress: "0x83a93500d23Fbc3e82B410aD07A6a9F7A0670D66",
|
|
27
|
+
whbarAddress: "0xb1F616b8134F602c3Bb465fB5b5e6565cCAd37Ed",
|
|
28
|
+
chainName: "hedera",
|
|
29
|
+
chainId: 296,
|
|
30
|
+
rpcUrl: "https://testnet.hashio.io/api",
|
|
31
|
+
apiBaseUrl: "https://testnet.api.axelarscan.io",
|
|
32
|
+
gmpApiBaseUrl: "https://testnet.api.gmp.axelarscan.io",
|
|
33
|
+
nestServerUrl: "https://nest-server-testnet.axelar.dev"
|
|
34
|
+
};
|
|
35
|
+
var NETWORK_DEFAULTS = {
|
|
36
|
+
mainnet: AXELAR_MAINNET,
|
|
37
|
+
testnet: AXELAR_TESTNET
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/utils/config.ts
|
|
41
|
+
function readContextConfig(context) {
|
|
42
|
+
const ctx = context;
|
|
43
|
+
for (const key of ["pluginConfig", "config", "agentConfig"]) {
|
|
44
|
+
const bucket = ctx[key];
|
|
45
|
+
if (bucket && typeof bucket === "object") {
|
|
46
|
+
const b = bucket;
|
|
47
|
+
if (b["hak-axelar-plugin"] && typeof b["hak-axelar-plugin"] === "object") {
|
|
48
|
+
return b["hak-axelar-plugin"];
|
|
49
|
+
}
|
|
50
|
+
if (b.axelar && typeof b.axelar === "object") {
|
|
51
|
+
return b.axelar;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
function readNetwork(value) {
|
|
58
|
+
if (value === "mainnet" || value === "testnet") return value;
|
|
59
|
+
return void 0;
|
|
60
|
+
}
|
|
61
|
+
function resolveNetworkDefaults(ctxConfig) {
|
|
62
|
+
const network = ctxConfig.network ?? readNetwork(process.env.AXELAR_NETWORK) ?? "mainnet";
|
|
63
|
+
const defaults = NETWORK_DEFAULTS[network];
|
|
64
|
+
return {
|
|
65
|
+
...defaults,
|
|
66
|
+
gatewayAddress: ctxConfig.gatewayAddress ?? process.env.AXELAR_GATEWAY_ADDRESS ?? defaults.gatewayAddress,
|
|
67
|
+
gasServiceAddress: ctxConfig.gasServiceAddress ?? process.env.AXELAR_GAS_SERVICE_ADDRESS ?? defaults.gasServiceAddress,
|
|
68
|
+
itsAddress: ctxConfig.itsAddress ?? process.env.AXELAR_ITS_ADDRESS ?? defaults.itsAddress,
|
|
69
|
+
apiBaseUrl: ctxConfig.apiBaseUrl ?? defaults.apiBaseUrl,
|
|
70
|
+
gmpApiBaseUrl: ctxConfig.gmpApiBaseUrl ?? defaults.gmpApiBaseUrl,
|
|
71
|
+
nestServerUrl: ctxConfig.nestServerUrl ?? defaults.nestServerUrl
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/tools/get-message-fee.ts
|
|
76
|
+
var GetMessageFeeSchema = z.object({
|
|
77
|
+
destinationChain: z.string().min(1).describe(
|
|
78
|
+
'Axelar chain name for the destination (e.g. "ethereum", "base", "arbitrum", "xrpl"). Use axelar_get_supported_chains to list valid names.'
|
|
79
|
+
),
|
|
80
|
+
gasLimit: z.number().int().positive().optional().default(2e5).describe("Gas limit for the destination contract execution (default: 200000)."),
|
|
81
|
+
network: z.enum(["mainnet", "testnet"]).optional().describe('Override network. Defaults to AXELAR_NETWORK env var or "mainnet".')
|
|
82
|
+
});
|
|
83
|
+
var GetMessageFeeTool = class extends BaseTool {
|
|
84
|
+
method = "axelar_get_message_fee";
|
|
85
|
+
name = "Axelar Get Message Fee";
|
|
86
|
+
description = "Estimates the HBAR fee required to send a cross-chain GMP message from Hedera via Axelar. Returns baseFee, executionFee, and total fee with multiplier, all denominated in tinybars. Call this before axelar_send_message to determine the gasTinybars parameter.";
|
|
87
|
+
parameters = GetMessageFeeSchema;
|
|
88
|
+
async normalizeParams(params, _context, _client) {
|
|
89
|
+
return GetMessageFeeSchema.parse(params);
|
|
90
|
+
}
|
|
91
|
+
async coreAction(args, context, _client) {
|
|
92
|
+
const ctxConfig = readContextConfig(context);
|
|
93
|
+
if (args.network) ctxConfig.network = args.network;
|
|
94
|
+
const net = resolveNetworkDefaults(ctxConfig);
|
|
95
|
+
try {
|
|
96
|
+
const fee = await fetchGasFee(
|
|
97
|
+
net.nestServerUrl,
|
|
98
|
+
net.chainName,
|
|
99
|
+
args.destinationChain,
|
|
100
|
+
args.gasLimit
|
|
101
|
+
);
|
|
102
|
+
return {
|
|
103
|
+
success: true,
|
|
104
|
+
network: ctxConfig.network ?? process.env.AXELAR_NETWORK ?? "mainnet",
|
|
105
|
+
sourceChain: net.chainName,
|
|
106
|
+
destinationChain: args.destinationChain,
|
|
107
|
+
gasLimit: args.gasLimit,
|
|
108
|
+
baseFee: fee.baseFee,
|
|
109
|
+
executionFee: fee.executionFee,
|
|
110
|
+
executionFeeWithMultiplier: fee.executionFeeWithMultiplier,
|
|
111
|
+
gasMultiplier: fee.gasMultiplier,
|
|
112
|
+
isExpressSupported: fee.isExpressSupported,
|
|
113
|
+
unit: "tinybars"
|
|
114
|
+
};
|
|
115
|
+
} catch (e) {
|
|
116
|
+
return { success: false, error: e.message };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async shouldSecondaryAction(coreResult, _context) {
|
|
120
|
+
return typeof coreResult === "object" && coreResult !== null && "transaction" in coreResult;
|
|
121
|
+
}
|
|
122
|
+
async secondaryAction() {
|
|
123
|
+
throw new Error("GetMessageFeeTool has no secondary action");
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
async function fetchGasFee(nestServerUrl, sourceChain, destinationChain, gasLimit) {
|
|
127
|
+
const res = await fetch(`${nestServerUrl}/getGasFee`, {
|
|
128
|
+
method: "POST",
|
|
129
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
130
|
+
body: JSON.stringify({
|
|
131
|
+
sourceChainName: sourceChain,
|
|
132
|
+
destinationChainName: destinationChain,
|
|
133
|
+
sourceTokenSymbol: "HBAR",
|
|
134
|
+
gasLimit: gasLimit.toString()
|
|
135
|
+
}),
|
|
136
|
+
signal: AbortSignal.timeout(15e3)
|
|
137
|
+
});
|
|
138
|
+
if (!res.ok) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Axelar fee API returned HTTP ${res.status}: ${await res.text().catch(() => "")}`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
const body = await res.json();
|
|
144
|
+
const result = body.result ?? body;
|
|
145
|
+
return {
|
|
146
|
+
baseFee: String(result.baseFee ?? result.base_fee ?? "0"),
|
|
147
|
+
executionFee: String(result.executionFee ?? result.execution_fee ?? "0"),
|
|
148
|
+
executionFeeWithMultiplier: String(
|
|
149
|
+
result.executionFeeWithMultiplier ?? result.executionFee ?? "0"
|
|
150
|
+
),
|
|
151
|
+
gasMultiplier: Number(result.gasMultiplier ?? result.gas_multiplier ?? 1),
|
|
152
|
+
isExpressSupported: Boolean(result.isExpressSupported ?? false)
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
var getMessageFeeTool = new GetMessageFeeTool();
|
|
156
|
+
var GetMessageStatusSchema = z.object({
|
|
157
|
+
txHash: z.string().min(1).describe(
|
|
158
|
+
"The Hedera transaction hash of the axelar_send_message or axelar_send_token call (the callContract transaction, not the gas payment transaction)."
|
|
159
|
+
),
|
|
160
|
+
network: z.enum(["mainnet", "testnet"]).optional().describe('Override network. Defaults to AXELAR_NETWORK env var or "mainnet".')
|
|
161
|
+
});
|
|
162
|
+
var GetMessageStatusTool = class extends BaseTool {
|
|
163
|
+
method = "axelar_get_message_status";
|
|
164
|
+
name = "Axelar Get Message Status";
|
|
165
|
+
description = "Checks the delivery status of a cross-chain message sent via Axelar from Hedera. Tracks all five stages: called \u2192 gas_paid \u2192 approved \u2192 executing \u2192 executed. Pass the Hedera transaction hash from axelar_send_message or axelar_send_token.";
|
|
166
|
+
parameters = GetMessageStatusSchema;
|
|
167
|
+
async normalizeParams(params, _context, _client) {
|
|
168
|
+
return GetMessageStatusSchema.parse(params);
|
|
169
|
+
}
|
|
170
|
+
async coreAction(args, context, _client) {
|
|
171
|
+
const ctxConfig = readContextConfig(context);
|
|
172
|
+
if (args.network) ctxConfig.network = args.network;
|
|
173
|
+
const net = resolveNetworkDefaults(ctxConfig);
|
|
174
|
+
try {
|
|
175
|
+
const url = `${net.gmpApiBaseUrl}/?method=searchGMP&sourceTransactionHash=${encodeURIComponent(args.txHash)}`;
|
|
176
|
+
const res = await fetch(url, {
|
|
177
|
+
headers: { Accept: "application/json" },
|
|
178
|
+
signal: AbortSignal.timeout(15e3)
|
|
179
|
+
});
|
|
180
|
+
if (!res.ok) {
|
|
181
|
+
return {
|
|
182
|
+
success: false,
|
|
183
|
+
error: `Axelar GMP API returned HTTP ${res.status}`
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const body = await res.json();
|
|
187
|
+
const items = Array.isArray(body.data) ? body.data : Array.isArray(body) ? body : [];
|
|
188
|
+
if (items.length === 0) {
|
|
189
|
+
return buildStatus(args.txHash, "not_found", null, net);
|
|
190
|
+
}
|
|
191
|
+
const item = items[0];
|
|
192
|
+
return buildStatus(args.txHash, resolveStatus(item), item, net);
|
|
193
|
+
} catch (e) {
|
|
194
|
+
return { success: false, error: e.message };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async shouldSecondaryAction(coreResult, _context) {
|
|
198
|
+
return typeof coreResult === "object" && coreResult !== null && "transaction" in coreResult;
|
|
199
|
+
}
|
|
200
|
+
async secondaryAction() {
|
|
201
|
+
throw new Error("GetMessageStatusTool has no secondary action");
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
function resolveStatus(item) {
|
|
205
|
+
const status = String(item.status ?? item.simplified_status ?? "").toLowerCase();
|
|
206
|
+
const map = {
|
|
207
|
+
called: "called",
|
|
208
|
+
gas_paid: "gas_paid",
|
|
209
|
+
gas_paid_enough: "gas_paid_enough",
|
|
210
|
+
confirming: "confirming",
|
|
211
|
+
confirmed: "confirmed",
|
|
212
|
+
approved: "approved",
|
|
213
|
+
executing: "executing",
|
|
214
|
+
executed: "executed",
|
|
215
|
+
error: "error",
|
|
216
|
+
insufficient_fee: "insufficient_fee"
|
|
217
|
+
};
|
|
218
|
+
return map[status] ?? "called";
|
|
219
|
+
}
|
|
220
|
+
function buildStatus(txHash, status, item, net) {
|
|
221
|
+
const call = item?.call ?? {};
|
|
222
|
+
const execute = item?.executed ?? {};
|
|
223
|
+
const isMainnet = net.gmpApiBaseUrl.includes("testnet") ? false : true;
|
|
224
|
+
const scanBase = isMainnet ? "https://axelarscan.io" : "https://testnet.axelarscan.io";
|
|
225
|
+
return {
|
|
226
|
+
success: true,
|
|
227
|
+
status,
|
|
228
|
+
txHash,
|
|
229
|
+
sourceChain: String(item?.sourceChain ?? net.chainName),
|
|
230
|
+
destinationChain: item?.destinationChain ? String(item.destinationChain) : void 0,
|
|
231
|
+
destinationContract: call.destinationContractAddress ? String(call.destinationContractAddress) : void 0,
|
|
232
|
+
executionTxHash: execute.transactionHash ? String(execute.transactionHash) : void 0,
|
|
233
|
+
gasPaid: Boolean(item?.gasPaid ?? item?.gas_paid),
|
|
234
|
+
isExecuted: status === "executed",
|
|
235
|
+
isError: status === "error" || status === "insufficient_fee",
|
|
236
|
+
axelarScanUrl: `${scanBase}/gmp/${txHash}`
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
var getMessageStatusTool = new GetMessageStatusTool();
|
|
240
|
+
var GetSupportedChainsSchema = z.object({
|
|
241
|
+
network: z.enum(["mainnet", "testnet"]).optional().describe('Override network selection. Defaults to AXELAR_NETWORK env var or "mainnet".')
|
|
242
|
+
});
|
|
243
|
+
var GetSupportedChainsTool = class extends BaseTool {
|
|
244
|
+
method = "axelar_get_supported_chains";
|
|
245
|
+
name = "Axelar Get Supported Chains";
|
|
246
|
+
description = "Returns all chains reachable from Hedera via Axelar Network. Includes chain IDs, names, and type (EVM / non-EVM). Use this before sending tokens or messages to confirm the destination chain is supported.";
|
|
247
|
+
parameters = GetSupportedChainsSchema;
|
|
248
|
+
async normalizeParams(params, _context, _client) {
|
|
249
|
+
return GetSupportedChainsSchema.parse(params);
|
|
250
|
+
}
|
|
251
|
+
async coreAction(args, context, _client) {
|
|
252
|
+
const ctxConfig = readContextConfig(context);
|
|
253
|
+
if (args.network) ctxConfig.network = args.network;
|
|
254
|
+
const net = resolveNetworkDefaults(ctxConfig);
|
|
255
|
+
try {
|
|
256
|
+
const url = `${net.apiBaseUrl}/api/getChains`;
|
|
257
|
+
const res = await fetch(url, {
|
|
258
|
+
headers: { Accept: "application/json" },
|
|
259
|
+
signal: AbortSignal.timeout(15e3)
|
|
260
|
+
});
|
|
261
|
+
if (!res.ok) {
|
|
262
|
+
return { success: false, error: `Axelar API returned HTTP ${res.status}` };
|
|
263
|
+
}
|
|
264
|
+
const data = await res.json();
|
|
265
|
+
const chains = parseChains(data);
|
|
266
|
+
return {
|
|
267
|
+
success: true,
|
|
268
|
+
network: ctxConfig.network ?? process.env.AXELAR_NETWORK ?? "mainnet",
|
|
269
|
+
sourceChain: net.chainName,
|
|
270
|
+
chains,
|
|
271
|
+
count: chains.length
|
|
272
|
+
};
|
|
273
|
+
} catch (e) {
|
|
274
|
+
return { success: false, error: e.message };
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
async shouldSecondaryAction(coreResult, _context) {
|
|
278
|
+
return typeof coreResult === "object" && coreResult !== null && "transaction" in coreResult;
|
|
279
|
+
}
|
|
280
|
+
async secondaryAction() {
|
|
281
|
+
throw new Error("GetSupportedChainsTool has no secondary action");
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
function parseChains(data) {
|
|
285
|
+
if (!data || typeof data !== "object") return [];
|
|
286
|
+
let raw = [];
|
|
287
|
+
if (Array.isArray(data)) {
|
|
288
|
+
raw = data;
|
|
289
|
+
} else {
|
|
290
|
+
const d = data;
|
|
291
|
+
if (Array.isArray(d.data)) raw = d.data;
|
|
292
|
+
else if (Array.isArray(d.result)) raw = d.result;
|
|
293
|
+
}
|
|
294
|
+
return raw.filter((c) => typeof c === "object" && c !== null).map((c) => ({
|
|
295
|
+
id: String(c.id ?? c.chain_id ?? c.axelarId ?? ""),
|
|
296
|
+
name: String(c.name ?? c.chain_name ?? c.id ?? ""),
|
|
297
|
+
chainId: typeof c.chain_id === "number" ? c.chain_id : void 0,
|
|
298
|
+
type: String(c.chain_type ?? (c.evm ? "evm" : "non-evm")),
|
|
299
|
+
status: typeof c.status === "string" ? c.status : void 0
|
|
300
|
+
})).filter((c) => c.id !== "hedera" && c.id !== "");
|
|
301
|
+
}
|
|
302
|
+
var getSupportedChainsTool = new GetSupportedChainsTool();
|
|
303
|
+
var safeUint256 = (value) => String(value);
|
|
304
|
+
var hexToBytes = (hex) => {
|
|
305
|
+
const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
306
|
+
if (clean.length > 0 && !/^[0-9a-fA-F]+$/.test(clean)) {
|
|
307
|
+
throw new Error(`Invalid hex string: "${hex}"`);
|
|
308
|
+
}
|
|
309
|
+
return Uint8Array.from(Buffer.from(clean, "hex"));
|
|
310
|
+
};
|
|
311
|
+
var encodeEvmAddress = (address) => {
|
|
312
|
+
const clean = address.replace(/^0x/i, "");
|
|
313
|
+
if (clean.length !== 40) {
|
|
314
|
+
throw new Error(`Invalid EVM address length: ${address}`);
|
|
315
|
+
}
|
|
316
|
+
return hexToBytes(clean);
|
|
317
|
+
};
|
|
318
|
+
var hexToBytes32 = (hex) => {
|
|
319
|
+
const clean = hex.replace(/^0x/i, "").padStart(64, "0");
|
|
320
|
+
return hexToBytes(clean);
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// src/tools/send-message.ts
|
|
324
|
+
var SendMessageSchema = z.object({
|
|
325
|
+
destinationChain: z.string().min(1).describe(
|
|
326
|
+
'Axelar chain name for the destination (e.g. "ethereum", "base", "arbitrum"). Use axelar_get_supported_chains to confirm the chain is reachable.'
|
|
327
|
+
),
|
|
328
|
+
destinationContract: z.string().min(1).describe(
|
|
329
|
+
"EVM address of the destination contract. It MUST implement AxelarExecutable (inherit from @axelar-network/axelar-gmp-sdk-solidity AxelarExecutable.sol)."
|
|
330
|
+
),
|
|
331
|
+
payload: z.string().optional().default("0x").describe(
|
|
332
|
+
'ABI-encoded payload bytes as a hex string (with or without 0x prefix). Use "0x" or omit for an empty payload (e.g. a ping).'
|
|
333
|
+
),
|
|
334
|
+
gasTinybars: z.string().min(1).describe(
|
|
335
|
+
"HBAR amount in tinybars (1 HBAR = 100,000,000 tinybars) to pay for Axelar relay execution. Use axelar_get_message_fee to estimate the required amount. Example: '5000000' = 0.05 HBAR."
|
|
336
|
+
),
|
|
337
|
+
network: z.enum(["mainnet", "testnet"]).optional().describe('Override network. Defaults to AXELAR_NETWORK env var or "mainnet".')
|
|
338
|
+
});
|
|
339
|
+
var SendMessageTool = class extends BaseTool {
|
|
340
|
+
method = "axelar_send_message";
|
|
341
|
+
name = "Axelar Send Cross-Chain Message";
|
|
342
|
+
description = "Sends a GMP (General Message Passing) message from Hedera to a contract on another chain via Axelar Network. The destination contract must implement AxelarExecutable. Submits two Hedera transactions: (1) gas payment to AxelarGasService, (2) callContract on AxelarGateway. Use axelar_get_message_fee first to estimate gasTinybars. Track delivery with axelar_get_message_status.";
|
|
343
|
+
parameters = SendMessageSchema;
|
|
344
|
+
async normalizeParams(params, _context, _client) {
|
|
345
|
+
return SendMessageSchema.parse(params);
|
|
346
|
+
}
|
|
347
|
+
async coreAction(args, context, client) {
|
|
348
|
+
const hederaClient = client;
|
|
349
|
+
const ctxConfig = readContextConfig(context);
|
|
350
|
+
if (args.network) ctxConfig.network = args.network;
|
|
351
|
+
const net = resolveNetworkDefaults(ctxConfig);
|
|
352
|
+
let senderEvmAddress;
|
|
353
|
+
try {
|
|
354
|
+
senderEvmAddress = hederaClient.operatorPublicKey?.toEvmAddress() ?? "";
|
|
355
|
+
} catch {
|
|
356
|
+
senderEvmAddress = "";
|
|
357
|
+
}
|
|
358
|
+
if (!senderEvmAddress) {
|
|
359
|
+
return {
|
|
360
|
+
success: false,
|
|
361
|
+
error: "Could not derive operator EVM address. Ensure the Hedera operator key is ECDSA-compatible (not Ed25519)."
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
let payloadBytes;
|
|
365
|
+
try {
|
|
366
|
+
payloadBytes = hexToBytes(args.payload ?? "0x");
|
|
367
|
+
} catch (e) {
|
|
368
|
+
return { success: false, error: `Invalid payload hex: ${e.message}` };
|
|
369
|
+
}
|
|
370
|
+
let gasTinybarsNum;
|
|
371
|
+
try {
|
|
372
|
+
gasTinybarsNum = Number(args.gasTinybars);
|
|
373
|
+
if (!Number.isFinite(gasTinybarsNum) || gasTinybarsNum <= 0) {
|
|
374
|
+
return { success: false, error: "gasTinybars must be a positive integer string" };
|
|
375
|
+
}
|
|
376
|
+
} catch {
|
|
377
|
+
return { success: false, error: "gasTinybars must be a positive integer string" };
|
|
378
|
+
}
|
|
379
|
+
try {
|
|
380
|
+
const gatewayId = ContractId.fromEvmAddress(0, 0, net.gatewayAddress);
|
|
381
|
+
const gasServiceId = ContractId.fromEvmAddress(0, 0, net.gasServiceAddress);
|
|
382
|
+
const gasPaymentTx = new ContractExecuteTransaction().setContractId(gasServiceId).setGas(4e5).setPayableAmount(Hbar.fromTinybars(gasTinybarsNum)).setFunction(
|
|
383
|
+
"payNativeGasForContractCall",
|
|
384
|
+
new ContractFunctionParameters().addAddress(senderEvmAddress).addString(args.destinationChain).addString(args.destinationContract).addBytes(payloadBytes).addAddress(senderEvmAddress)
|
|
385
|
+
// refundAddress
|
|
386
|
+
);
|
|
387
|
+
const callContractTx = new ContractExecuteTransaction().setContractId(gatewayId).setGas(4e5).setFunction(
|
|
388
|
+
"callContract",
|
|
389
|
+
new ContractFunctionParameters().addString(args.destinationChain).addString(args.destinationContract).addBytes(payloadBytes)
|
|
390
|
+
);
|
|
391
|
+
return {
|
|
392
|
+
transaction: gasPaymentTx,
|
|
393
|
+
extras: {
|
|
394
|
+
callContractTx,
|
|
395
|
+
destinationChain: args.destinationChain,
|
|
396
|
+
destinationContract: args.destinationContract,
|
|
397
|
+
payloadHex: args.payload ?? "0x",
|
|
398
|
+
gasTinybars: args.gasTinybars
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
} catch (e) {
|
|
402
|
+
return { success: false, error: `Failed to build transaction: ${e.message}` };
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
async shouldSecondaryAction(coreResult, _context) {
|
|
406
|
+
return typeof coreResult === "object" && coreResult !== null && "transaction" in coreResult;
|
|
407
|
+
}
|
|
408
|
+
async secondaryAction(payload, client, context) {
|
|
409
|
+
const hederaClient = client;
|
|
410
|
+
const ctxConfig = readContextConfig(context);
|
|
411
|
+
const net = resolveNetworkDefaults(ctxConfig);
|
|
412
|
+
const isMainnet = !net.gmpApiBaseUrl.includes("testnet");
|
|
413
|
+
const scanBase = isMainnet ? "https://axelarscan.io" : "https://testnet.axelarscan.io";
|
|
414
|
+
let gasTxId;
|
|
415
|
+
try {
|
|
416
|
+
const gasResult = await handleTransaction(payload.transaction, hederaClient, context);
|
|
417
|
+
gasTxId = gasResult.transactionId;
|
|
418
|
+
} catch (e) {
|
|
419
|
+
return {
|
|
420
|
+
success: false,
|
|
421
|
+
error: `Gas payment transaction failed: ${e.message}`,
|
|
422
|
+
destinationChain: payload.extras.destinationChain,
|
|
423
|
+
destinationContract: payload.extras.destinationContract,
|
|
424
|
+
payloadHex: payload.extras.payloadHex,
|
|
425
|
+
gasTinybars: payload.extras.gasTinybars
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
let callResult;
|
|
429
|
+
try {
|
|
430
|
+
callResult = await handleTransaction(
|
|
431
|
+
payload.extras.callContractTx,
|
|
432
|
+
hederaClient,
|
|
433
|
+
context
|
|
434
|
+
);
|
|
435
|
+
} catch (e) {
|
|
436
|
+
return {
|
|
437
|
+
success: false,
|
|
438
|
+
error: `callContract transaction failed: ${e.message}`,
|
|
439
|
+
gasTxId,
|
|
440
|
+
destinationChain: payload.extras.destinationChain,
|
|
441
|
+
destinationContract: payload.extras.destinationContract,
|
|
442
|
+
payloadHex: payload.extras.payloadHex,
|
|
443
|
+
gasTinybars: payload.extras.gasTinybars
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
const callTxId = callResult.transactionId;
|
|
447
|
+
const callTxHash = callTxId?.replace(/[@/]/g, "-") ?? "";
|
|
448
|
+
return {
|
|
449
|
+
success: true,
|
|
450
|
+
gasTxId,
|
|
451
|
+
callTxId,
|
|
452
|
+
sourceTxHash: callTxHash,
|
|
453
|
+
destinationChain: payload.extras.destinationChain,
|
|
454
|
+
destinationContract: payload.extras.destinationContract,
|
|
455
|
+
payloadHex: payload.extras.payloadHex,
|
|
456
|
+
gasTinybars: payload.extras.gasTinybars,
|
|
457
|
+
axelarScanUrl: callTxHash ? `${scanBase}/gmp/${callTxHash}` : void 0
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
var sendMessageTool = new SendMessageTool();
|
|
462
|
+
var SendTokenSchema = z.object({
|
|
463
|
+
interchainTokenId: z.string().min(1).describe(
|
|
464
|
+
"The Axelar ITS token ID (bytes32 hex string, with or without 0x prefix). This is the token registry ID, NOT the ERC-20 contract address. Find token IDs via the Axelar ITS registry or the token deployer."
|
|
465
|
+
),
|
|
466
|
+
destinationChain: z.string().min(1).describe(
|
|
467
|
+
'Axelar chain name for the destination (e.g. "ethereum", "base", "arbitrum"). The token must be registered on this chain in the ITS registry.'
|
|
468
|
+
),
|
|
469
|
+
destinationAddress: z.string().min(1).describe(
|
|
470
|
+
"EVM address of the token recipient on the destination chain (0x-prefixed hex, 20 bytes)."
|
|
471
|
+
),
|
|
472
|
+
amount: z.string().min(1).describe(
|
|
473
|
+
"Token amount to bridge in base units (smallest denomination). Example: to send 1 USDC (6 decimals), pass '1000000'."
|
|
474
|
+
),
|
|
475
|
+
gasTinybars: z.string().min(1).describe(
|
|
476
|
+
"HBAR amount in tinybars to pay for Axelar relay execution on the destination chain. This HBAR is forwarded to AxelarGasService via the ITS contract. Use axelar_get_message_fee to estimate. Example: '5000000' = 0.05 HBAR."
|
|
477
|
+
),
|
|
478
|
+
metadata: z.string().optional().default("0x").describe(
|
|
479
|
+
'ABI-encoded ITS metadata as hex string. Pass "0x" or omit for standard transfer.'
|
|
480
|
+
),
|
|
481
|
+
network: z.enum(["mainnet", "testnet"]).optional().describe('Override network. Defaults to AXELAR_NETWORK env var or "mainnet".')
|
|
482
|
+
});
|
|
483
|
+
var SendTokenTool = class extends BaseTool {
|
|
484
|
+
method = "axelar_send_token";
|
|
485
|
+
name = "Axelar Send Interchain Token";
|
|
486
|
+
description = "Bridges a registered ITS (Interchain Token Service) token from Hedera to another chain via Axelar Network. Calls InterchainTokenService.interchainTransfer() in a single transaction, paying gas in HBAR. IMPORTANT: The token must be registered in the Axelar ITS registry. Classic axlUSDC deposit-address bridging is NOT supported on Hedera (Amplifier architecture). The recipient must have associated the HTS token on Hedera before receiving it back.";
|
|
487
|
+
parameters = SendTokenSchema;
|
|
488
|
+
async normalizeParams(params, _context, _client) {
|
|
489
|
+
return SendTokenSchema.parse(params);
|
|
490
|
+
}
|
|
491
|
+
async coreAction(args, context, _client) {
|
|
492
|
+
const ctxConfig = readContextConfig(context);
|
|
493
|
+
if (args.network) ctxConfig.network = args.network;
|
|
494
|
+
const net = resolveNetworkDefaults(ctxConfig);
|
|
495
|
+
let tokenIdBytes;
|
|
496
|
+
try {
|
|
497
|
+
tokenIdBytes = hexToBytes32(args.interchainTokenId);
|
|
498
|
+
} catch (e) {
|
|
499
|
+
return { success: false, error: `Invalid interchainTokenId: ${e.message}` };
|
|
500
|
+
}
|
|
501
|
+
let destAddressBytes;
|
|
502
|
+
try {
|
|
503
|
+
destAddressBytes = encodeEvmAddress(args.destinationAddress);
|
|
504
|
+
} catch (e) {
|
|
505
|
+
return { success: false, error: `Invalid destinationAddress: ${e.message}` };
|
|
506
|
+
}
|
|
507
|
+
let metadataBytes;
|
|
508
|
+
try {
|
|
509
|
+
const rawMeta = args.metadata ?? "0x";
|
|
510
|
+
metadataBytes = rawMeta === "0x" || rawMeta === "" ? new Uint8Array(0) : hexToBytes32(rawMeta);
|
|
511
|
+
} catch (e) {
|
|
512
|
+
return { success: false, error: `Invalid metadata: ${e.message}` };
|
|
513
|
+
}
|
|
514
|
+
let gasTinybarsNum;
|
|
515
|
+
try {
|
|
516
|
+
gasTinybarsNum = Number(args.gasTinybars);
|
|
517
|
+
if (!Number.isFinite(gasTinybarsNum) || gasTinybarsNum <= 0) {
|
|
518
|
+
return { success: false, error: "gasTinybars must be a positive integer string" };
|
|
519
|
+
}
|
|
520
|
+
} catch {
|
|
521
|
+
return { success: false, error: "gasTinybars must be a positive integer string" };
|
|
522
|
+
}
|
|
523
|
+
const gasValueWei = (BigInt(args.gasTinybars) * BigInt(1e10)).toString();
|
|
524
|
+
try {
|
|
525
|
+
const itsId = ContractId.fromEvmAddress(0, 0, net.itsAddress);
|
|
526
|
+
const tx = new ContractExecuteTransaction().setContractId(itsId).setGas(8e5).setPayableAmount(Hbar.fromTinybars(gasTinybarsNum)).setFunction(
|
|
527
|
+
"interchainTransfer",
|
|
528
|
+
new ContractFunctionParameters().addBytes32(tokenIdBytes).addString(args.destinationChain).addBytes(destAddressBytes).addUint256(safeUint256(args.amount)).addBytes(metadataBytes).addUint256(safeUint256(gasValueWei))
|
|
529
|
+
);
|
|
530
|
+
return {
|
|
531
|
+
transaction: tx,
|
|
532
|
+
extras: {
|
|
533
|
+
interchainTokenId: args.interchainTokenId,
|
|
534
|
+
destinationChain: args.destinationChain,
|
|
535
|
+
destinationAddress: args.destinationAddress,
|
|
536
|
+
amount: args.amount,
|
|
537
|
+
gasTinybars: args.gasTinybars
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
} catch (e) {
|
|
541
|
+
return { success: false, error: `Failed to build transaction: ${e.message}` };
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
async shouldSecondaryAction(coreResult, _context) {
|
|
545
|
+
return typeof coreResult === "object" && coreResult !== null && "transaction" in coreResult;
|
|
546
|
+
}
|
|
547
|
+
async secondaryAction(payload, client, context) {
|
|
548
|
+
const hederaClient = client;
|
|
549
|
+
const ctxConfig = readContextConfig(context);
|
|
550
|
+
const net = resolveNetworkDefaults(ctxConfig);
|
|
551
|
+
const isMainnet = !net.gmpApiBaseUrl.includes("testnet");
|
|
552
|
+
const scanBase = isMainnet ? "https://axelarscan.io" : "https://testnet.axelarscan.io";
|
|
553
|
+
let result;
|
|
554
|
+
try {
|
|
555
|
+
result = await handleTransaction(
|
|
556
|
+
payload.transaction,
|
|
557
|
+
hederaClient,
|
|
558
|
+
context
|
|
559
|
+
);
|
|
560
|
+
} catch (e) {
|
|
561
|
+
return {
|
|
562
|
+
success: false,
|
|
563
|
+
error: e.message,
|
|
564
|
+
...payload.extras
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
const txId = result.transactionId;
|
|
568
|
+
const sourceTxHash = txId?.replace(/[@/]/g, "-") ?? "";
|
|
569
|
+
return {
|
|
570
|
+
success: true,
|
|
571
|
+
txId,
|
|
572
|
+
sourceTxHash,
|
|
573
|
+
...payload.extras,
|
|
574
|
+
axelarScanUrl: sourceTxHash ? `${scanBase}/gmp/${sourceTxHash}` : void 0
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
var sendTokenTool = new SendTokenTool();
|
|
579
|
+
|
|
580
|
+
// src/plugin.ts
|
|
581
|
+
var axelarPlugin = {
|
|
582
|
+
name: "hak-axelar-plugin",
|
|
583
|
+
version: "1.0.0",
|
|
584
|
+
description: "Axelar Network cross-chain plugin for Hedera Agent Kit \u2014 bridge tokens and send GMP messages between Hedera and 60+ chains",
|
|
585
|
+
tools: (_context) => [
|
|
586
|
+
getSupportedChainsTool,
|
|
587
|
+
getMessageFeeTool,
|
|
588
|
+
sendMessageTool,
|
|
589
|
+
sendTokenTool,
|
|
590
|
+
getMessageStatusTool
|
|
591
|
+
]
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
// src/index.ts
|
|
595
|
+
var axelarPluginToolNames = {
|
|
596
|
+
AXELAR_GET_SUPPORTED_CHAINS: "axelar_get_supported_chains",
|
|
597
|
+
AXELAR_GET_MESSAGE_FEE: "axelar_get_message_fee",
|
|
598
|
+
AXELAR_SEND_MESSAGE: "axelar_send_message",
|
|
599
|
+
AXELAR_SEND_TOKEN: "axelar_send_token",
|
|
600
|
+
AXELAR_GET_MESSAGE_STATUS: "axelar_get_message_status"
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
export { AXELAR_MAINNET, AXELAR_TESTNET, NETWORK_DEFAULTS, axelarPlugin, axelarPluginToolNames, axelarPlugin as default };
|
|
604
|
+
//# sourceMappingURL=index.js.map
|
|
605
|
+
//# sourceMappingURL=index.js.map
|