@continuumdao/ctm-mpc-defi 0.2.13 → 0.2.17
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/dist/agent/catalog.cjs +1149 -287
- package/dist/agent/catalog.cjs.map +1 -1
- package/dist/agent/catalog.d.ts +1655 -306
- package/dist/agent/catalog.js +1144 -288
- package/dist/agent/catalog.js.map +1 -1
- package/dist/agent/skills/_shared/multisign-mcp-gas.md +7 -4
- package/dist/agent/skills/circle-cctp/SKILL.md +63 -0
- package/dist/chains/evm/index.cjs +75 -0
- package/dist/chains/evm/index.cjs.map +1 -1
- package/dist/chains/evm/index.d.ts +31 -1
- package/dist/chains/evm/index.js +73 -2
- package/dist/chains/evm/index.js.map +1 -1
- package/dist/index.cjs +140 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +138 -34
- package/dist/index.js.map +1 -1
- package/dist/protocols/evm/circle-cctp/index.cjs +1067 -0
- package/dist/protocols/evm/circle-cctp/index.cjs.map +1 -0
- package/dist/protocols/evm/circle-cctp/index.d.ts +281 -0
- package/dist/protocols/evm/circle-cctp/index.js +1026 -0
- package/dist/protocols/evm/circle-cctp/index.js.map +1 -0
- package/dist/protocols/evm/euler-v2/index.cjs +29 -2
- package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
- package/dist/protocols/evm/euler-v2/index.d.ts +23 -1
- package/dist/protocols/evm/euler-v2/index.js +29 -3
- package/dist/protocols/evm/euler-v2/index.js.map +1 -1
- package/dist/protocols/evm/gmx/index.cjs +42 -8
- package/dist/protocols/evm/gmx/index.cjs.map +1 -1
- package/dist/protocols/evm/gmx/index.d.ts +20 -4
- package/dist/protocols/evm/gmx/index.js +42 -9
- package/dist/protocols/evm/gmx/index.js.map +1 -1
- package/dist/protocols/evm/maple/index.cjs +9 -1
- package/dist/protocols/evm/maple/index.cjs.map +1 -1
- package/dist/protocols/evm/maple/index.d.ts +2 -1
- package/dist/protocols/evm/maple/index.js +9 -1
- package/dist/protocols/evm/maple/index.js.map +1 -1
- package/dist/protocols/evm/morpho/index.cjs +56 -0
- package/dist/protocols/evm/morpho/index.cjs.map +1 -1
- package/dist/protocols/evm/morpho/index.d.ts +28 -1
- package/dist/protocols/evm/morpho/index.js +56 -1
- package/dist/protocols/evm/morpho/index.js.map +1 -1
- package/dist/protocols/evm/uniswap-v4/index.cjs +98 -26
- package/dist/protocols/evm/uniswap-v4/index.cjs.map +1 -1
- package/dist/protocols/evm/uniswap-v4/index.js +99 -27
- package/dist/protocols/evm/uniswap-v4/index.js.map +1 -1
- package/package.json +6 -1
|
@@ -0,0 +1,1067 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var viem = require('viem');
|
|
4
|
+
var continuumNodeSdk = require('@continuumdao/continuum-node-sdk');
|
|
5
|
+
|
|
6
|
+
// src/core/registry.ts
|
|
7
|
+
var modules = [];
|
|
8
|
+
function registerProtocolModule(mod) {
|
|
9
|
+
const existing = modules.findIndex((m) => m.id === mod.id);
|
|
10
|
+
if (existing >= 0) {
|
|
11
|
+
modules[existing] = mod;
|
|
12
|
+
} else {
|
|
13
|
+
modules.push(mod);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/protocols/evm/circle-cctp/constants.ts
|
|
18
|
+
var CIRCLE_CCTP_PROTOCOL_ID = "circle-cctp";
|
|
19
|
+
var IRIS_API_MAINNET = "https://iris-api.circle.com";
|
|
20
|
+
var IRIS_API_SANDBOX = "https://iris-api-sandbox.circle.com";
|
|
21
|
+
var TOKEN_MESSENGER_V2_MAINNET = "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d";
|
|
22
|
+
var TOKEN_MESSENGER_V2_TESTNET = "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA";
|
|
23
|
+
var FORWARDING_SERVICE_HOOK_DATA = "0x636374702d666f72776172640000000000000000000000000000000000000000";
|
|
24
|
+
var CCTP_DEFAULT_MIN_FINALITY_THRESHOLD = 1e3;
|
|
25
|
+
var CCTP_USDC_DECIMALS = 6;
|
|
26
|
+
var CCTP_ERC20_APPROVE_FALLBACK_GAS = 100000n;
|
|
27
|
+
var CCTP_DEPOSIT_FOR_BURN_FALLBACK_GAS = 350000n;
|
|
28
|
+
var CCTP_EVM_CHAINS = [
|
|
29
|
+
{
|
|
30
|
+
chainId: 1,
|
|
31
|
+
domain: 0,
|
|
32
|
+
network: "mainnet",
|
|
33
|
+
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
34
|
+
tokenMessenger: TOKEN_MESSENGER_V2_MAINNET,
|
|
35
|
+
label: "Ethereum"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
chainId: 43114,
|
|
39
|
+
domain: 1,
|
|
40
|
+
network: "mainnet",
|
|
41
|
+
usdc: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
|
42
|
+
tokenMessenger: TOKEN_MESSENGER_V2_MAINNET,
|
|
43
|
+
label: "Avalanche"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
chainId: 10,
|
|
47
|
+
domain: 2,
|
|
48
|
+
network: "mainnet",
|
|
49
|
+
usdc: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
|
50
|
+
tokenMessenger: TOKEN_MESSENGER_V2_MAINNET,
|
|
51
|
+
label: "OP Mainnet"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
chainId: 42161,
|
|
55
|
+
domain: 3,
|
|
56
|
+
network: "mainnet",
|
|
57
|
+
usdc: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
58
|
+
tokenMessenger: TOKEN_MESSENGER_V2_MAINNET,
|
|
59
|
+
label: "Arbitrum"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
chainId: 8453,
|
|
63
|
+
domain: 6,
|
|
64
|
+
network: "mainnet",
|
|
65
|
+
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
66
|
+
tokenMessenger: TOKEN_MESSENGER_V2_MAINNET,
|
|
67
|
+
label: "Base"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
chainId: 137,
|
|
71
|
+
domain: 7,
|
|
72
|
+
network: "mainnet",
|
|
73
|
+
usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
74
|
+
tokenMessenger: TOKEN_MESSENGER_V2_MAINNET,
|
|
75
|
+
label: "Polygon PoS"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
chainId: 11155111,
|
|
79
|
+
domain: 0,
|
|
80
|
+
network: "testnet",
|
|
81
|
+
usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
82
|
+
tokenMessenger: TOKEN_MESSENGER_V2_TESTNET,
|
|
83
|
+
label: "Ethereum Sepolia"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
chainId: 43113,
|
|
87
|
+
domain: 1,
|
|
88
|
+
network: "testnet",
|
|
89
|
+
usdc: "0x5425890298aed601595a70AB815c96711a31Bc65",
|
|
90
|
+
tokenMessenger: TOKEN_MESSENGER_V2_TESTNET,
|
|
91
|
+
label: "Avalanche Fuji"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
chainId: 11155420,
|
|
95
|
+
domain: 2,
|
|
96
|
+
network: "testnet",
|
|
97
|
+
usdc: "0x5fd84259d66Cd46123540766Be93DFE6D43130D7",
|
|
98
|
+
tokenMessenger: TOKEN_MESSENGER_V2_TESTNET,
|
|
99
|
+
label: "OP Sepolia"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
chainId: 421614,
|
|
103
|
+
domain: 3,
|
|
104
|
+
network: "testnet",
|
|
105
|
+
usdc: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
|
|
106
|
+
tokenMessenger: TOKEN_MESSENGER_V2_TESTNET,
|
|
107
|
+
label: "Arbitrum Sepolia"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
chainId: 84532,
|
|
111
|
+
domain: 6,
|
|
112
|
+
network: "testnet",
|
|
113
|
+
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
114
|
+
tokenMessenger: TOKEN_MESSENGER_V2_TESTNET,
|
|
115
|
+
label: "Base Sepolia"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
chainId: 80002,
|
|
119
|
+
domain: 7,
|
|
120
|
+
network: "testnet",
|
|
121
|
+
usdc: "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582",
|
|
122
|
+
tokenMessenger: TOKEN_MESSENGER_V2_TESTNET,
|
|
123
|
+
label: "Polygon Amoy"
|
|
124
|
+
}
|
|
125
|
+
];
|
|
126
|
+
function cctpChainConfig(chainId) {
|
|
127
|
+
return CCTP_EVM_CHAINS.find((c) => c.chainId === chainId) ?? null;
|
|
128
|
+
}
|
|
129
|
+
function parseCctpEvmChainId(chainId) {
|
|
130
|
+
if (typeof chainId === "number") {
|
|
131
|
+
return Number.isFinite(chainId) ? chainId : Number.NaN;
|
|
132
|
+
}
|
|
133
|
+
const t = String(chainId).trim();
|
|
134
|
+
if (!t) return Number.NaN;
|
|
135
|
+
const low = t.toLowerCase();
|
|
136
|
+
if (low.startsWith("eip155:")) {
|
|
137
|
+
const n2 = Number.parseInt(t.slice("eip155:".length).trim(), 10);
|
|
138
|
+
return Number.isFinite(n2) ? n2 : Number.NaN;
|
|
139
|
+
}
|
|
140
|
+
if (low.startsWith("0x")) {
|
|
141
|
+
const n2 = Number.parseInt(t, 16);
|
|
142
|
+
return Number.isFinite(n2) ? n2 : Number.NaN;
|
|
143
|
+
}
|
|
144
|
+
const n = Number.parseInt(t, 10);
|
|
145
|
+
return Number.isFinite(n) ? n : Number.NaN;
|
|
146
|
+
}
|
|
147
|
+
function isCctpSourceChainSupported(chainId) {
|
|
148
|
+
const n = parseCctpEvmChainId(chainId);
|
|
149
|
+
return Number.isFinite(n) && cctpChainConfig(n) != null;
|
|
150
|
+
}
|
|
151
|
+
function cctpCanonicalUsdcAddress(chainId) {
|
|
152
|
+
const n = parseCctpEvmChainId(chainId);
|
|
153
|
+
if (!Number.isFinite(n)) return null;
|
|
154
|
+
const raw = cctpChainConfig(n)?.usdc;
|
|
155
|
+
if (!raw) return null;
|
|
156
|
+
try {
|
|
157
|
+
return viem.getAddress(raw);
|
|
158
|
+
} catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function isCctpCanonicalUsdcRow(chainId, contractAddress) {
|
|
163
|
+
const n = parseCctpEvmChainId(chainId);
|
|
164
|
+
if (!Number.isFinite(n)) return false;
|
|
165
|
+
const canonical = cctpCanonicalUsdcAddress(n);
|
|
166
|
+
if (!canonical) return false;
|
|
167
|
+
try {
|
|
168
|
+
return viem.getAddress(contractAddress).toLowerCase() === canonical.toLowerCase();
|
|
169
|
+
} catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function chainIdToDomain(chainId) {
|
|
174
|
+
return cctpChainConfig(chainId)?.domain ?? null;
|
|
175
|
+
}
|
|
176
|
+
function domainToChainId(domain, network) {
|
|
177
|
+
const row = CCTP_EVM_CHAINS.find((c) => c.domain === domain && c.network === network);
|
|
178
|
+
return row?.chainId ?? null;
|
|
179
|
+
}
|
|
180
|
+
function isCctpRouteSupported(sourceChainId, destChainId) {
|
|
181
|
+
const src = cctpChainConfig(sourceChainId);
|
|
182
|
+
const dst = cctpChainConfig(destChainId);
|
|
183
|
+
if (!src || !dst) return false;
|
|
184
|
+
if (src.network !== dst.network) return false;
|
|
185
|
+
if (src.chainId === dst.chainId) return false;
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
function irisApiBaseForChainId(chainId) {
|
|
189
|
+
const cfg = cctpChainConfig(chainId);
|
|
190
|
+
if (!cfg) throw new Error(`Chain ${chainId} is not a supported CCTP EVM chain.`);
|
|
191
|
+
return cfg.network === "mainnet" ? IRIS_API_MAINNET : IRIS_API_SANDBOX;
|
|
192
|
+
}
|
|
193
|
+
function cctpSupportedRoutes(network) {
|
|
194
|
+
const chains = network ? CCTP_EVM_CHAINS.filter((c) => c.network === network) : [...CCTP_EVM_CHAINS];
|
|
195
|
+
const out = [];
|
|
196
|
+
for (const src of chains) {
|
|
197
|
+
for (const dst of chains) {
|
|
198
|
+
if (src.chainId === dst.chainId) continue;
|
|
199
|
+
if (src.network !== dst.network) continue;
|
|
200
|
+
out.push({
|
|
201
|
+
sourceChainId: src.chainId,
|
|
202
|
+
destChainId: dst.chainId,
|
|
203
|
+
sourceDomain: src.domain,
|
|
204
|
+
destDomain: dst.domain,
|
|
205
|
+
sourceLabel: src.label,
|
|
206
|
+
destLabel: dst.label,
|
|
207
|
+
network: src.network
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return out;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/protocols/evm/circle-cctp/fees.ts
|
|
215
|
+
function pickForwardFeeTier(row, tier) {
|
|
216
|
+
const forwardFee = row.forwardFee;
|
|
217
|
+
if (forwardFee && typeof forwardFee === "object") {
|
|
218
|
+
const key = tier === "low" ? "low" : tier === "high" ? "high" : "med";
|
|
219
|
+
const v = forwardFee[key];
|
|
220
|
+
if (v != null) return BigInt(String(v));
|
|
221
|
+
}
|
|
222
|
+
const maxFee = row.maximumFee ?? row.maxFee ?? row.forwardFee;
|
|
223
|
+
if (maxFee != null) return BigInt(String(maxFee));
|
|
224
|
+
throw new Error("Iris fee response missing forwardFee tiers.");
|
|
225
|
+
}
|
|
226
|
+
async function fetchCctpBurnFees(args) {
|
|
227
|
+
const tier = args.feeTier ?? "med";
|
|
228
|
+
const base = irisApiBaseForChainId(args.sourceChainId);
|
|
229
|
+
const url = `${base}/v2/burn/USDC/fees/${args.sourceDomain}/${args.destDomain}?forward=true`;
|
|
230
|
+
const res = await fetch(url, { headers: { Accept: "application/json" } });
|
|
231
|
+
if (!res.ok) {
|
|
232
|
+
const text = await res.text().catch(() => "");
|
|
233
|
+
throw new Error(`Iris fee quote failed (${res.status}): ${text.slice(0, 200)}`);
|
|
234
|
+
}
|
|
235
|
+
const json = await res.json();
|
|
236
|
+
const rows = Array.isArray(json) ? json : Array.isArray(json.data) ? json.data : [json];
|
|
237
|
+
const row = rows[0] ?? {};
|
|
238
|
+
const low = pickForwardFeeTier(row, "low");
|
|
239
|
+
const med = pickForwardFeeTier(row, "med");
|
|
240
|
+
const high = pickForwardFeeTier(row, "high");
|
|
241
|
+
const maxFee = tier === "low" ? low : tier === "high" ? high : med;
|
|
242
|
+
return {
|
|
243
|
+
sourceDomain: args.sourceDomain,
|
|
244
|
+
destDomain: args.destDomain,
|
|
245
|
+
maxFee,
|
|
246
|
+
forwardFeeLow: low,
|
|
247
|
+
forwardFeeMed: med,
|
|
248
|
+
forwardFeeHigh: high,
|
|
249
|
+
feeTierUsed: tier
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function formatCctpUsdcHuman(wei) {
|
|
253
|
+
const raw = viem.formatUnits(wei, CCTP_USDC_DECIMALS);
|
|
254
|
+
if (!raw.includes(".")) return raw;
|
|
255
|
+
return raw.replace(/\.?0+$/, "") || "0";
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// src/core/purpose.ts
|
|
259
|
+
function mergePurposeText(purposeText, purposeSuffix) {
|
|
260
|
+
const t = (purposeText ?? "").trim();
|
|
261
|
+
const suffix = (purposeSuffix ?? "").trim();
|
|
262
|
+
if (!suffix) return t;
|
|
263
|
+
return t ? `${t}
|
|
264
|
+
|
|
265
|
+
${suffix}` : suffix;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// src/core/envelope.ts
|
|
269
|
+
function finalizeMultisign(input) {
|
|
270
|
+
const { keyGen, destinationChainID, legs } = input;
|
|
271
|
+
if (legs.length === 0) {
|
|
272
|
+
throw new Error("finalizeMultisign requires at least one leg");
|
|
273
|
+
}
|
|
274
|
+
const ph = (keyGen.pubkeyhex ?? "").trim();
|
|
275
|
+
if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
|
|
276
|
+
const keyList = keyGen.keylist ?? [];
|
|
277
|
+
const clientId = continuumNodeSdk.getClientIdFromKeyGenResult(keyGen);
|
|
278
|
+
const first = legs[0];
|
|
279
|
+
const messageHashes = legs.map((l) => l.msgHash);
|
|
280
|
+
const messageRawBatch = legs.map((l) => l.msgRaw);
|
|
281
|
+
const batchMeta = legs.map((l) => ({
|
|
282
|
+
destinationAddress: l.destinationAddress,
|
|
283
|
+
signatureText: l.signatureText,
|
|
284
|
+
...l.audit
|
|
285
|
+
}));
|
|
286
|
+
const proposalTxParams = legs.map((l) => l.proposalTxParams).filter((p) => p != null && typeof p === "object");
|
|
287
|
+
const extraPayload = {
|
|
288
|
+
batchMeta,
|
|
289
|
+
...input.extraJSON ?? {}
|
|
290
|
+
};
|
|
291
|
+
const extraJSON = JSON.stringify(extraPayload);
|
|
292
|
+
const bodyForSign = {
|
|
293
|
+
keyList,
|
|
294
|
+
pubKey: ph,
|
|
295
|
+
msgHash: messageHashes[0],
|
|
296
|
+
msgRaw: first.msgRaw,
|
|
297
|
+
destinationChainID,
|
|
298
|
+
destinationAddress: input.destinationAddress ?? first.destinationAddress,
|
|
299
|
+
extraJSON,
|
|
300
|
+
signatureText: first.signatureText,
|
|
301
|
+
purpose: mergePurposeText(input.purposeText, input.purposeSuffix),
|
|
302
|
+
...first.feeSnapshot
|
|
303
|
+
};
|
|
304
|
+
if (legs.length > 1) {
|
|
305
|
+
bodyForSign.messageHashes = messageHashes;
|
|
306
|
+
bodyForSign.messageRawBatch = messageRawBatch;
|
|
307
|
+
}
|
|
308
|
+
if (proposalTxParams.length > 0) {
|
|
309
|
+
bodyForSign.proposalTxParams = proposalTxParams;
|
|
310
|
+
}
|
|
311
|
+
const valueWei = first.valueWei;
|
|
312
|
+
if (valueWei != null && valueWei > 0n) {
|
|
313
|
+
bodyForSign.value = valueWei.toString();
|
|
314
|
+
}
|
|
315
|
+
if (clientId) bodyForSign.clientId = clientId;
|
|
316
|
+
return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
|
|
317
|
+
}
|
|
318
|
+
function routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimit) {
|
|
319
|
+
if (chainGasLimit != null && Number.isFinite(chainGasLimit) && chainGasLimit > 0) {
|
|
320
|
+
return continuumNodeSdk.gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit);
|
|
321
|
+
}
|
|
322
|
+
return (estimatedGas * 12n + 9n) / 10n;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// src/chains/evm/buildBatch.ts
|
|
326
|
+
async function buildEvmMultisignBatch(args) {
|
|
327
|
+
const { context, steps } = args;
|
|
328
|
+
const {
|
|
329
|
+
chainId,
|
|
330
|
+
rpcUrl,
|
|
331
|
+
executorAddress,
|
|
332
|
+
chainDetail,
|
|
333
|
+
useCustomGas,
|
|
334
|
+
customGasChainDetails,
|
|
335
|
+
keyGen,
|
|
336
|
+
purposeText
|
|
337
|
+
} = context;
|
|
338
|
+
if (steps.length === 0) throw new Error("buildEvmMultisignBatch requires at least one step");
|
|
339
|
+
const ch = viem.defineChain({
|
|
340
|
+
id: chainId,
|
|
341
|
+
name: "Destination",
|
|
342
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
343
|
+
rpcUrls: { default: { http: [rpcUrl] } }
|
|
344
|
+
});
|
|
345
|
+
const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(rpcUrl) });
|
|
346
|
+
const feeParams = await continuumNodeSdk.fetchChainFeeParams(rpcUrl, chainId);
|
|
347
|
+
const legacy = Boolean(chainDetail?.legacy) || !feeParams.isEip1559;
|
|
348
|
+
const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
|
|
349
|
+
const gasLimitConfig = useCustomGas && chainDetail?.gasLimit != null ? Number(chainDetail.gasLimit) : void 0;
|
|
350
|
+
const chainGasLimitRouter = chainDetail?.gasLimit != null && Number.isFinite(Number(chainDetail.gasLimit)) && Number(chainDetail.gasLimit) > 0 ? Number(chainDetail.gasLimit) : void 0;
|
|
351
|
+
const gasFeeMultiplier = useCustomGas && chainDetail?.gasMultiplier != null ? Number(chainDetail.gasMultiplier) : void 0;
|
|
352
|
+
const executor = viem.getAddress(executorAddress);
|
|
353
|
+
const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
|
|
354
|
+
const legs = [];
|
|
355
|
+
for (let i = 0; i < steps.length; i++) {
|
|
356
|
+
const step = steps[i];
|
|
357
|
+
const currentNonce = baseNonce + i;
|
|
358
|
+
let estimatedGas;
|
|
359
|
+
if (args.estimateGasForStep) {
|
|
360
|
+
estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient, executor });
|
|
361
|
+
} else {
|
|
362
|
+
try {
|
|
363
|
+
estimatedGas = await publicClient.estimateGas({
|
|
364
|
+
to: step.to,
|
|
365
|
+
data: step.data,
|
|
366
|
+
value: step.value,
|
|
367
|
+
account: executor
|
|
368
|
+
});
|
|
369
|
+
} catch {
|
|
370
|
+
estimatedGas = step.fallbackGas ?? 100000n;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
let gasLimitI;
|
|
374
|
+
if (args.resolveGasLimit) {
|
|
375
|
+
gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient });
|
|
376
|
+
} else if (step.routerSwap) {
|
|
377
|
+
gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
|
|
378
|
+
} else {
|
|
379
|
+
gasLimitI = useCustomGas ? continuumNodeSdk.gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
|
|
380
|
+
}
|
|
381
|
+
let proposalTxParams;
|
|
382
|
+
let feeSnapshot;
|
|
383
|
+
let serialized;
|
|
384
|
+
if (legacy) {
|
|
385
|
+
let gasPriceWei = await publicClient.getGasPrice();
|
|
386
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
387
|
+
gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
388
|
+
}
|
|
389
|
+
if (useCustomGas && chainDetail?.gasPrice != null && chainDetail.gasPrice > 0) {
|
|
390
|
+
const configured = viem.parseGwei(continuumNodeSdk.gweiToDecimalString(Number(chainDetail.gasPrice)));
|
|
391
|
+
if (configured > gasPriceWei) gasPriceWei = configured;
|
|
392
|
+
}
|
|
393
|
+
serialized = viem.serializeTransaction({
|
|
394
|
+
type: "legacy",
|
|
395
|
+
to: step.to,
|
|
396
|
+
data: step.data,
|
|
397
|
+
value: step.value,
|
|
398
|
+
gas: gasLimitI,
|
|
399
|
+
gasPrice: gasPriceWei,
|
|
400
|
+
nonce: currentNonce,
|
|
401
|
+
chainId
|
|
402
|
+
});
|
|
403
|
+
proposalTxParams = {
|
|
404
|
+
nonce: currentNonce,
|
|
405
|
+
gasLimit: gasLimitI.toString(),
|
|
406
|
+
txType: "legacy",
|
|
407
|
+
gasPrice: gasPriceWei.toString()
|
|
408
|
+
};
|
|
409
|
+
feeSnapshot = continuumNodeSdk.proposalTxParamsToFeeSnapshot(proposalTxParams);
|
|
410
|
+
} else {
|
|
411
|
+
const fetchedBase = feeParams.baseFeeGwei ?? 0;
|
|
412
|
+
const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
|
|
413
|
+
const configuredBase = useCustomGas && chainDetail?.baseFee != null ? Number(chainDetail.baseFee) : 0;
|
|
414
|
+
const configuredPriority = useCustomGas && chainDetail?.priorityFee != null ? Number(chainDetail.priorityFee) : 0;
|
|
415
|
+
const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
|
|
416
|
+
const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
|
|
417
|
+
const baseFeeMultiplierPct = useCustomGas && chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(chainDetail.baseFeeMultiplier)) : 100;
|
|
418
|
+
const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
|
|
419
|
+
const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
|
|
420
|
+
let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(continuumNodeSdk.gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
|
|
421
|
+
let maxFeePerGas = viem.parseGwei(continuumNodeSdk.gweiToDecimalString(maxFeePerGasGwei));
|
|
422
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
423
|
+
maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
424
|
+
maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
425
|
+
}
|
|
426
|
+
({ maxFeePerGas, maxPriorityFeePerGas } = continuumNodeSdk.alignEip1559FeesWithLatestBase(
|
|
427
|
+
maxFeePerGas,
|
|
428
|
+
maxPriorityFeePerGas,
|
|
429
|
+
latestBaseFeeWei
|
|
430
|
+
));
|
|
431
|
+
serialized = viem.serializeTransaction({
|
|
432
|
+
type: "eip1559",
|
|
433
|
+
to: step.to,
|
|
434
|
+
data: step.data,
|
|
435
|
+
value: step.value,
|
|
436
|
+
gas: gasLimitI,
|
|
437
|
+
maxFeePerGas,
|
|
438
|
+
maxPriorityFeePerGas,
|
|
439
|
+
nonce: currentNonce,
|
|
440
|
+
chainId
|
|
441
|
+
});
|
|
442
|
+
proposalTxParams = {
|
|
443
|
+
nonce: currentNonce,
|
|
444
|
+
gasLimit: gasLimitI.toString(),
|
|
445
|
+
txType: "eip1559",
|
|
446
|
+
maxFeePerGas: maxFeePerGas.toString(),
|
|
447
|
+
maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
448
|
+
};
|
|
449
|
+
feeSnapshot = i === 0 ? continuumNodeSdk.proposalTxParamsToFeeSnapshot(proposalTxParams) : {};
|
|
450
|
+
}
|
|
451
|
+
const h = viem.keccak256(serialized);
|
|
452
|
+
const msgHash = h.startsWith("0x") ? h.slice(2) : h;
|
|
453
|
+
const batchMetaExtra = args.buildBatchMeta({ step, index: i, gasLimit: gasLimitI });
|
|
454
|
+
legs.push({
|
|
455
|
+
msgHash,
|
|
456
|
+
msgRaw: i === 0 && args.firstMsgRawNo0x != null ? args.firstMsgRawNo0x : serialized,
|
|
457
|
+
destinationAddress: step.to,
|
|
458
|
+
signatureText: typeof batchMetaExtra.signatureText === "string" ? batchMetaExtra.signatureText : JSON.stringify(batchMetaExtra.signatureText ?? {}),
|
|
459
|
+
audit: batchMetaExtra,
|
|
460
|
+
feeSnapshot: i === 0 ? feeSnapshot : {},
|
|
461
|
+
proposalTxParams,
|
|
462
|
+
valueWei: i === 0 ? step.value : void 0
|
|
463
|
+
});
|
|
464
|
+
if (i === 0 && args.firstMsgRawNo0x != null) {
|
|
465
|
+
legs[0].msgRaw = args.firstMsgRawNo0x;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
const extraJSON = {};
|
|
469
|
+
if (useCustomGas && customGasChainDetails && Object.keys(customGasChainDetails).length > 0) {
|
|
470
|
+
extraJSON.customGasChainDetails = customGasChainDetails;
|
|
471
|
+
}
|
|
472
|
+
const result = finalizeMultisign({
|
|
473
|
+
keyGen,
|
|
474
|
+
purposeText,
|
|
475
|
+
purposeSuffix: args.purposeSuffix,
|
|
476
|
+
destinationChainID: String(chainId),
|
|
477
|
+
destinationAddress: args.destinationAddress ?? steps[0].to,
|
|
478
|
+
legs,
|
|
479
|
+
extraJSON: Object.keys(extraJSON).length > 0 ? extraJSON : void 0
|
|
480
|
+
});
|
|
481
|
+
const pv = args.payableValueWei;
|
|
482
|
+
if (pv != null && pv > 0n) {
|
|
483
|
+
result.bodyForSign.value = pv.toString();
|
|
484
|
+
}
|
|
485
|
+
return result;
|
|
486
|
+
}
|
|
487
|
+
var erc20BalanceAbi = viem.parseAbi(["function balanceOf(address account) view returns (uint256)"]);
|
|
488
|
+
async function cctpAssertBurnPreconditions(args) {
|
|
489
|
+
if (!isCctpRouteSupported(args.sourceChainId, args.destChainId)) {
|
|
490
|
+
throw new Error("CCTP route is not supported for the selected source and destination chains.");
|
|
491
|
+
}
|
|
492
|
+
const src = cctpChainConfig(args.sourceChainId);
|
|
493
|
+
if (!src) throw new Error("Unsupported CCTP source chain.");
|
|
494
|
+
const transferAmount = viem.parseUnits(args.transferAmountHuman.trim(), CCTP_USDC_DECIMALS);
|
|
495
|
+
if (transferAmount <= 0n) throw new Error("Transfer amount must be greater than zero.");
|
|
496
|
+
const totalBurn = transferAmount + args.maxFee;
|
|
497
|
+
const ch = viem.defineChain({
|
|
498
|
+
id: args.sourceChainId,
|
|
499
|
+
name: src.label,
|
|
500
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
501
|
+
rpcUrls: { default: { http: [args.rpcUrl.trim()] } }
|
|
502
|
+
});
|
|
503
|
+
const client = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl.trim()) });
|
|
504
|
+
const owner = viem.getAddress(args.executorAddress);
|
|
505
|
+
const balance = await client.readContract({
|
|
506
|
+
address: viem.getAddress(src.usdc),
|
|
507
|
+
abi: erc20BalanceAbi,
|
|
508
|
+
functionName: "balanceOf",
|
|
509
|
+
args: [owner]
|
|
510
|
+
});
|
|
511
|
+
if (balance < totalBurn) {
|
|
512
|
+
throw new Error(
|
|
513
|
+
`Insufficient USDC on source chain: need ${totalBurn.toString()} (transfer + maxFee), have ${balance.toString()}.`
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
return { transferAmount, totalBurn };
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// src/protocols/evm/circle-cctp/multisign.ts
|
|
520
|
+
var erc20ApproveAbi = viem.parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
|
|
521
|
+
var tokenMessengerAbi = viem.parseAbi([
|
|
522
|
+
"function depositForBurnWithHook(uint256 amount, uint32 destinationDomain, bytes32 mintRecipient, address burnToken, bytes32 destinationCaller, uint256 maxFee, uint32 minFinalityThreshold, bytes hookData)"
|
|
523
|
+
]);
|
|
524
|
+
function buildCctpBurnExtraJSON(args) {
|
|
525
|
+
return {
|
|
526
|
+
version: 1,
|
|
527
|
+
sourceChainId: args.sourceChainId,
|
|
528
|
+
sourceDomain: args.sourceDomain,
|
|
529
|
+
destDomain: args.destDomain,
|
|
530
|
+
destChainId: args.destChainId,
|
|
531
|
+
transferAmount: args.transferAmount.toString(),
|
|
532
|
+
mintRecipient: viem.pad(args.mintRecipient, { size: 32 }),
|
|
533
|
+
burnToken: args.burnToken,
|
|
534
|
+
tokenMessenger: args.tokenMessenger,
|
|
535
|
+
useForwardingService: true,
|
|
536
|
+
forwardingHookData: FORWARDING_SERVICE_HOOK_DATA,
|
|
537
|
+
minFinalityThreshold: args.minFinalityThreshold,
|
|
538
|
+
proposalMaxFee: args.proposalMaxFee.toString(),
|
|
539
|
+
feeTier: args.feeTier
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
function buildCctpBurnSteps(args) {
|
|
543
|
+
const src = cctpChainConfig(args.sourceChainId);
|
|
544
|
+
const dst = cctpChainConfig(args.destChainId);
|
|
545
|
+
if (!src || !dst) throw new Error("Unsupported CCTP source or destination chain.");
|
|
546
|
+
const usdc = viem.getAddress(src.usdc);
|
|
547
|
+
const tokenMessenger = viem.getAddress(src.tokenMessenger);
|
|
548
|
+
const totalBurn = args.transferAmount + args.maxFee;
|
|
549
|
+
const mintRecipientBytes32 = viem.pad(args.mintRecipient, { size: 32 });
|
|
550
|
+
const destinationCaller = viem.pad("0x", { size: 32 });
|
|
551
|
+
const steps = [];
|
|
552
|
+
const allowance = args.currentAllowance ?? 0n;
|
|
553
|
+
if (allowance < totalBurn) {
|
|
554
|
+
if (allowance > 0n) {
|
|
555
|
+
steps.push({
|
|
556
|
+
kind: "approve",
|
|
557
|
+
to: usdc,
|
|
558
|
+
data: viem.encodeFunctionData({ abi: erc20ApproveAbi, functionName: "approve", args: [tokenMessenger, 0n] }),
|
|
559
|
+
value: 0n,
|
|
560
|
+
fallbackGas: CCTP_ERC20_APPROVE_FALLBACK_GAS
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
steps.push({
|
|
564
|
+
kind: "approve",
|
|
565
|
+
to: usdc,
|
|
566
|
+
data: viem.encodeFunctionData({
|
|
567
|
+
abi: erc20ApproveAbi,
|
|
568
|
+
functionName: "approve",
|
|
569
|
+
args: [tokenMessenger, totalBurn]
|
|
570
|
+
}),
|
|
571
|
+
value: 0n,
|
|
572
|
+
fallbackGas: CCTP_ERC20_APPROVE_FALLBACK_GAS
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
steps.push({
|
|
576
|
+
kind: "depositForBurnWithHook",
|
|
577
|
+
to: tokenMessenger,
|
|
578
|
+
data: viem.encodeFunctionData({
|
|
579
|
+
abi: tokenMessengerAbi,
|
|
580
|
+
functionName: "depositForBurnWithHook",
|
|
581
|
+
args: [
|
|
582
|
+
totalBurn,
|
|
583
|
+
dst.domain,
|
|
584
|
+
mintRecipientBytes32,
|
|
585
|
+
usdc,
|
|
586
|
+
destinationCaller,
|
|
587
|
+
args.maxFee,
|
|
588
|
+
args.minFinalityThreshold,
|
|
589
|
+
FORWARDING_SERVICE_HOOK_DATA
|
|
590
|
+
]
|
|
591
|
+
}),
|
|
592
|
+
value: 0n,
|
|
593
|
+
fallbackGas: CCTP_DEPOSIT_FOR_BURN_FALLBACK_GAS
|
|
594
|
+
});
|
|
595
|
+
return steps;
|
|
596
|
+
}
|
|
597
|
+
async function buildEvmMultisignBodyCctpBurnBatch(args) {
|
|
598
|
+
const src = cctpChainConfig(args.sourceChainId);
|
|
599
|
+
const dst = cctpChainConfig(args.destChainId);
|
|
600
|
+
if (!src || !dst) throw new Error("Unsupported CCTP source or destination chain.");
|
|
601
|
+
const executor = viem.getAddress(args.executorAddress);
|
|
602
|
+
const mintRecipient = viem.getAddress(args.mintRecipient ?? executor);
|
|
603
|
+
const minFinality = args.minFinalityThreshold ?? CCTP_DEFAULT_MIN_FINALITY_THRESHOLD;
|
|
604
|
+
let maxFee = args.maxFee;
|
|
605
|
+
let proposalMaxFee = args.proposalMaxFee ?? maxFee;
|
|
606
|
+
if (maxFee == null) {
|
|
607
|
+
const fees = await fetchCctpBurnFees({
|
|
608
|
+
sourceChainId: args.sourceChainId,
|
|
609
|
+
sourceDomain: src.domain,
|
|
610
|
+
destDomain: dst.domain,
|
|
611
|
+
feeTier: args.feeTier
|
|
612
|
+
});
|
|
613
|
+
maxFee = fees.maxFee;
|
|
614
|
+
proposalMaxFee = fees.maxFee;
|
|
615
|
+
} else if (proposalMaxFee == null) {
|
|
616
|
+
proposalMaxFee = maxFee;
|
|
617
|
+
}
|
|
618
|
+
const { transferAmount } = await cctpAssertBurnPreconditions({
|
|
619
|
+
sourceChainId: args.sourceChainId,
|
|
620
|
+
destChainId: args.destChainId,
|
|
621
|
+
transferAmountHuman: args.transferAmountHuman,
|
|
622
|
+
maxFee,
|
|
623
|
+
rpcUrl: args.rpcUrl,
|
|
624
|
+
executorAddress: executor
|
|
625
|
+
});
|
|
626
|
+
const steps = buildCctpBurnSteps({
|
|
627
|
+
sourceChainId: args.sourceChainId,
|
|
628
|
+
destChainId: args.destChainId,
|
|
629
|
+
mintRecipient,
|
|
630
|
+
transferAmount,
|
|
631
|
+
maxFee,
|
|
632
|
+
minFinalityThreshold: minFinality,
|
|
633
|
+
currentAllowance: 0n
|
|
634
|
+
});
|
|
635
|
+
const cctpBurn = buildCctpBurnExtraJSON({
|
|
636
|
+
sourceChainId: args.sourceChainId,
|
|
637
|
+
sourceDomain: src.domain,
|
|
638
|
+
destDomain: dst.domain,
|
|
639
|
+
destChainId: dst.chainId,
|
|
640
|
+
transferAmount,
|
|
641
|
+
mintRecipient,
|
|
642
|
+
burnToken: viem.getAddress(src.usdc),
|
|
643
|
+
tokenMessenger: viem.getAddress(src.tokenMessenger),
|
|
644
|
+
minFinalityThreshold: minFinality,
|
|
645
|
+
proposalMaxFee,
|
|
646
|
+
feeTier: args.feeTier ?? "med"
|
|
647
|
+
});
|
|
648
|
+
const transferHuman = args.transferAmountHuman.trim();
|
|
649
|
+
const totalBurn = transferAmount + maxFee;
|
|
650
|
+
const purposeSuffix = `CCTP (Forwarding Service): ${transferHuman} USDC ${src.label} \u2192 ${dst.label}.`;
|
|
651
|
+
const firstDataNo0x = steps[0].data.startsWith("0x") ? steps[0].data.slice(2) : steps[0].data;
|
|
652
|
+
const result = await buildEvmMultisignBatch({
|
|
653
|
+
context: {
|
|
654
|
+
chainCategory: "evm",
|
|
655
|
+
keyGen: args.keyGen,
|
|
656
|
+
purposeText: args.purposeText,
|
|
657
|
+
chainId: args.sourceChainId,
|
|
658
|
+
rpcUrl: args.rpcUrl,
|
|
659
|
+
executorAddress: executor,
|
|
660
|
+
chainDetail: args.chainDetail,
|
|
661
|
+
useCustomGas: args.useCustomGas,
|
|
662
|
+
customGasChainDetails: args.customGasChainDetails
|
|
663
|
+
},
|
|
664
|
+
steps,
|
|
665
|
+
purposeSuffix,
|
|
666
|
+
firstMsgRawNo0x: firstDataNo0x,
|
|
667
|
+
destinationAddress: steps[0].to,
|
|
668
|
+
buildBatchMeta: ({ step }) => {
|
|
669
|
+
const s = step;
|
|
670
|
+
if (s.kind === "approve") {
|
|
671
|
+
return {
|
|
672
|
+
signatureText: JSON.stringify({
|
|
673
|
+
kind: "CircleCCTP",
|
|
674
|
+
name: "USDC.approve",
|
|
675
|
+
spender: src.tokenMessenger,
|
|
676
|
+
amount: totalBurn.toString()
|
|
677
|
+
}),
|
|
678
|
+
evm: { type: "cctp_erc20_approve", version: 1, chainId: String(args.sourceChainId) }
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
return {
|
|
682
|
+
signatureText: JSON.stringify({
|
|
683
|
+
kind: "CircleCCTP",
|
|
684
|
+
name: "TokenMessengerV2.depositForBurnWithHook",
|
|
685
|
+
destDomain: dst.domain,
|
|
686
|
+
destChainId: dst.chainId,
|
|
687
|
+
mintRecipient,
|
|
688
|
+
transferAmount: transferAmount.toString(),
|
|
689
|
+
maxFee: maxFee.toString()
|
|
690
|
+
}),
|
|
691
|
+
evm: { type: "cctp_deposit_for_burn_forward", version: 1, chainId: String(args.sourceChainId) }
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
const body = result.bodyForSign;
|
|
696
|
+
const existingExtra = typeof body.extraJSON === "string" ? JSON.parse(body.extraJSON) : body.extraJSON ?? {};
|
|
697
|
+
body.extraJSON = JSON.stringify({ ...existingExtra, cctpBurn });
|
|
698
|
+
return result;
|
|
699
|
+
}
|
|
700
|
+
async function buildEvmMultisignBodyCctpBurnBatchFromMcp(args) {
|
|
701
|
+
let maxFee;
|
|
702
|
+
let proposalMaxFee;
|
|
703
|
+
if (args.feeSnapshot?.maxFee != null) {
|
|
704
|
+
maxFee = BigInt(String(args.feeSnapshot.maxFee));
|
|
705
|
+
proposalMaxFee = maxFee;
|
|
706
|
+
}
|
|
707
|
+
return buildEvmMultisignBodyCctpBurnBatch({
|
|
708
|
+
keyGen: args.keyGen,
|
|
709
|
+
sourceChainId: args.chainId,
|
|
710
|
+
destChainId: args.destChainId,
|
|
711
|
+
rpcUrl: args.rpcUrl,
|
|
712
|
+
chainDetail: args.chainDetail,
|
|
713
|
+
useCustomGas: args.useCustomGas,
|
|
714
|
+
customGasChainDetails: args.customGasChainDetails,
|
|
715
|
+
transferAmountHuman: args.transferAmountHuman,
|
|
716
|
+
mintRecipient: args.mintRecipient ? viem.getAddress(args.mintRecipient) : void 0,
|
|
717
|
+
executorAddress: args.executorAddress,
|
|
718
|
+
purposeText: args.purposeText,
|
|
719
|
+
maxFee,
|
|
720
|
+
proposalMaxFee,
|
|
721
|
+
feeTier: args.feeTier,
|
|
722
|
+
minFinalityThreshold: args.minFinalityThreshold
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// src/protocols/evm/circle-cctp/executeHelpers.ts
|
|
727
|
+
function parseExtraJsonObject(detail) {
|
|
728
|
+
if (!detail) return null;
|
|
729
|
+
const raw = detail.ExtraJSON ?? detail.extraJSON;
|
|
730
|
+
if (raw == null) return null;
|
|
731
|
+
if (typeof raw === "object" && !Array.isArray(raw)) return raw;
|
|
732
|
+
if (typeof raw === "string" && raw.trim()) {
|
|
733
|
+
try {
|
|
734
|
+
const p = JSON.parse(raw);
|
|
735
|
+
if (p && typeof p === "object" && !Array.isArray(p)) return p;
|
|
736
|
+
} catch {
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
function parseCctpBurnExtraJSON(detail) {
|
|
743
|
+
const ex = parseExtraJsonObject(detail);
|
|
744
|
+
const raw = ex?.cctpBurn;
|
|
745
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
746
|
+
const c = raw;
|
|
747
|
+
if (c.useForwardingService !== true) return null;
|
|
748
|
+
const version = Number(c.version ?? 0);
|
|
749
|
+
if (version !== 1) return null;
|
|
750
|
+
return {
|
|
751
|
+
version: 1,
|
|
752
|
+
sourceChainId: Number(c.sourceChainId ?? 0),
|
|
753
|
+
sourceDomain: Number(c.sourceDomain),
|
|
754
|
+
destDomain: Number(c.destDomain),
|
|
755
|
+
destChainId: Number(c.destChainId),
|
|
756
|
+
transferAmount: String(c.transferAmount ?? ""),
|
|
757
|
+
mintRecipient: String(c.mintRecipient ?? ""),
|
|
758
|
+
burnToken: String(c.burnToken ?? ""),
|
|
759
|
+
tokenMessenger: String(c.tokenMessenger ?? ""),
|
|
760
|
+
useForwardingService: true,
|
|
761
|
+
forwardingHookData: String(c.forwardingHookData ?? FORWARDING_SERVICE_HOOK_DATA),
|
|
762
|
+
minFinalityThreshold: Number(c.minFinalityThreshold ?? 1e3),
|
|
763
|
+
proposalMaxFee: String(c.proposalMaxFee ?? "0"),
|
|
764
|
+
feeTier: c.feeTier === "low" || c.feeTier === "high" || c.feeTier === "med" ? c.feeTier : void 0
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
function isCctpBurnEvmSignRequest(detail, batchIndex) {
|
|
768
|
+
if (parseCctpBurnExtraJSON(detail)) return true;
|
|
769
|
+
const ex = parseExtraJsonObject(detail);
|
|
770
|
+
if (batchIndex != null && batchIndex >= 0 && ex) {
|
|
771
|
+
const bm = ex.batchMeta;
|
|
772
|
+
if (Array.isArray(bm) && bm[batchIndex] && typeof bm[batchIndex] === "object" && !Array.isArray(bm[batchIndex])) {
|
|
773
|
+
const evm0 = bm[batchIndex].evm;
|
|
774
|
+
if (evm0 && typeof evm0 === "object" && !Array.isArray(evm0)) {
|
|
775
|
+
const t2 = String(evm0.type ?? "");
|
|
776
|
+
return t2 === "cctp_deposit_for_burn_forward" || t2 === "cctp_erc20_approve";
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
const evm = ex?.evm;
|
|
781
|
+
if (!evm || typeof evm !== "object" || Array.isArray(evm)) return false;
|
|
782
|
+
const t = String(evm.type ?? "");
|
|
783
|
+
return t === "cctp_deposit_for_burn_forward";
|
|
784
|
+
}
|
|
785
|
+
function isCctpErc20ApproveEvmSignRequest(detail, batchIndex) {
|
|
786
|
+
const ex = parseExtraJsonObject(detail);
|
|
787
|
+
const index = batchIndex != null && batchIndex >= 0 ? batchIndex : 0;
|
|
788
|
+
const bm = ex?.batchMeta;
|
|
789
|
+
if (Array.isArray(bm) && bm[index] && typeof bm[index] === "object" && !Array.isArray(bm[index])) {
|
|
790
|
+
const evm0 = bm[index].evm;
|
|
791
|
+
if (evm0 && typeof evm0 === "object" && !Array.isArray(evm0)) {
|
|
792
|
+
return String(evm0.type ?? "") === "cctp_erc20_approve";
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
function resolveCctpBatchStepGasFromSignRequest(detail, batchIndex) {
|
|
798
|
+
const index = batchIndex != null && batchIndex >= 0 ? batchIndex : 0;
|
|
799
|
+
if (isCctpErc20ApproveEvmSignRequest(detail, index)) return CCTP_ERC20_APPROVE_FALLBACK_GAS;
|
|
800
|
+
if (isCctpBurnEvmSignRequest(detail, index)) return CCTP_DEPOSIT_FOR_BURN_FALLBACK_GAS;
|
|
801
|
+
return null;
|
|
802
|
+
}
|
|
803
|
+
function rebuildCctpBurnCalldataAtGetSig(args) {
|
|
804
|
+
const transferAmount = BigInt(args.cctpBurn.transferAmount);
|
|
805
|
+
const mintRecipient = cctpMintRecipientAddressFromExtra(args.cctpBurn);
|
|
806
|
+
const steps = buildCctpBurnSteps({
|
|
807
|
+
sourceChainId: args.cctpBurn.sourceChainId,
|
|
808
|
+
destChainId: args.cctpBurn.destChainId,
|
|
809
|
+
executorAddress: args.executorAddress,
|
|
810
|
+
mintRecipient,
|
|
811
|
+
transferAmount,
|
|
812
|
+
maxFee: args.freshMaxFee,
|
|
813
|
+
minFinalityThreshold: args.cctpBurn.minFinalityThreshold,
|
|
814
|
+
currentAllowance: args.forceApprove ? 0n : void 0
|
|
815
|
+
});
|
|
816
|
+
return steps.map((s) => ({ to: s.to, data: s.data }));
|
|
817
|
+
}
|
|
818
|
+
function cctpMintRecipientAddressFromExtra(cctpBurn) {
|
|
819
|
+
const hex = cctpBurn.mintRecipient.startsWith("0x") ? cctpBurn.mintRecipient.slice(2) : cctpBurn.mintRecipient;
|
|
820
|
+
return viem.getAddress(`0x${hex.slice(-40)}`);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// src/protocols/evm/circle-cctp/reads.ts
|
|
824
|
+
var erc20BalanceAbi2 = viem.parseAbi(["function balanceOf(address account) view returns (uint256)"]);
|
|
825
|
+
async function cctpFetchSupportedRoutes(args) {
|
|
826
|
+
return { routes: cctpSupportedRoutes(args?.network) };
|
|
827
|
+
}
|
|
828
|
+
function buildCctpMcpFeeFields(args) {
|
|
829
|
+
const { fees, transferAmount, transferAmountHuman } = args;
|
|
830
|
+
const maxFeeHuman = formatCctpUsdcHuman(fees.maxFee);
|
|
831
|
+
const forwardFeeLowHuman = formatCctpUsdcHuman(fees.forwardFeeLow);
|
|
832
|
+
const forwardFeeMedHuman = formatCctpUsdcHuman(fees.forwardFeeMed);
|
|
833
|
+
const forwardFeeHighHuman = formatCctpUsdcHuman(fees.forwardFeeHigh);
|
|
834
|
+
let totalBurn;
|
|
835
|
+
let totalBurnHuman;
|
|
836
|
+
if (transferAmount != null) {
|
|
837
|
+
totalBurn = transferAmount + fees.maxFee;
|
|
838
|
+
totalBurnHuman = formatCctpUsdcHuman(totalBurn);
|
|
839
|
+
}
|
|
840
|
+
const recipientPart = transferAmountHuman != null ? ` Recipient receives ${transferAmountHuman} USDC; wallet burns ${totalBurnHuman} USDC total (transfer + max fee).` : "";
|
|
841
|
+
const feeSummary = `Forwarding Service max fee (${fees.feeTierUsed} tier): ${maxFeeHuman} USDC.${recipientPart} Fee tiers \u2014 low: ${forwardFeeLowHuman}, med: ${forwardFeeMedHuman}, high: ${forwardFeeHighHuman} USDC. Fees refresh again at Get Sig.`;
|
|
842
|
+
return {
|
|
843
|
+
feeTierUsed: fees.feeTierUsed,
|
|
844
|
+
maxFee: fees.maxFee.toString(),
|
|
845
|
+
maxFeeHuman,
|
|
846
|
+
forwardFeeLow: fees.forwardFeeLow.toString(),
|
|
847
|
+
forwardFeeLowHuman,
|
|
848
|
+
forwardFeeMed: fees.forwardFeeMed.toString(),
|
|
849
|
+
forwardFeeMedHuman,
|
|
850
|
+
forwardFeeHigh: fees.forwardFeeHigh.toString(),
|
|
851
|
+
forwardFeeHighHuman,
|
|
852
|
+
...transferAmount != null ? { transferAmount: transferAmount.toString() } : {},
|
|
853
|
+
...transferAmountHuman != null ? { transferAmountHuman } : {},
|
|
854
|
+
...totalBurn != null ? { totalBurn: totalBurn.toString() } : {},
|
|
855
|
+
...totalBurnHuman != null ? { totalBurnHuman } : {},
|
|
856
|
+
feeSummary
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
function cctpFeeSummaryFromBodyForSign(bodyForSign) {
|
|
860
|
+
const cctpBurn = parseCctpBurnExtraJSON(bodyForSign);
|
|
861
|
+
if (!cctpBurn) return null;
|
|
862
|
+
const transferAmount = BigInt(cctpBurn.transferAmount);
|
|
863
|
+
const maxFee = BigInt(cctpBurn.proposalMaxFee);
|
|
864
|
+
const totalBurn = transferAmount + maxFee;
|
|
865
|
+
const transferAmountHuman = formatCctpUsdcHuman(transferAmount);
|
|
866
|
+
const maxFeeHuman = formatCctpUsdcHuman(maxFee);
|
|
867
|
+
const totalBurnHuman = formatCctpUsdcHuman(totalBurn);
|
|
868
|
+
const tier = cctpBurn.feeTier ?? "med";
|
|
869
|
+
const src = cctpChainConfig(cctpBurn.sourceChainId);
|
|
870
|
+
const dst = cctpChainConfig(cctpBurn.destChainId);
|
|
871
|
+
const routePart = src && dst ? ` Route: ${src.label} \u2192 ${dst.label}.` : "";
|
|
872
|
+
return {
|
|
873
|
+
feeTierUsed: tier,
|
|
874
|
+
maxFee: maxFee.toString(),
|
|
875
|
+
maxFeeHuman,
|
|
876
|
+
forwardFeeLow: maxFee.toString(),
|
|
877
|
+
forwardFeeLowHuman: maxFeeHuman,
|
|
878
|
+
forwardFeeMed: maxFee.toString(),
|
|
879
|
+
forwardFeeMedHuman: maxFeeHuman,
|
|
880
|
+
forwardFeeHigh: maxFee.toString(),
|
|
881
|
+
forwardFeeHighHuman: maxFeeHuman,
|
|
882
|
+
transferAmount: transferAmount.toString(),
|
|
883
|
+
transferAmountHuman,
|
|
884
|
+
totalBurn: totalBurn.toString(),
|
|
885
|
+
totalBurnHuman,
|
|
886
|
+
feeSummary: `Proposed CCTP burn.${routePart} Recipient receives ${transferAmountHuman} USDC; proposal max fee (${tier} tier): ${maxFeeHuman} USDC; total burn ${totalBurnHuman} USDC. Max fee refreshes at Get Sig.`
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
async function cctpFetchUsdcBalance(args) {
|
|
890
|
+
const cfg = cctpChainConfig(args.chainId);
|
|
891
|
+
if (!cfg) throw new Error(`Chain ${args.chainId} is not CCTP-supported.`);
|
|
892
|
+
const ch = viem.defineChain({
|
|
893
|
+
id: args.chainId,
|
|
894
|
+
name: cfg.label,
|
|
895
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
896
|
+
rpcUrls: { default: { http: [args.rpcUrl.trim()] } }
|
|
897
|
+
});
|
|
898
|
+
const client = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl.trim()) });
|
|
899
|
+
const owner = viem.getAddress(args.executorAddress);
|
|
900
|
+
const balanceRaw = await client.readContract({
|
|
901
|
+
address: viem.getAddress(cfg.usdc),
|
|
902
|
+
abi: erc20BalanceAbi2,
|
|
903
|
+
functionName: "balanceOf",
|
|
904
|
+
args: [owner]
|
|
905
|
+
});
|
|
906
|
+
const out = {
|
|
907
|
+
balanceRaw: balanceRaw.toString(),
|
|
908
|
+
balanceUsdcHuman: formatCctpUsdcHuman(balanceRaw),
|
|
909
|
+
usdcAddress: cfg.usdc
|
|
910
|
+
};
|
|
911
|
+
const transferHuman = args.transferAmountHuman?.trim();
|
|
912
|
+
const maxFeeRaw = args.feeSnapshot?.maxFee;
|
|
913
|
+
if (transferHuman && maxFeeRaw != null) {
|
|
914
|
+
const transferAmount = viem.parseUnits(transferHuman, CCTP_USDC_DECIMALS);
|
|
915
|
+
const maxFee = BigInt(String(maxFeeRaw));
|
|
916
|
+
const totalRequired = transferAmount + maxFee;
|
|
917
|
+
out.requiredTotalBurnRaw = totalRequired.toString();
|
|
918
|
+
out.requiredTotalBurnHuman = formatCctpUsdcHuman(totalRequired);
|
|
919
|
+
out.sufficientForTransfer = balanceRaw >= totalRequired;
|
|
920
|
+
out.feeSummary = `Wallet holds ${out.balanceUsdcHuman} USDC; transfer requires ${formatCctpUsdcHuman(totalRequired)} USDC (${transferHuman} + ${formatCctpUsdcHuman(maxFee)} max fee).`;
|
|
921
|
+
}
|
|
922
|
+
return out;
|
|
923
|
+
}
|
|
924
|
+
async function cctpFetchTransferStatus(args) {
|
|
925
|
+
const base = irisApiBaseForChainId(args.sourceChainId);
|
|
926
|
+
const hash = args.burnTxHash.trim().replace(/^0x/, "");
|
|
927
|
+
const url = `${base}/v2/messages/${args.sourceDomain}?transactionHash=0x${hash}`;
|
|
928
|
+
const res = await fetch(url, { headers: { Accept: "application/json" } });
|
|
929
|
+
if (!res.ok) {
|
|
930
|
+
const text = await res.text().catch(() => "");
|
|
931
|
+
throw new Error(`Iris message lookup failed (${res.status}): ${text.slice(0, 200)}`);
|
|
932
|
+
}
|
|
933
|
+
const json = await res.json();
|
|
934
|
+
const messages = json.messages ?? json.data ?? [];
|
|
935
|
+
const first = messages[0] ?? json;
|
|
936
|
+
return {
|
|
937
|
+
status: String(first.status ?? "unknown"),
|
|
938
|
+
attestation: typeof first.attestation === "string" ? first.attestation : void 0,
|
|
939
|
+
message: first.message,
|
|
940
|
+
eventNonce: first.eventNonce
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
async function cctpFetchBurnFeesForRoute(args) {
|
|
944
|
+
const src = cctpChainConfig(args.sourceChainId);
|
|
945
|
+
const dst = cctpChainConfig(args.destChainId);
|
|
946
|
+
if (!src || !dst) throw new Error("Unsupported source or destination chain for CCTP.");
|
|
947
|
+
if (src.network !== dst.network) throw new Error("Source and destination must be on the same network (mainnet or testnet).");
|
|
948
|
+
if (src.chainId === dst.chainId) throw new Error("Source and destination must differ.");
|
|
949
|
+
const fees = await fetchCctpBurnFees({
|
|
950
|
+
sourceChainId: args.sourceChainId,
|
|
951
|
+
sourceDomain: src.domain,
|
|
952
|
+
destDomain: dst.domain,
|
|
953
|
+
feeTier: args.feeTier
|
|
954
|
+
});
|
|
955
|
+
const transferHuman = args.transferAmountHuman?.trim();
|
|
956
|
+
const transferAmount = transferHuman ? viem.parseUnits(transferHuman, CCTP_USDC_DECIMALS) : void 0;
|
|
957
|
+
const feeFields = buildCctpMcpFeeFields({
|
|
958
|
+
fees,
|
|
959
|
+
transferAmount,
|
|
960
|
+
transferAmountHuman: transferHuman || void 0
|
|
961
|
+
});
|
|
962
|
+
return {
|
|
963
|
+
sourceChainId: args.sourceChainId,
|
|
964
|
+
destChainId: args.destChainId,
|
|
965
|
+
sourceDomain: src.domain,
|
|
966
|
+
destDomain: dst.domain,
|
|
967
|
+
sourceLabel: src.label,
|
|
968
|
+
destLabel: dst.label,
|
|
969
|
+
network: src.network,
|
|
970
|
+
fees: feeFields,
|
|
971
|
+
...feeFields
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// src/protocols/evm/circle-cctp/index.ts
|
|
976
|
+
var circleCctpProtocolModule = {
|
|
977
|
+
id: CIRCLE_CCTP_PROTOCOL_ID,
|
|
978
|
+
chainCategory: "evm",
|
|
979
|
+
isChainSupported(ctx) {
|
|
980
|
+
if (ctx.chainCategory !== "evm") return false;
|
|
981
|
+
return isCctpSourceChainSupported(ctx.chainId);
|
|
982
|
+
},
|
|
983
|
+
isTokenSupported(token) {
|
|
984
|
+
return token.category === "evm" && token.kind === "erc20";
|
|
985
|
+
},
|
|
986
|
+
actions: [
|
|
987
|
+
{
|
|
988
|
+
id: "circle-cctp.fetch-routes",
|
|
989
|
+
protocolId: CIRCLE_CCTP_PROTOCOL_ID,
|
|
990
|
+
chainCategory: "evm",
|
|
991
|
+
description: "List supported CCTP Forwarding Service routes (EVM)",
|
|
992
|
+
commonParams: [],
|
|
993
|
+
params: {
|
|
994
|
+
network: { type: "string", required: false, description: "mainnet | testnet" }
|
|
995
|
+
}
|
|
996
|
+
},
|
|
997
|
+
{
|
|
998
|
+
id: "circle-cctp.fetch-fees",
|
|
999
|
+
protocolId: CIRCLE_CCTP_PROTOCOL_ID,
|
|
1000
|
+
chainCategory: "evm",
|
|
1001
|
+
description: "Quote Iris Forwarding Service burn fees for a route",
|
|
1002
|
+
commonParams: [],
|
|
1003
|
+
params: {
|
|
1004
|
+
sourceChainId: { type: "number", required: true, description: "Source EIP-155 chain id" },
|
|
1005
|
+
destChainId: { type: "number", required: true, description: "Destination EIP-155 chain id" },
|
|
1006
|
+
transferAmountHuman: { type: "string", required: true, description: "USDC amount recipient receives" }
|
|
1007
|
+
}
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
id: "circle-cctp.burn-forward",
|
|
1011
|
+
protocolId: CIRCLE_CCTP_PROTOCOL_ID,
|
|
1012
|
+
chainCategory: "evm",
|
|
1013
|
+
description: "Burn USDC on source chain via CCTP Forwarding Service (approve + depositForBurnWithHook batch)",
|
|
1014
|
+
commonParams: ["keyGen", "purposeText", "useCustomGas"],
|
|
1015
|
+
params: {
|
|
1016
|
+
sourceChainId: { type: "number", required: true, description: "Source EIP-155 chain id" },
|
|
1017
|
+
destChainId: { type: "number", required: true, description: "Destination EIP-155 chain id" },
|
|
1018
|
+
transferAmountHuman: { type: "string", required: true, description: "USDC amount recipient receives" },
|
|
1019
|
+
mintRecipient: { type: "address", required: false, description: "Destination mint recipient (default executor)" }
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
]
|
|
1023
|
+
};
|
|
1024
|
+
registerProtocolModule(circleCctpProtocolModule);
|
|
1025
|
+
|
|
1026
|
+
exports.CCTP_DEFAULT_MIN_FINALITY_THRESHOLD = CCTP_DEFAULT_MIN_FINALITY_THRESHOLD;
|
|
1027
|
+
exports.CCTP_DEPOSIT_FOR_BURN_FALLBACK_GAS = CCTP_DEPOSIT_FOR_BURN_FALLBACK_GAS;
|
|
1028
|
+
exports.CCTP_ERC20_APPROVE_FALLBACK_GAS = CCTP_ERC20_APPROVE_FALLBACK_GAS;
|
|
1029
|
+
exports.CCTP_EVM_CHAINS = CCTP_EVM_CHAINS;
|
|
1030
|
+
exports.CCTP_USDC_DECIMALS = CCTP_USDC_DECIMALS;
|
|
1031
|
+
exports.CIRCLE_CCTP_PROTOCOL_ID = CIRCLE_CCTP_PROTOCOL_ID;
|
|
1032
|
+
exports.FORWARDING_SERVICE_HOOK_DATA = FORWARDING_SERVICE_HOOK_DATA;
|
|
1033
|
+
exports.IRIS_API_MAINNET = IRIS_API_MAINNET;
|
|
1034
|
+
exports.IRIS_API_SANDBOX = IRIS_API_SANDBOX;
|
|
1035
|
+
exports.TOKEN_MESSENGER_V2_MAINNET = TOKEN_MESSENGER_V2_MAINNET;
|
|
1036
|
+
exports.TOKEN_MESSENGER_V2_TESTNET = TOKEN_MESSENGER_V2_TESTNET;
|
|
1037
|
+
exports.buildCctpBurnExtraJSON = buildCctpBurnExtraJSON;
|
|
1038
|
+
exports.buildCctpBurnSteps = buildCctpBurnSteps;
|
|
1039
|
+
exports.buildEvmMultisignBodyCctpBurnBatch = buildEvmMultisignBodyCctpBurnBatch;
|
|
1040
|
+
exports.buildEvmMultisignBodyCctpBurnBatchFromMcp = buildEvmMultisignBodyCctpBurnBatchFromMcp;
|
|
1041
|
+
exports.cctpAssertBurnPreconditions = cctpAssertBurnPreconditions;
|
|
1042
|
+
exports.cctpCanonicalUsdcAddress = cctpCanonicalUsdcAddress;
|
|
1043
|
+
exports.cctpChainConfig = cctpChainConfig;
|
|
1044
|
+
exports.cctpFeeSummaryFromBodyForSign = cctpFeeSummaryFromBodyForSign;
|
|
1045
|
+
exports.cctpFetchBurnFeesForRoute = cctpFetchBurnFeesForRoute;
|
|
1046
|
+
exports.cctpFetchSupportedRoutes = cctpFetchSupportedRoutes;
|
|
1047
|
+
exports.cctpFetchTransferStatus = cctpFetchTransferStatus;
|
|
1048
|
+
exports.cctpFetchUsdcBalance = cctpFetchUsdcBalance;
|
|
1049
|
+
exports.cctpMintRecipientAddressFromExtra = cctpMintRecipientAddressFromExtra;
|
|
1050
|
+
exports.cctpSupportedRoutes = cctpSupportedRoutes;
|
|
1051
|
+
exports.chainIdToDomain = chainIdToDomain;
|
|
1052
|
+
exports.circleCctpProtocolModule = circleCctpProtocolModule;
|
|
1053
|
+
exports.domainToChainId = domainToChainId;
|
|
1054
|
+
exports.fetchCctpBurnFees = fetchCctpBurnFees;
|
|
1055
|
+
exports.formatCctpUsdcHuman = formatCctpUsdcHuman;
|
|
1056
|
+
exports.irisApiBaseForChainId = irisApiBaseForChainId;
|
|
1057
|
+
exports.isCctpBurnEvmSignRequest = isCctpBurnEvmSignRequest;
|
|
1058
|
+
exports.isCctpCanonicalUsdcRow = isCctpCanonicalUsdcRow;
|
|
1059
|
+
exports.isCctpErc20ApproveEvmSignRequest = isCctpErc20ApproveEvmSignRequest;
|
|
1060
|
+
exports.isCctpRouteSupported = isCctpRouteSupported;
|
|
1061
|
+
exports.isCctpSourceChainSupported = isCctpSourceChainSupported;
|
|
1062
|
+
exports.parseCctpBurnExtraJSON = parseCctpBurnExtraJSON;
|
|
1063
|
+
exports.parseCctpEvmChainId = parseCctpEvmChainId;
|
|
1064
|
+
exports.rebuildCctpBurnCalldataAtGetSig = rebuildCctpBurnCalldataAtGetSig;
|
|
1065
|
+
exports.resolveCctpBatchStepGasFromSignRequest = resolveCctpBatchStepGasFromSignRequest;
|
|
1066
|
+
//# sourceMappingURL=index.cjs.map
|
|
1067
|
+
//# sourceMappingURL=index.cjs.map
|