@t402/mcp 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.
@@ -0,0 +1,1692 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ BRIDGEABLE_CHAINS: () => BRIDGEABLE_CHAINS,
34
+ CHAIN_IDS: () => CHAIN_IDS,
35
+ DEFAULT_RPC_URLS: () => DEFAULT_RPC_URLS,
36
+ EXPLORER_URLS: () => EXPLORER_URLS,
37
+ GASLESS_SUPPORTED_NETWORKS: () => GASLESS_SUPPORTED_NETWORKS,
38
+ NATIVE_SYMBOLS: () => NATIVE_SYMBOLS,
39
+ T402McpServer: () => T402McpServer,
40
+ TOOL_DEFINITIONS: () => TOOL_DEFINITIONS,
41
+ USDC_ADDRESSES: () => USDC_ADDRESSES,
42
+ USDT0_ADDRESSES: () => USDT0_ADDRESSES,
43
+ USDT_ADDRESSES: () => USDT_ADDRESSES,
44
+ bridgeInputSchema: () => bridgeInputSchema,
45
+ createT402McpServer: () => createT402McpServer,
46
+ executeBridge: () => executeBridge,
47
+ executeGetAllBalances: () => executeGetAllBalances,
48
+ executeGetBalance: () => executeGetBalance,
49
+ executeGetBridgeFee: () => executeGetBridgeFee,
50
+ executePay: () => executePay,
51
+ executePayGasless: () => executePayGasless,
52
+ formatAllBalancesResult: () => formatAllBalancesResult,
53
+ formatBalanceResult: () => formatBalanceResult,
54
+ formatBridgeFeeResult: () => formatBridgeFeeResult,
55
+ formatBridgeResult: () => formatBridgeResult,
56
+ formatGaslessPaymentResult: () => formatGaslessPaymentResult,
57
+ formatPaymentResult: () => formatPaymentResult,
58
+ formatTokenAmount: () => formatTokenAmount,
59
+ getAllBalancesInputSchema: () => getAllBalancesInputSchema,
60
+ getBalanceInputSchema: () => getBalanceInputSchema,
61
+ getBridgeFeeInputSchema: () => getBridgeFeeInputSchema,
62
+ getExplorerTxUrl: () => getExplorerTxUrl,
63
+ getLayerZeroScanUrl: () => getLayerZeroScanUrl,
64
+ getTokenAddress: () => getTokenAddress,
65
+ loadConfigFromEnv: () => loadConfigFromEnv,
66
+ parseTokenAmount: () => parseTokenAmount,
67
+ payGaslessInputSchema: () => payGaslessInputSchema,
68
+ payInputSchema: () => payInputSchema,
69
+ supportsToken: () => supportsToken
70
+ });
71
+ module.exports = __toCommonJS(src_exports);
72
+
73
+ // src/server/t402Server.ts
74
+ var import_server = require("@modelcontextprotocol/sdk/server/index.js");
75
+ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
76
+ var import_types = require("@modelcontextprotocol/sdk/types.js");
77
+
78
+ // src/tools/getBalance.ts
79
+ var import_zod = require("zod");
80
+ var import_viem = require("viem");
81
+ var chains = __toESM(require("viem/chains"));
82
+
83
+ // src/constants.ts
84
+ var CHAIN_IDS = {
85
+ ethereum: 1,
86
+ base: 8453,
87
+ arbitrum: 42161,
88
+ optimism: 10,
89
+ polygon: 137,
90
+ avalanche: 43114,
91
+ ink: 57073,
92
+ berachain: 80094,
93
+ unichain: 130
94
+ };
95
+ var NATIVE_SYMBOLS = {
96
+ ethereum: "ETH",
97
+ base: "ETH",
98
+ arbitrum: "ETH",
99
+ optimism: "ETH",
100
+ polygon: "MATIC",
101
+ avalanche: "AVAX",
102
+ ink: "ETH",
103
+ berachain: "BERA",
104
+ unichain: "ETH"
105
+ };
106
+ var EXPLORER_URLS = {
107
+ ethereum: "https://etherscan.io",
108
+ base: "https://basescan.org",
109
+ arbitrum: "https://arbiscan.io",
110
+ optimism: "https://optimistic.etherscan.io",
111
+ polygon: "https://polygonscan.com",
112
+ avalanche: "https://snowtrace.io",
113
+ ink: "https://explorer.inkonchain.com",
114
+ berachain: "https://berascan.com",
115
+ unichain: "https://unichain.blockscout.com"
116
+ };
117
+ var DEFAULT_RPC_URLS = {
118
+ ethereum: "https://eth.llamarpc.com",
119
+ base: "https://mainnet.base.org",
120
+ arbitrum: "https://arb1.arbitrum.io/rpc",
121
+ optimism: "https://mainnet.optimism.io",
122
+ polygon: "https://polygon-rpc.com",
123
+ avalanche: "https://api.avax.network/ext/bc/C/rpc",
124
+ ink: "https://rpc-gel.inkonchain.com",
125
+ berachain: "https://rpc.berachain.com",
126
+ unichain: "https://mainnet.unichain.org"
127
+ };
128
+ var USDC_ADDRESSES = {
129
+ ethereum: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
130
+ base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
131
+ arbitrum: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
132
+ optimism: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
133
+ polygon: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
134
+ avalanche: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"
135
+ };
136
+ var USDT_ADDRESSES = {
137
+ ethereum: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
138
+ arbitrum: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
139
+ polygon: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
140
+ avalanche: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
141
+ optimism: "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58"
142
+ };
143
+ var USDT0_ADDRESSES = {
144
+ ethereum: "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee",
145
+ arbitrum: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
146
+ ink: "0x0200C29006150606B650577BBE7B6248F58470c1",
147
+ berachain: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736",
148
+ unichain: "0x588ce4F028D8e7B53B687865d6A67b3A54C75518"
149
+ };
150
+ var BRIDGEABLE_CHAINS = [
151
+ "ethereum",
152
+ "arbitrum",
153
+ "ink",
154
+ "berachain",
155
+ "unichain"
156
+ ];
157
+ var ERC20_ABI = [
158
+ {
159
+ name: "balanceOf",
160
+ type: "function",
161
+ stateMutability: "view",
162
+ inputs: [{ name: "account", type: "address" }],
163
+ outputs: [{ name: "", type: "uint256" }]
164
+ },
165
+ {
166
+ name: "decimals",
167
+ type: "function",
168
+ stateMutability: "view",
169
+ inputs: [],
170
+ outputs: [{ name: "", type: "uint8" }]
171
+ },
172
+ {
173
+ name: "symbol",
174
+ type: "function",
175
+ stateMutability: "view",
176
+ inputs: [],
177
+ outputs: [{ name: "", type: "string" }]
178
+ },
179
+ {
180
+ name: "transfer",
181
+ type: "function",
182
+ stateMutability: "nonpayable",
183
+ inputs: [
184
+ { name: "to", type: "address" },
185
+ { name: "amount", type: "uint256" }
186
+ ],
187
+ outputs: [{ name: "", type: "bool" }]
188
+ },
189
+ {
190
+ name: "approve",
191
+ type: "function",
192
+ stateMutability: "nonpayable",
193
+ inputs: [
194
+ { name: "spender", type: "address" },
195
+ { name: "amount", type: "uint256" }
196
+ ],
197
+ outputs: [{ name: "", type: "bool" }]
198
+ },
199
+ {
200
+ name: "allowance",
201
+ type: "function",
202
+ stateMutability: "view",
203
+ inputs: [
204
+ { name: "owner", type: "address" },
205
+ { name: "spender", type: "address" }
206
+ ],
207
+ outputs: [{ name: "", type: "uint256" }]
208
+ }
209
+ ];
210
+ function getExplorerTxUrl(network, txHash) {
211
+ return `${EXPLORER_URLS[network]}/tx/${txHash}`;
212
+ }
213
+ function getLayerZeroScanUrl(messageGuid) {
214
+ return `https://layerzeroscan.com/tx/${messageGuid}`;
215
+ }
216
+ function supportsToken(network, token) {
217
+ switch (token) {
218
+ case "USDC":
219
+ return network in USDC_ADDRESSES;
220
+ case "USDT":
221
+ return network in USDT_ADDRESSES;
222
+ case "USDT0":
223
+ return network in USDT0_ADDRESSES;
224
+ default:
225
+ return false;
226
+ }
227
+ }
228
+ function getTokenAddress(network, token) {
229
+ switch (token) {
230
+ case "USDC":
231
+ return USDC_ADDRESSES[network];
232
+ case "USDT":
233
+ return USDT_ADDRESSES[network];
234
+ case "USDT0":
235
+ return USDT0_ADDRESSES[network];
236
+ default:
237
+ return void 0;
238
+ }
239
+ }
240
+ function formatTokenAmount(amount, decimals, symbol) {
241
+ const divisor = BigInt(10 ** decimals);
242
+ const wholePart = amount / divisor;
243
+ const fractionalPart = amount % divisor;
244
+ const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
245
+ const trimmedFractional = fractionalStr.replace(/0+$/, "") || "0";
246
+ if (trimmedFractional === "0") {
247
+ return `${wholePart} ${symbol}`;
248
+ }
249
+ return `${wholePart}.${trimmedFractional} ${symbol}`;
250
+ }
251
+ function parseTokenAmount(amount, decimals) {
252
+ const [wholePart, fractionalPart = ""] = amount.split(".");
253
+ const paddedFractional = fractionalPart.padEnd(decimals, "0").slice(0, decimals);
254
+ return BigInt(wholePart + paddedFractional);
255
+ }
256
+
257
+ // src/tools/getBalance.ts
258
+ var getBalanceInputSchema = import_zod.z.object({
259
+ network: import_zod.z.enum([
260
+ "ethereum",
261
+ "base",
262
+ "arbitrum",
263
+ "optimism",
264
+ "polygon",
265
+ "avalanche",
266
+ "ink",
267
+ "berachain",
268
+ "unichain"
269
+ ]).describe("Blockchain network to check balance on"),
270
+ address: import_zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/).describe("Wallet address to check balance for")
271
+ });
272
+ function getViemChain(network) {
273
+ switch (network) {
274
+ case "ethereum":
275
+ return chains.mainnet;
276
+ case "base":
277
+ return chains.base;
278
+ case "arbitrum":
279
+ return chains.arbitrum;
280
+ case "optimism":
281
+ return chains.optimism;
282
+ case "polygon":
283
+ return chains.polygon;
284
+ case "avalanche":
285
+ return chains.avalanche;
286
+ case "ink":
287
+ return chains.ink;
288
+ case "berachain":
289
+ return chains.berachain;
290
+ case "unichain":
291
+ return chains.unichain;
292
+ default:
293
+ return chains.mainnet;
294
+ }
295
+ }
296
+ async function getTokenBalance(client, tokenAddress, walletAddress) {
297
+ try {
298
+ const [balance, decimals, symbol] = await Promise.all([
299
+ client.readContract({
300
+ address: tokenAddress,
301
+ abi: ERC20_ABI,
302
+ functionName: "balanceOf",
303
+ args: [walletAddress]
304
+ }),
305
+ client.readContract({
306
+ address: tokenAddress,
307
+ abi: ERC20_ABI,
308
+ functionName: "decimals"
309
+ }),
310
+ client.readContract({
311
+ address: tokenAddress,
312
+ abi: ERC20_ABI,
313
+ functionName: "symbol"
314
+ })
315
+ ]);
316
+ return {
317
+ symbol,
318
+ address: tokenAddress,
319
+ balance: balance.toString(),
320
+ formatted: (0, import_viem.formatUnits)(balance, decimals),
321
+ decimals
322
+ };
323
+ } catch {
324
+ return null;
325
+ }
326
+ }
327
+ async function executeGetBalance(input, rpcUrls) {
328
+ const { network, address } = input;
329
+ const walletAddress = address;
330
+ const rpcUrl = rpcUrls?.[network] || DEFAULT_RPC_URLS[network];
331
+ const chain = getViemChain(network);
332
+ const client = (0, import_viem.createPublicClient)({
333
+ chain,
334
+ transport: (0, import_viem.http)(rpcUrl)
335
+ });
336
+ const nativeBalance = await client.getBalance({ address: walletAddress });
337
+ const tokenAddresses = [];
338
+ if (USDC_ADDRESSES[network]) {
339
+ tokenAddresses.push({ token: USDC_ADDRESSES[network], expected: "USDC" });
340
+ }
341
+ if (USDT_ADDRESSES[network]) {
342
+ tokenAddresses.push({ token: USDT_ADDRESSES[network], expected: "USDT" });
343
+ }
344
+ if (USDT0_ADDRESSES[network]) {
345
+ tokenAddresses.push({ token: USDT0_ADDRESSES[network], expected: "USDT0" });
346
+ }
347
+ const tokenBalances = await Promise.all(
348
+ tokenAddresses.map(
349
+ ({ token }) => getTokenBalance(client, token, walletAddress)
350
+ )
351
+ );
352
+ const tokens = tokenBalances.filter(
353
+ (t) => t !== null
354
+ );
355
+ return {
356
+ network,
357
+ chainId: CHAIN_IDS[network],
358
+ native: {
359
+ symbol: NATIVE_SYMBOLS[network],
360
+ balance: nativeBalance.toString(),
361
+ formatted: (0, import_viem.formatEther)(nativeBalance)
362
+ },
363
+ tokens
364
+ };
365
+ }
366
+ function formatBalanceResult(balance) {
367
+ const lines = [
368
+ `## Balance on ${balance.network} (Chain ID: ${balance.chainId})`,
369
+ "",
370
+ `### Native Token`,
371
+ `- ${balance.native.symbol}: ${balance.native.formatted}`,
372
+ ""
373
+ ];
374
+ if (balance.tokens.length > 0) {
375
+ lines.push("### Stablecoins");
376
+ for (const token of balance.tokens) {
377
+ lines.push(`- ${token.symbol}: ${token.formatted}`);
378
+ }
379
+ } else {
380
+ lines.push("_No stablecoin balances found_");
381
+ }
382
+ return lines.join("\n");
383
+ }
384
+
385
+ // src/tools/getAllBalances.ts
386
+ var import_zod2 = require("zod");
387
+ var getAllBalancesInputSchema = import_zod2.z.object({
388
+ address: import_zod2.z.string().regex(/^0x[a-fA-F0-9]{40}$/).describe("Wallet address to check balances for"),
389
+ networks: import_zod2.z.array(
390
+ import_zod2.z.enum([
391
+ "ethereum",
392
+ "base",
393
+ "arbitrum",
394
+ "optimism",
395
+ "polygon",
396
+ "avalanche",
397
+ "ink",
398
+ "berachain",
399
+ "unichain"
400
+ ])
401
+ ).optional().describe(
402
+ "Optional list of networks to check. If not provided, checks all supported networks."
403
+ )
404
+ });
405
+ var ALL_NETWORKS = [
406
+ "ethereum",
407
+ "base",
408
+ "arbitrum",
409
+ "optimism",
410
+ "polygon",
411
+ "avalanche",
412
+ "ink",
413
+ "berachain",
414
+ "unichain"
415
+ ];
416
+ async function executeGetAllBalances(input, rpcUrls) {
417
+ const { address, networks = ALL_NETWORKS } = input;
418
+ const balancePromises = networks.map(
419
+ (network) => executeGetBalance({ network, address }, rpcUrls).catch((error) => {
420
+ console.error(`Failed to fetch balance for ${network}:`, error);
421
+ return null;
422
+ })
423
+ );
424
+ const results = await Promise.all(balancePromises);
425
+ const balances = results.filter((b) => b !== null);
426
+ let totalUsdc = 0n;
427
+ let totalUsdt = 0n;
428
+ for (const balance of balances) {
429
+ for (const token of balance.tokens) {
430
+ if (token.symbol === "USDC") {
431
+ totalUsdc += BigInt(token.balance);
432
+ } else if (token.symbol === "USDT" || token.symbol === "USDT0") {
433
+ totalUsdt += BigInt(token.balance);
434
+ }
435
+ }
436
+ }
437
+ const formatTotal = (amount) => {
438
+ const divisor = BigInt(10 ** 6);
439
+ const whole = amount / divisor;
440
+ const fraction = amount % divisor;
441
+ const fractionStr = fraction.toString().padStart(6, "0").replace(/0+$/, "");
442
+ return fractionStr ? `${whole}.${fractionStr}` : whole.toString();
443
+ };
444
+ const totalUsdcFormatted = formatTotal(totalUsdc);
445
+ const totalUsdtFormatted = formatTotal(totalUsdt);
446
+ const chainsWithBalance = balances.filter(
447
+ (b) => b.tokens.some((t) => BigInt(t.balance) > 0n) || BigInt(b.native.balance) > 0n
448
+ );
449
+ const summary = [
450
+ `Found balances on ${chainsWithBalance.length} of ${balances.length} networks checked.`,
451
+ `Total USDC: ${totalUsdcFormatted}`,
452
+ `Total USDT: ${totalUsdtFormatted}`
453
+ ].join(" ");
454
+ return {
455
+ address,
456
+ balances,
457
+ totalUsdcBalance: totalUsdcFormatted,
458
+ totalUsdtBalance: totalUsdtFormatted,
459
+ summary
460
+ };
461
+ }
462
+ function formatAllBalancesResult(result) {
463
+ const lines = [
464
+ `## Multi-Chain Balance Summary`,
465
+ `**Address:** \`${result.address}\``,
466
+ "",
467
+ `### Totals`,
468
+ `- **Total USDC:** ${result.totalUsdcBalance}`,
469
+ `- **Total USDT:** ${result.totalUsdtBalance}`,
470
+ "",
471
+ `### By Network`,
472
+ ""
473
+ ];
474
+ for (const balance of result.balances) {
475
+ const hasBalance = balance.tokens.some((t) => BigInt(t.balance) > 0n) || BigInt(balance.native.balance) > 0n;
476
+ if (!hasBalance) continue;
477
+ lines.push(`#### ${balance.network}`);
478
+ lines.push(`- ${balance.native.symbol}: ${balance.native.formatted}`);
479
+ for (const token of balance.tokens) {
480
+ if (BigInt(token.balance) > 0n) {
481
+ lines.push(`- ${token.symbol}: ${token.formatted}`);
482
+ }
483
+ }
484
+ lines.push("");
485
+ }
486
+ const emptyNetworks = result.balances.filter(
487
+ (b) => !b.tokens.some((t) => BigInt(t.balance) > 0n) && BigInt(b.native.balance) === 0n
488
+ );
489
+ if (emptyNetworks.length > 0) {
490
+ lines.push(`_No balance on: ${emptyNetworks.map((n) => n.network).join(", ")}_`);
491
+ }
492
+ return lines.join("\n");
493
+ }
494
+
495
+ // src/tools/pay.ts
496
+ var import_zod3 = require("zod");
497
+ var import_viem2 = require("viem");
498
+ var import_accounts = require("viem/accounts");
499
+ var chains2 = __toESM(require("viem/chains"));
500
+ var payInputSchema = import_zod3.z.object({
501
+ to: import_zod3.z.string().regex(/^0x[a-fA-F0-9]{40}$/).describe("Recipient address"),
502
+ amount: import_zod3.z.string().regex(/^\d+(\.\d+)?$/).describe("Amount to pay (e.g., '10.50' for 10.50 USDC)"),
503
+ token: import_zod3.z.enum(["USDC", "USDT", "USDT0"]).describe("Token to use for payment"),
504
+ network: import_zod3.z.enum([
505
+ "ethereum",
506
+ "base",
507
+ "arbitrum",
508
+ "optimism",
509
+ "polygon",
510
+ "avalanche",
511
+ "ink",
512
+ "berachain",
513
+ "unichain"
514
+ ]).describe("Network to execute payment on"),
515
+ memo: import_zod3.z.string().optional().describe("Optional memo/reference for the payment")
516
+ });
517
+ function getViemChain2(network) {
518
+ switch (network) {
519
+ case "ethereum":
520
+ return chains2.mainnet;
521
+ case "base":
522
+ return chains2.base;
523
+ case "arbitrum":
524
+ return chains2.arbitrum;
525
+ case "optimism":
526
+ return chains2.optimism;
527
+ case "polygon":
528
+ return chains2.polygon;
529
+ case "avalanche":
530
+ return chains2.avalanche;
531
+ case "ink":
532
+ return chains2.ink;
533
+ case "berachain":
534
+ return chains2.berachain;
535
+ case "unichain":
536
+ return chains2.unichain;
537
+ default:
538
+ return chains2.mainnet;
539
+ }
540
+ }
541
+ async function executePay(input, options) {
542
+ const { to, amount, token, network, memo } = input;
543
+ const { privateKey, rpcUrl, demoMode } = options;
544
+ if (!supportsToken(network, token)) {
545
+ throw new Error(`Token ${token} is not supported on ${network}`);
546
+ }
547
+ const tokenAddress = getTokenAddress(network, token);
548
+ if (!tokenAddress) {
549
+ throw new Error(`Could not find ${token} address for ${network}`);
550
+ }
551
+ const decimals = 6;
552
+ const amountBigInt = (0, import_viem2.parseUnits)(amount, decimals);
553
+ if (demoMode) {
554
+ const fakeTxHash = `0x${"0".repeat(64)}`;
555
+ return {
556
+ txHash: fakeTxHash,
557
+ network,
558
+ amount,
559
+ token,
560
+ to,
561
+ explorerUrl: getExplorerTxUrl(network, fakeTxHash)
562
+ };
563
+ }
564
+ const chain = getViemChain2(network);
565
+ const transport = (0, import_viem2.http)(rpcUrl || DEFAULT_RPC_URLS[network]);
566
+ const account = (0, import_accounts.privateKeyToAccount)(privateKey);
567
+ const publicClient = (0, import_viem2.createPublicClient)({
568
+ chain,
569
+ transport
570
+ });
571
+ const walletClient = (0, import_viem2.createWalletClient)({
572
+ account,
573
+ chain,
574
+ transport
575
+ });
576
+ const balance = await publicClient.readContract({
577
+ address: tokenAddress,
578
+ abi: ERC20_ABI,
579
+ functionName: "balanceOf",
580
+ args: [account.address]
581
+ });
582
+ if (balance < amountBigInt) {
583
+ throw new Error(
584
+ `Insufficient ${token} balance. Have: ${balance.toString()}, Need: ${amountBigInt.toString()}`
585
+ );
586
+ }
587
+ const hash = await walletClient.writeContract({
588
+ address: tokenAddress,
589
+ abi: ERC20_ABI,
590
+ functionName: "transfer",
591
+ args: [to, amountBigInt]
592
+ });
593
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
594
+ if (receipt.status !== "success") {
595
+ throw new Error(`Transaction failed: ${hash}`);
596
+ }
597
+ return {
598
+ txHash: hash,
599
+ network,
600
+ amount,
601
+ token,
602
+ to,
603
+ explorerUrl: getExplorerTxUrl(network, hash)
604
+ };
605
+ }
606
+ function formatPaymentResult(result) {
607
+ return [
608
+ `## Payment Successful`,
609
+ "",
610
+ `- **Amount:** ${result.amount} ${result.token}`,
611
+ `- **To:** \`${result.to}\``,
612
+ `- **Network:** ${result.network}`,
613
+ `- **Transaction:** \`${result.txHash}\``,
614
+ "",
615
+ `[View on Explorer](${result.explorerUrl})`
616
+ ].join("\n");
617
+ }
618
+
619
+ // src/tools/payGasless.ts
620
+ var import_zod4 = require("zod");
621
+ var import_viem3 = require("viem");
622
+ var import_accounts2 = require("viem/accounts");
623
+ var chains3 = __toESM(require("viem/chains"));
624
+ var payGaslessInputSchema = import_zod4.z.object({
625
+ to: import_zod4.z.string().regex(/^0x[a-fA-F0-9]{40}$/).describe("Recipient address"),
626
+ amount: import_zod4.z.string().regex(/^\d+(\.\d+)?$/).describe("Amount to pay (e.g., '10.50' for 10.50 USDC)"),
627
+ token: import_zod4.z.enum(["USDC", "USDT", "USDT0"]).describe("Token to use for payment"),
628
+ network: import_zod4.z.enum([
629
+ "ethereum",
630
+ "base",
631
+ "arbitrum",
632
+ "optimism",
633
+ "polygon",
634
+ "avalanche"
635
+ ]).describe("Network to execute gasless payment on (must support ERC-4337)")
636
+ });
637
+ var GASLESS_SUPPORTED_NETWORKS = [
638
+ "ethereum",
639
+ "base",
640
+ "arbitrum",
641
+ "optimism",
642
+ "polygon",
643
+ "avalanche"
644
+ ];
645
+ function getViemChain3(network) {
646
+ switch (network) {
647
+ case "ethereum":
648
+ return chains3.mainnet;
649
+ case "base":
650
+ return chains3.base;
651
+ case "arbitrum":
652
+ return chains3.arbitrum;
653
+ case "optimism":
654
+ return chains3.optimism;
655
+ case "polygon":
656
+ return chains3.polygon;
657
+ case "avalanche":
658
+ return chains3.avalanche;
659
+ case "ink":
660
+ return chains3.ink;
661
+ case "berachain":
662
+ return chains3.berachain;
663
+ case "unichain":
664
+ return chains3.unichain;
665
+ default:
666
+ return chains3.mainnet;
667
+ }
668
+ }
669
+ async function executePayGasless(input, options) {
670
+ const { to, amount, token, network } = input;
671
+ const { privateKey, bundlerUrl, paymasterUrl, rpcUrl, demoMode } = options;
672
+ if (!GASLESS_SUPPORTED_NETWORKS.includes(network)) {
673
+ throw new Error(
674
+ `Network ${network} does not support ERC-4337 gasless transactions. Supported: ${GASLESS_SUPPORTED_NETWORKS.join(", ")}`
675
+ );
676
+ }
677
+ if (!supportsToken(network, token)) {
678
+ throw new Error(`Token ${token} is not supported on ${network}`);
679
+ }
680
+ const tokenAddress = getTokenAddress(network, token);
681
+ if (!tokenAddress) {
682
+ throw new Error(`Could not find ${token} address for ${network}`);
683
+ }
684
+ const decimals = 6;
685
+ const amountBigInt = (0, import_viem3.parseUnits)(amount, decimals);
686
+ if (demoMode) {
687
+ const fakeTxHash = `0x${"1".repeat(64)}`;
688
+ const fakeUserOpHash = `0x${"2".repeat(64)}`;
689
+ return {
690
+ txHash: fakeTxHash,
691
+ userOpHash: fakeUserOpHash,
692
+ network,
693
+ amount,
694
+ token,
695
+ to,
696
+ explorerUrl: getExplorerTxUrl(network, fakeTxHash),
697
+ paymaster: "demo-paymaster"
698
+ };
699
+ }
700
+ const chain = getViemChain3(network);
701
+ const transport = (0, import_viem3.http)(rpcUrl || DEFAULT_RPC_URLS[network]);
702
+ const account = (0, import_accounts2.privateKeyToAccount)(privateKey);
703
+ const publicClient = (0, import_viem3.createPublicClient)({
704
+ chain,
705
+ transport
706
+ });
707
+ const callData = (0, import_viem3.encodeFunctionData)({
708
+ abi: ERC20_ABI,
709
+ functionName: "transfer",
710
+ args: [to, amountBigInt]
711
+ });
712
+ const nonce = await publicClient.getTransactionCount({
713
+ address: account.address
714
+ });
715
+ const gasPrice = await publicClient.getGasPrice();
716
+ const userOp = {
717
+ sender: account.address,
718
+ nonce: BigInt(nonce),
719
+ initCode: "0x",
720
+ callData,
721
+ callGasLimit: 100000n,
722
+ verificationGasLimit: 100000n,
723
+ preVerificationGas: 50000n,
724
+ maxFeePerGas: gasPrice,
725
+ maxPriorityFeePerGas: gasPrice / 10n,
726
+ paymasterAndData: "0x",
727
+ // Would be filled by paymaster
728
+ signature: "0x"
729
+ };
730
+ const response = await fetch(bundlerUrl, {
731
+ method: "POST",
732
+ headers: { "Content-Type": "application/json" },
733
+ body: JSON.stringify({
734
+ jsonrpc: "2.0",
735
+ id: 1,
736
+ method: "eth_sendUserOperation",
737
+ params: [userOp, chain.id]
738
+ })
739
+ });
740
+ if (!response.ok) {
741
+ throw new Error(`Bundler request failed: ${response.statusText}`);
742
+ }
743
+ const result = await response.json();
744
+ if (result.error) {
745
+ throw new Error(`Bundler error: ${result.error.message}`);
746
+ }
747
+ const userOpHash = result.result;
748
+ let receipt = null;
749
+ for (let i = 0; i < 30; i++) {
750
+ const receiptResponse = await fetch(bundlerUrl, {
751
+ method: "POST",
752
+ headers: { "Content-Type": "application/json" },
753
+ body: JSON.stringify({
754
+ jsonrpc: "2.0",
755
+ id: 1,
756
+ method: "eth_getUserOperationReceipt",
757
+ params: [userOpHash]
758
+ })
759
+ });
760
+ const receiptResult = await receiptResponse.json();
761
+ if (receiptResult.result) {
762
+ receipt = receiptResult.result;
763
+ break;
764
+ }
765
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
766
+ }
767
+ if (!receipt) {
768
+ throw new Error("Timeout waiting for user operation receipt");
769
+ }
770
+ return {
771
+ txHash: receipt.transactionHash,
772
+ userOpHash,
773
+ network,
774
+ amount,
775
+ token,
776
+ to,
777
+ explorerUrl: getExplorerTxUrl(network, receipt.transactionHash)
778
+ };
779
+ }
780
+ function formatGaslessPaymentResult(result) {
781
+ const lines = [
782
+ `## Gasless Payment Successful`,
783
+ "",
784
+ `- **Amount:** ${result.amount} ${result.token}`,
785
+ `- **To:** \`${result.to}\``,
786
+ `- **Network:** ${result.network}`,
787
+ `- **Transaction:** \`${result.txHash}\``,
788
+ `- **UserOp Hash:** \`${result.userOpHash}\``
789
+ ];
790
+ if (result.paymaster) {
791
+ lines.push(`- **Paymaster:** ${result.paymaster}`);
792
+ }
793
+ lines.push("");
794
+ lines.push(`[View on Explorer](${result.explorerUrl})`);
795
+ lines.push("");
796
+ lines.push("_Gas fees were sponsored - no ETH was deducted from your wallet._");
797
+ return lines.join("\n");
798
+ }
799
+
800
+ // src/tools/getBridgeFee.ts
801
+ var import_zod5 = require("zod");
802
+ var import_viem4 = require("viem");
803
+ var chains4 = __toESM(require("viem/chains"));
804
+ var getBridgeFeeInputSchema = import_zod5.z.object({
805
+ fromChain: import_zod5.z.enum(["ethereum", "arbitrum", "ink", "berachain", "unichain"]).describe("Source chain to bridge from"),
806
+ toChain: import_zod5.z.enum(["ethereum", "arbitrum", "ink", "berachain", "unichain"]).describe("Destination chain to bridge to"),
807
+ amount: import_zod5.z.string().regex(/^\d+(\.\d+)?$/).describe("Amount of USDT0 to bridge (e.g., '100' for 100 USDT0)"),
808
+ recipient: import_zod5.z.string().regex(/^0x[a-fA-F0-9]{40}$/).describe("Recipient address on destination chain")
809
+ });
810
+ var LAYERZERO_ENDPOINT_IDS = {
811
+ ethereum: 30101,
812
+ arbitrum: 30110,
813
+ ink: 30291,
814
+ berachain: 30362,
815
+ unichain: 30320
816
+ };
817
+ var ESTIMATED_BRIDGE_TIMES = {
818
+ ethereum: 900,
819
+ // 15 minutes
820
+ arbitrum: 300,
821
+ // 5 minutes
822
+ ink: 300,
823
+ // 5 minutes
824
+ berachain: 300,
825
+ // 5 minutes
826
+ unichain: 300
827
+ // 5 minutes
828
+ };
829
+ var OFT_ABI = [
830
+ {
831
+ name: "quoteSend",
832
+ type: "function",
833
+ stateMutability: "view",
834
+ inputs: [
835
+ {
836
+ name: "_sendParam",
837
+ type: "tuple",
838
+ components: [
839
+ { name: "dstEid", type: "uint32" },
840
+ { name: "to", type: "bytes32" },
841
+ { name: "amountLD", type: "uint256" },
842
+ { name: "minAmountLD", type: "uint256" },
843
+ { name: "extraOptions", type: "bytes" },
844
+ { name: "composeMsg", type: "bytes" },
845
+ { name: "oftCmd", type: "bytes" }
846
+ ]
847
+ },
848
+ { name: "_payInLzToken", type: "bool" }
849
+ ],
850
+ outputs: [
851
+ {
852
+ name: "msgFee",
853
+ type: "tuple",
854
+ components: [
855
+ { name: "nativeFee", type: "uint256" },
856
+ { name: "lzTokenFee", type: "uint256" }
857
+ ]
858
+ }
859
+ ]
860
+ }
861
+ ];
862
+ function getViemChain4(network) {
863
+ switch (network) {
864
+ case "ethereum":
865
+ return chains4.mainnet;
866
+ case "arbitrum":
867
+ return chains4.arbitrum;
868
+ case "ink":
869
+ return chains4.ink;
870
+ case "berachain":
871
+ return chains4.berachain;
872
+ case "unichain":
873
+ return chains4.unichain;
874
+ default:
875
+ return void 0;
876
+ }
877
+ }
878
+ function addressToBytes32(address) {
879
+ return `0x${address.slice(2).padStart(64, "0")}`;
880
+ }
881
+ async function executeGetBridgeFee(input, rpcUrls) {
882
+ const { fromChain, toChain, amount, recipient } = input;
883
+ if (fromChain === toChain) {
884
+ throw new Error("Source and destination chains must be different");
885
+ }
886
+ if (!BRIDGEABLE_CHAINS.includes(fromChain)) {
887
+ throw new Error(`Chain ${fromChain} does not support USDT0 bridging`);
888
+ }
889
+ if (!BRIDGEABLE_CHAINS.includes(toChain)) {
890
+ throw new Error(`Chain ${toChain} does not support USDT0 bridging`);
891
+ }
892
+ const usdt0Address = USDT0_ADDRESSES[fromChain];
893
+ if (!usdt0Address) {
894
+ throw new Error(`USDT0 not found on ${fromChain}`);
895
+ }
896
+ const amountBigInt = parseTokenAmount(amount, 6);
897
+ const chain = getViemChain4(fromChain);
898
+ if (!chain) {
899
+ throw new Error(`Unsupported chain: ${fromChain}`);
900
+ }
901
+ const rpcUrl = rpcUrls?.[fromChain] || DEFAULT_RPC_URLS[fromChain];
902
+ const client = (0, import_viem4.createPublicClient)({
903
+ chain,
904
+ transport: (0, import_viem4.http)(rpcUrl)
905
+ });
906
+ const dstEid = LAYERZERO_ENDPOINT_IDS[toChain];
907
+ const sendParam = {
908
+ dstEid,
909
+ to: addressToBytes32(recipient),
910
+ amountLD: amountBigInt,
911
+ minAmountLD: amountBigInt,
912
+ // No slippage for quote
913
+ extraOptions: "0x",
914
+ composeMsg: "0x",
915
+ oftCmd: "0x"
916
+ };
917
+ const quote = await client.readContract({
918
+ address: usdt0Address,
919
+ abi: OFT_ABI,
920
+ functionName: "quoteSend",
921
+ args: [sendParam, false]
922
+ });
923
+ const nativeSymbol = NATIVE_SYMBOLS[fromChain];
924
+ const estimatedTime = ESTIMATED_BRIDGE_TIMES[toChain] || 300;
925
+ return {
926
+ fromChain,
927
+ toChain,
928
+ amount,
929
+ nativeFee: quote.nativeFee.toString(),
930
+ nativeFeeFormatted: `${(0, import_viem4.formatEther)(quote.nativeFee)} ${nativeSymbol}`,
931
+ estimatedTime
932
+ };
933
+ }
934
+ function formatBridgeFeeResult(result) {
935
+ const minutes = Math.ceil(result.estimatedTime / 60);
936
+ return [
937
+ `## Bridge Fee Quote`,
938
+ "",
939
+ `- **Route:** ${result.fromChain} \u2192 ${result.toChain}`,
940
+ `- **Amount:** ${result.amount} USDT0`,
941
+ `- **Native Fee:** ${result.nativeFeeFormatted}`,
942
+ `- **Estimated Time:** ~${minutes} minutes`,
943
+ "",
944
+ "_Note: Actual fees may vary slightly at execution time._"
945
+ ].join("\n");
946
+ }
947
+
948
+ // src/tools/bridge.ts
949
+ var import_zod6 = require("zod");
950
+ var import_viem5 = require("viem");
951
+ var import_accounts3 = require("viem/accounts");
952
+ var chains5 = __toESM(require("viem/chains"));
953
+ var bridgeInputSchema = import_zod6.z.object({
954
+ fromChain: import_zod6.z.enum(["ethereum", "arbitrum", "ink", "berachain", "unichain"]).describe("Source chain to bridge from"),
955
+ toChain: import_zod6.z.enum(["ethereum", "arbitrum", "ink", "berachain", "unichain"]).describe("Destination chain to bridge to"),
956
+ amount: import_zod6.z.string().regex(/^\d+(\.\d+)?$/).describe("Amount of USDT0 to bridge (e.g., '100' for 100 USDT0)"),
957
+ recipient: import_zod6.z.string().regex(/^0x[a-fA-F0-9]{40}$/).describe("Recipient address on destination chain")
958
+ });
959
+ var LAYERZERO_ENDPOINT_IDS2 = {
960
+ ethereum: 30101,
961
+ arbitrum: 30110,
962
+ ink: 30291,
963
+ berachain: 30362,
964
+ unichain: 30320
965
+ };
966
+ var ESTIMATED_BRIDGE_TIMES2 = {
967
+ ethereum: 900,
968
+ arbitrum: 300,
969
+ ink: 300,
970
+ berachain: 300,
971
+ unichain: 300
972
+ };
973
+ var OFT_ABI2 = [
974
+ {
975
+ name: "quoteSend",
976
+ type: "function",
977
+ stateMutability: "view",
978
+ inputs: [
979
+ {
980
+ name: "_sendParam",
981
+ type: "tuple",
982
+ components: [
983
+ { name: "dstEid", type: "uint32" },
984
+ { name: "to", type: "bytes32" },
985
+ { name: "amountLD", type: "uint256" },
986
+ { name: "minAmountLD", type: "uint256" },
987
+ { name: "extraOptions", type: "bytes" },
988
+ { name: "composeMsg", type: "bytes" },
989
+ { name: "oftCmd", type: "bytes" }
990
+ ]
991
+ },
992
+ { name: "_payInLzToken", type: "bool" }
993
+ ],
994
+ outputs: [
995
+ {
996
+ name: "msgFee",
997
+ type: "tuple",
998
+ components: [
999
+ { name: "nativeFee", type: "uint256" },
1000
+ { name: "lzTokenFee", type: "uint256" }
1001
+ ]
1002
+ }
1003
+ ]
1004
+ },
1005
+ {
1006
+ name: "send",
1007
+ type: "function",
1008
+ stateMutability: "payable",
1009
+ inputs: [
1010
+ {
1011
+ name: "_sendParam",
1012
+ type: "tuple",
1013
+ components: [
1014
+ { name: "dstEid", type: "uint32" },
1015
+ { name: "to", type: "bytes32" },
1016
+ { name: "amountLD", type: "uint256" },
1017
+ { name: "minAmountLD", type: "uint256" },
1018
+ { name: "extraOptions", type: "bytes" },
1019
+ { name: "composeMsg", type: "bytes" },
1020
+ { name: "oftCmd", type: "bytes" }
1021
+ ]
1022
+ },
1023
+ {
1024
+ name: "_fee",
1025
+ type: "tuple",
1026
+ components: [
1027
+ { name: "nativeFee", type: "uint256" },
1028
+ { name: "lzTokenFee", type: "uint256" }
1029
+ ]
1030
+ },
1031
+ { name: "_refundAddress", type: "address" }
1032
+ ],
1033
+ outputs: [
1034
+ {
1035
+ name: "msgReceipt",
1036
+ type: "tuple",
1037
+ components: [
1038
+ { name: "guid", type: "bytes32" },
1039
+ { name: "nonce", type: "uint64" },
1040
+ {
1041
+ name: "fee",
1042
+ type: "tuple",
1043
+ components: [
1044
+ { name: "nativeFee", type: "uint256" },
1045
+ { name: "lzTokenFee", type: "uint256" }
1046
+ ]
1047
+ }
1048
+ ]
1049
+ },
1050
+ {
1051
+ name: "oftReceipt",
1052
+ type: "tuple",
1053
+ components: [
1054
+ { name: "amountSentLD", type: "uint256" },
1055
+ { name: "amountReceivedLD", type: "uint256" }
1056
+ ]
1057
+ }
1058
+ ]
1059
+ }
1060
+ ];
1061
+ var OFT_SENT_EVENT_TOPIC = (0, import_viem5.keccak256)(
1062
+ (0, import_viem5.toBytes)("OFTSent(bytes32,uint32,address,uint256,uint256)")
1063
+ );
1064
+ function getViemChain5(network) {
1065
+ switch (network) {
1066
+ case "ethereum":
1067
+ return chains5.mainnet;
1068
+ case "arbitrum":
1069
+ return chains5.arbitrum;
1070
+ case "ink":
1071
+ return chains5.ink;
1072
+ case "berachain":
1073
+ return chains5.berachain;
1074
+ case "unichain":
1075
+ return chains5.unichain;
1076
+ default:
1077
+ return void 0;
1078
+ }
1079
+ }
1080
+ function addressToBytes322(address) {
1081
+ return `0x${address.slice(2).padStart(64, "0")}`;
1082
+ }
1083
+ async function executeBridge(input, options) {
1084
+ const { fromChain, toChain, amount, recipient } = input;
1085
+ const { privateKey, rpcUrl, demoMode, slippageTolerance = 0.5 } = options;
1086
+ if (fromChain === toChain) {
1087
+ throw new Error("Source and destination chains must be different");
1088
+ }
1089
+ if (!BRIDGEABLE_CHAINS.includes(fromChain)) {
1090
+ throw new Error(`Chain ${fromChain} does not support USDT0 bridging`);
1091
+ }
1092
+ if (!BRIDGEABLE_CHAINS.includes(toChain)) {
1093
+ throw new Error(`Chain ${toChain} does not support USDT0 bridging`);
1094
+ }
1095
+ const usdt0Address = USDT0_ADDRESSES[fromChain];
1096
+ if (!usdt0Address) {
1097
+ throw new Error(`USDT0 not found on ${fromChain}`);
1098
+ }
1099
+ const amountBigInt = parseTokenAmount(amount, 6);
1100
+ const slippageMultiplier = BigInt(Math.floor((100 - slippageTolerance) * 100));
1101
+ const minAmountBigInt = amountBigInt * slippageMultiplier / 10000n;
1102
+ if (demoMode) {
1103
+ const fakeTxHash = `0x${"a".repeat(64)}`;
1104
+ const fakeGuid = `0x${"b".repeat(64)}`;
1105
+ const estimatedTime2 = ESTIMATED_BRIDGE_TIMES2[toChain] || 300;
1106
+ return {
1107
+ txHash: fakeTxHash,
1108
+ messageGuid: fakeGuid,
1109
+ amount,
1110
+ fromChain,
1111
+ toChain,
1112
+ estimatedTime: estimatedTime2,
1113
+ trackingUrl: getLayerZeroScanUrl(fakeGuid)
1114
+ };
1115
+ }
1116
+ const chain = getViemChain5(fromChain);
1117
+ if (!chain) {
1118
+ throw new Error(`Unsupported chain: ${fromChain}`);
1119
+ }
1120
+ const transport = (0, import_viem5.http)(rpcUrl || DEFAULT_RPC_URLS[fromChain]);
1121
+ const account = (0, import_accounts3.privateKeyToAccount)(privateKey);
1122
+ const publicClient = (0, import_viem5.createPublicClient)({
1123
+ chain,
1124
+ transport
1125
+ });
1126
+ const walletClient = (0, import_viem5.createWalletClient)({
1127
+ account,
1128
+ chain,
1129
+ transport
1130
+ });
1131
+ const balance = await publicClient.readContract({
1132
+ address: usdt0Address,
1133
+ abi: ERC20_ABI,
1134
+ functionName: "balanceOf",
1135
+ args: [account.address]
1136
+ });
1137
+ if (balance < amountBigInt) {
1138
+ throw new Error(
1139
+ `Insufficient USDT0 balance. Have: ${balance.toString()}, Need: ${amountBigInt.toString()}`
1140
+ );
1141
+ }
1142
+ const dstEid = LAYERZERO_ENDPOINT_IDS2[toChain];
1143
+ const sendParam = {
1144
+ dstEid,
1145
+ to: addressToBytes322(recipient),
1146
+ amountLD: amountBigInt,
1147
+ minAmountLD: minAmountBigInt,
1148
+ extraOptions: "0x",
1149
+ composeMsg: "0x",
1150
+ oftCmd: "0x"
1151
+ };
1152
+ const quote = await publicClient.readContract({
1153
+ address: usdt0Address,
1154
+ abi: OFT_ABI2,
1155
+ functionName: "quoteSend",
1156
+ args: [sendParam, false]
1157
+ });
1158
+ const nativeFeeWithBuffer = quote.nativeFee * 110n / 100n;
1159
+ const hash = await walletClient.writeContract({
1160
+ address: usdt0Address,
1161
+ abi: OFT_ABI2,
1162
+ functionName: "send",
1163
+ args: [
1164
+ sendParam,
1165
+ { nativeFee: nativeFeeWithBuffer, lzTokenFee: 0n },
1166
+ account.address
1167
+ ],
1168
+ value: nativeFeeWithBuffer
1169
+ });
1170
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
1171
+ if (receipt.status !== "success") {
1172
+ throw new Error(`Bridge transaction failed: ${hash}`);
1173
+ }
1174
+ let messageGuid = "0x";
1175
+ for (const log of receipt.logs) {
1176
+ if (log.topics[0] === OFT_SENT_EVENT_TOPIC && log.topics[1]) {
1177
+ messageGuid = log.topics[1];
1178
+ break;
1179
+ }
1180
+ }
1181
+ if (messageGuid === "0x") {
1182
+ throw new Error("Failed to extract message GUID from transaction logs");
1183
+ }
1184
+ const estimatedTime = ESTIMATED_BRIDGE_TIMES2[toChain] || 300;
1185
+ return {
1186
+ txHash: hash,
1187
+ messageGuid,
1188
+ amount,
1189
+ fromChain,
1190
+ toChain,
1191
+ estimatedTime,
1192
+ trackingUrl: getLayerZeroScanUrl(messageGuid)
1193
+ };
1194
+ }
1195
+ function formatBridgeResult(result) {
1196
+ const minutes = Math.ceil(result.estimatedTime / 60);
1197
+ return [
1198
+ `## Bridge Transaction Submitted`,
1199
+ "",
1200
+ `- **Route:** ${result.fromChain} \u2192 ${result.toChain}`,
1201
+ `- **Amount:** ${result.amount} USDT0`,
1202
+ `- **Transaction:** \`${result.txHash}\``,
1203
+ `- **Message GUID:** \`${result.messageGuid}\``,
1204
+ `- **Estimated Delivery:** ~${minutes} minutes`,
1205
+ "",
1206
+ `### Tracking`,
1207
+ `- [View on LayerZero Scan](${result.trackingUrl})`,
1208
+ `- [View Source TX](${getExplorerTxUrl(result.fromChain, result.txHash)})`,
1209
+ "",
1210
+ "_Your USDT0 will arrive on ${result.toChain} once the LayerZero message is delivered._"
1211
+ ].join("\n");
1212
+ }
1213
+
1214
+ // src/tools/index.ts
1215
+ var TOOL_DEFINITIONS = {
1216
+ "t402/getBalance": {
1217
+ name: "t402/getBalance",
1218
+ description: "Get token balances (native + stablecoins) for a wallet on a specific blockchain network. Returns ETH/native token balance plus USDC, USDT, and USDT0 balances where supported.",
1219
+ inputSchema: {
1220
+ type: "object",
1221
+ properties: {
1222
+ network: {
1223
+ type: "string",
1224
+ enum: [
1225
+ "ethereum",
1226
+ "base",
1227
+ "arbitrum",
1228
+ "optimism",
1229
+ "polygon",
1230
+ "avalanche",
1231
+ "ink",
1232
+ "berachain",
1233
+ "unichain"
1234
+ ],
1235
+ description: "Blockchain network to check balance on"
1236
+ },
1237
+ address: {
1238
+ type: "string",
1239
+ pattern: "^0x[a-fA-F0-9]{40}$",
1240
+ description: "Wallet address to check balance for"
1241
+ }
1242
+ },
1243
+ required: ["network", "address"]
1244
+ }
1245
+ },
1246
+ "t402/getAllBalances": {
1247
+ name: "t402/getAllBalances",
1248
+ description: "Get token balances across all supported networks for a wallet. Returns aggregated totals and per-network breakdown of native tokens and stablecoins (USDC, USDT, USDT0).",
1249
+ inputSchema: {
1250
+ type: "object",
1251
+ properties: {
1252
+ address: {
1253
+ type: "string",
1254
+ pattern: "^0x[a-fA-F0-9]{40}$",
1255
+ description: "Wallet address to check balances for"
1256
+ },
1257
+ networks: {
1258
+ type: "array",
1259
+ items: {
1260
+ type: "string",
1261
+ enum: [
1262
+ "ethereum",
1263
+ "base",
1264
+ "arbitrum",
1265
+ "optimism",
1266
+ "polygon",
1267
+ "avalanche",
1268
+ "ink",
1269
+ "berachain",
1270
+ "unichain"
1271
+ ]
1272
+ },
1273
+ description: "Optional list of networks to check. If not provided, checks all supported networks."
1274
+ }
1275
+ },
1276
+ required: ["address"]
1277
+ }
1278
+ },
1279
+ "t402/pay": {
1280
+ name: "t402/pay",
1281
+ description: "Execute a stablecoin payment on a specific blockchain network. Supports USDC, USDT, and USDT0 tokens. Requires a configured wallet with sufficient balance and native token for gas.",
1282
+ inputSchema: {
1283
+ type: "object",
1284
+ properties: {
1285
+ to: {
1286
+ type: "string",
1287
+ pattern: "^0x[a-fA-F0-9]{40}$",
1288
+ description: "Recipient address"
1289
+ },
1290
+ amount: {
1291
+ type: "string",
1292
+ pattern: "^\\d+(\\.\\d+)?$",
1293
+ description: "Amount to pay (e.g., '10.50' for 10.50 USDC)"
1294
+ },
1295
+ token: {
1296
+ type: "string",
1297
+ enum: ["USDC", "USDT", "USDT0"],
1298
+ description: "Token to use for payment"
1299
+ },
1300
+ network: {
1301
+ type: "string",
1302
+ enum: [
1303
+ "ethereum",
1304
+ "base",
1305
+ "arbitrum",
1306
+ "optimism",
1307
+ "polygon",
1308
+ "avalanche",
1309
+ "ink",
1310
+ "berachain",
1311
+ "unichain"
1312
+ ],
1313
+ description: "Network to execute payment on"
1314
+ },
1315
+ memo: {
1316
+ type: "string",
1317
+ description: "Optional memo/reference for the payment"
1318
+ }
1319
+ },
1320
+ required: ["to", "amount", "token", "network"]
1321
+ }
1322
+ },
1323
+ "t402/payGasless": {
1324
+ name: "t402/payGasless",
1325
+ description: "Execute a gasless stablecoin payment using ERC-4337 account abstraction. Gas fees are sponsored by a paymaster, so no ETH is needed for the transaction. Supported on select networks.",
1326
+ inputSchema: {
1327
+ type: "object",
1328
+ properties: {
1329
+ to: {
1330
+ type: "string",
1331
+ pattern: "^0x[a-fA-F0-9]{40}$",
1332
+ description: "Recipient address"
1333
+ },
1334
+ amount: {
1335
+ type: "string",
1336
+ pattern: "^\\d+(\\.\\d+)?$",
1337
+ description: "Amount to pay (e.g., '10.50' for 10.50 USDC)"
1338
+ },
1339
+ token: {
1340
+ type: "string",
1341
+ enum: ["USDC", "USDT", "USDT0"],
1342
+ description: "Token to use for payment"
1343
+ },
1344
+ network: {
1345
+ type: "string",
1346
+ enum: ["ethereum", "base", "arbitrum", "optimism", "polygon", "avalanche"],
1347
+ description: "Network to execute gasless payment on (must support ERC-4337)"
1348
+ }
1349
+ },
1350
+ required: ["to", "amount", "token", "network"]
1351
+ }
1352
+ },
1353
+ "t402/getBridgeFee": {
1354
+ name: "t402/getBridgeFee",
1355
+ description: "Get the fee quote for bridging USDT0 between chains using LayerZero OFT. Returns the native token fee required and estimated delivery time. Supported chains: ethereum, arbitrum, ink, berachain, unichain.",
1356
+ inputSchema: {
1357
+ type: "object",
1358
+ properties: {
1359
+ fromChain: {
1360
+ type: "string",
1361
+ enum: ["ethereum", "arbitrum", "ink", "berachain", "unichain"],
1362
+ description: "Source chain to bridge from"
1363
+ },
1364
+ toChain: {
1365
+ type: "string",
1366
+ enum: ["ethereum", "arbitrum", "ink", "berachain", "unichain"],
1367
+ description: "Destination chain to bridge to"
1368
+ },
1369
+ amount: {
1370
+ type: "string",
1371
+ pattern: "^\\d+(\\.\\d+)?$",
1372
+ description: "Amount of USDT0 to bridge (e.g., '100' for 100 USDT0)"
1373
+ },
1374
+ recipient: {
1375
+ type: "string",
1376
+ pattern: "^0x[a-fA-F0-9]{40}$",
1377
+ description: "Recipient address on destination chain"
1378
+ }
1379
+ },
1380
+ required: ["fromChain", "toChain", "amount", "recipient"]
1381
+ }
1382
+ },
1383
+ "t402/bridge": {
1384
+ name: "t402/bridge",
1385
+ description: "Bridge USDT0 between chains using LayerZero OFT standard. Executes a cross-chain transfer and returns the LayerZero message GUID for tracking. Supported chains: ethereum, arbitrum, ink, berachain, unichain.",
1386
+ inputSchema: {
1387
+ type: "object",
1388
+ properties: {
1389
+ fromChain: {
1390
+ type: "string",
1391
+ enum: ["ethereum", "arbitrum", "ink", "berachain", "unichain"],
1392
+ description: "Source chain to bridge from"
1393
+ },
1394
+ toChain: {
1395
+ type: "string",
1396
+ enum: ["ethereum", "arbitrum", "ink", "berachain", "unichain"],
1397
+ description: "Destination chain to bridge to"
1398
+ },
1399
+ amount: {
1400
+ type: "string",
1401
+ pattern: "^\\d+(\\.\\d+)?$",
1402
+ description: "Amount of USDT0 to bridge (e.g., '100' for 100 USDT0)"
1403
+ },
1404
+ recipient: {
1405
+ type: "string",
1406
+ pattern: "^0x[a-fA-F0-9]{40}$",
1407
+ description: "Recipient address on destination chain"
1408
+ }
1409
+ },
1410
+ required: ["fromChain", "toChain", "amount", "recipient"]
1411
+ }
1412
+ }
1413
+ };
1414
+
1415
+ // src/server/t402Server.ts
1416
+ var T402McpServer = class {
1417
+ constructor(config = {}) {
1418
+ this.config = config;
1419
+ this.server = new import_server.Server(
1420
+ {
1421
+ name: "t402",
1422
+ version: "1.0.0"
1423
+ },
1424
+ {
1425
+ capabilities: {
1426
+ tools: {}
1427
+ }
1428
+ }
1429
+ );
1430
+ this.setupHandlers();
1431
+ }
1432
+ /**
1433
+ * Set up MCP request handlers
1434
+ */
1435
+ setupHandlers() {
1436
+ this.server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
1437
+ return {
1438
+ tools: Object.values(TOOL_DEFINITIONS)
1439
+ };
1440
+ });
1441
+ this.server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
1442
+ const { name, arguments: args } = request.params;
1443
+ try {
1444
+ switch (name) {
1445
+ case "t402/getBalance":
1446
+ return await this.handleGetBalance(args);
1447
+ case "t402/getAllBalances":
1448
+ return await this.handleGetAllBalances(args);
1449
+ case "t402/pay":
1450
+ return await this.handlePay(args);
1451
+ case "t402/payGasless":
1452
+ return await this.handlePayGasless(args);
1453
+ case "t402/getBridgeFee":
1454
+ return await this.handleGetBridgeFee(args);
1455
+ case "t402/bridge":
1456
+ return await this.handleBridge(args);
1457
+ default:
1458
+ throw new Error(`Unknown tool: ${name}`);
1459
+ }
1460
+ } catch (error) {
1461
+ const message = error instanceof Error ? error.message : String(error);
1462
+ return {
1463
+ content: [
1464
+ {
1465
+ type: "text",
1466
+ text: `Error: ${message}`
1467
+ }
1468
+ ],
1469
+ isError: true
1470
+ };
1471
+ }
1472
+ });
1473
+ }
1474
+ /**
1475
+ * Handle t402/getBalance
1476
+ */
1477
+ async handleGetBalance(args) {
1478
+ const input = getBalanceInputSchema.parse(args);
1479
+ const result = await executeGetBalance(input, this.config.rpcUrls);
1480
+ return {
1481
+ content: [
1482
+ {
1483
+ type: "text",
1484
+ text: formatBalanceResult(result)
1485
+ }
1486
+ ]
1487
+ };
1488
+ }
1489
+ /**
1490
+ * Handle t402/getAllBalances
1491
+ */
1492
+ async handleGetAllBalances(args) {
1493
+ const input = getAllBalancesInputSchema.parse(args);
1494
+ const result = await executeGetAllBalances(input, this.config.rpcUrls);
1495
+ return {
1496
+ content: [
1497
+ {
1498
+ type: "text",
1499
+ text: formatAllBalancesResult(result)
1500
+ }
1501
+ ]
1502
+ };
1503
+ }
1504
+ /**
1505
+ * Handle t402/pay
1506
+ */
1507
+ async handlePay(args) {
1508
+ if (!this.config.privateKey && !this.config.demoMode) {
1509
+ throw new Error(
1510
+ "Private key not configured. Set T402_PRIVATE_KEY environment variable or enable demo mode."
1511
+ );
1512
+ }
1513
+ const input = payInputSchema.parse(args);
1514
+ const result = await executePay(input, {
1515
+ privateKey: this.config.privateKey || "0x",
1516
+ rpcUrl: this.config.rpcUrls?.[input.network],
1517
+ demoMode: this.config.demoMode
1518
+ });
1519
+ return {
1520
+ content: [
1521
+ {
1522
+ type: "text",
1523
+ text: formatPaymentResult(result)
1524
+ }
1525
+ ]
1526
+ };
1527
+ }
1528
+ /**
1529
+ * Handle t402/payGasless
1530
+ */
1531
+ async handlePayGasless(args) {
1532
+ if (!this.config.privateKey && !this.config.demoMode) {
1533
+ throw new Error(
1534
+ "Private key not configured. Set T402_PRIVATE_KEY environment variable or enable demo mode."
1535
+ );
1536
+ }
1537
+ if (!this.config.bundlerUrl && !this.config.demoMode) {
1538
+ throw new Error(
1539
+ "Bundler URL not configured. Set T402_BUNDLER_URL environment variable or enable demo mode."
1540
+ );
1541
+ }
1542
+ if (!this.config.paymasterUrl && !this.config.demoMode) {
1543
+ throw new Error(
1544
+ "Paymaster URL not configured. Set T402_PAYMASTER_URL environment variable or enable demo mode."
1545
+ );
1546
+ }
1547
+ const input = payGaslessInputSchema.parse(args);
1548
+ const result = await executePayGasless(input, {
1549
+ privateKey: this.config.privateKey || "0x",
1550
+ bundlerUrl: this.config.bundlerUrl || "",
1551
+ paymasterUrl: this.config.paymasterUrl || "",
1552
+ rpcUrl: this.config.rpcUrls?.[input.network],
1553
+ demoMode: this.config.demoMode
1554
+ });
1555
+ return {
1556
+ content: [
1557
+ {
1558
+ type: "text",
1559
+ text: formatGaslessPaymentResult(result)
1560
+ }
1561
+ ]
1562
+ };
1563
+ }
1564
+ /**
1565
+ * Handle t402/getBridgeFee
1566
+ */
1567
+ async handleGetBridgeFee(args) {
1568
+ const input = getBridgeFeeInputSchema.parse(args);
1569
+ const result = await executeGetBridgeFee(input, this.config.rpcUrls);
1570
+ return {
1571
+ content: [
1572
+ {
1573
+ type: "text",
1574
+ text: formatBridgeFeeResult(result)
1575
+ }
1576
+ ]
1577
+ };
1578
+ }
1579
+ /**
1580
+ * Handle t402/bridge
1581
+ */
1582
+ async handleBridge(args) {
1583
+ if (!this.config.privateKey && !this.config.demoMode) {
1584
+ throw new Error(
1585
+ "Private key not configured. Set T402_PRIVATE_KEY environment variable or enable demo mode."
1586
+ );
1587
+ }
1588
+ const input = bridgeInputSchema.parse(args);
1589
+ const result = await executeBridge(input, {
1590
+ privateKey: this.config.privateKey || "0x",
1591
+ rpcUrl: this.config.rpcUrls?.[input.fromChain],
1592
+ demoMode: this.config.demoMode
1593
+ });
1594
+ return {
1595
+ content: [
1596
+ {
1597
+ type: "text",
1598
+ text: formatBridgeResult(result)
1599
+ }
1600
+ ]
1601
+ };
1602
+ }
1603
+ /**
1604
+ * Start the server using stdio transport
1605
+ */
1606
+ async run() {
1607
+ const transport = new import_stdio.StdioServerTransport();
1608
+ await this.server.connect(transport);
1609
+ console.error("t402 MCP Server running on stdio");
1610
+ }
1611
+ };
1612
+ function createT402McpServer(config) {
1613
+ return new T402McpServer(config);
1614
+ }
1615
+ function loadConfigFromEnv() {
1616
+ const config = {};
1617
+ if (process.env.T402_PRIVATE_KEY) {
1618
+ config.privateKey = process.env.T402_PRIVATE_KEY;
1619
+ }
1620
+ if (process.env.T402_DEMO_MODE === "true") {
1621
+ config.demoMode = true;
1622
+ }
1623
+ if (process.env.T402_BUNDLER_URL) {
1624
+ config.bundlerUrl = process.env.T402_BUNDLER_URL;
1625
+ }
1626
+ if (process.env.T402_PAYMASTER_URL) {
1627
+ config.paymasterUrl = process.env.T402_PAYMASTER_URL;
1628
+ }
1629
+ const rpcUrls = {};
1630
+ const networks = [
1631
+ "ethereum",
1632
+ "base",
1633
+ "arbitrum",
1634
+ "optimism",
1635
+ "polygon",
1636
+ "avalanche",
1637
+ "ink",
1638
+ "berachain",
1639
+ "unichain"
1640
+ ];
1641
+ for (const network of networks) {
1642
+ const envVar = `T402_RPC_${network.toUpperCase()}`;
1643
+ if (process.env[envVar]) {
1644
+ rpcUrls[network] = process.env[envVar];
1645
+ }
1646
+ }
1647
+ if (Object.keys(rpcUrls).length > 0) {
1648
+ config.rpcUrls = rpcUrls;
1649
+ }
1650
+ return config;
1651
+ }
1652
+ // Annotate the CommonJS export names for ESM import in node:
1653
+ 0 && (module.exports = {
1654
+ BRIDGEABLE_CHAINS,
1655
+ CHAIN_IDS,
1656
+ DEFAULT_RPC_URLS,
1657
+ EXPLORER_URLS,
1658
+ GASLESS_SUPPORTED_NETWORKS,
1659
+ NATIVE_SYMBOLS,
1660
+ T402McpServer,
1661
+ TOOL_DEFINITIONS,
1662
+ USDC_ADDRESSES,
1663
+ USDT0_ADDRESSES,
1664
+ USDT_ADDRESSES,
1665
+ bridgeInputSchema,
1666
+ createT402McpServer,
1667
+ executeBridge,
1668
+ executeGetAllBalances,
1669
+ executeGetBalance,
1670
+ executeGetBridgeFee,
1671
+ executePay,
1672
+ executePayGasless,
1673
+ formatAllBalancesResult,
1674
+ formatBalanceResult,
1675
+ formatBridgeFeeResult,
1676
+ formatBridgeResult,
1677
+ formatGaslessPaymentResult,
1678
+ formatPaymentResult,
1679
+ formatTokenAmount,
1680
+ getAllBalancesInputSchema,
1681
+ getBalanceInputSchema,
1682
+ getBridgeFeeInputSchema,
1683
+ getExplorerTxUrl,
1684
+ getLayerZeroScanUrl,
1685
+ getTokenAddress,
1686
+ loadConfigFromEnv,
1687
+ parseTokenAmount,
1688
+ payGaslessInputSchema,
1689
+ payInputSchema,
1690
+ supportsToken
1691
+ });
1692
+ //# sourceMappingURL=index.js.map