@circle-fin/provider-cctp-v2 1.8.4 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.md +5 -5
- package/index.cjs +422 -173
- package/index.d.cts +103 -0
- package/index.d.mts +103 -0
- package/index.d.ts +103 -0
- package/index.mjs +422 -173
- package/package.json +3 -1
package/index.mjs
CHANGED
|
@@ -33,6 +33,7 @@ import { formatUnits as formatUnits$1 } from '@ethersproject/units';
|
|
|
33
33
|
import { hexlify, hexZeroPad } from '@ethersproject/bytes';
|
|
34
34
|
import { getAddress } from '@ethersproject/address';
|
|
35
35
|
import bs58 from 'bs58';
|
|
36
|
+
import '@ethersproject/abi';
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* @packageDocumentation
|
|
@@ -77,6 +78,8 @@ import bs58 from 'bs58';
|
|
|
77
78
|
Blockchain["Celo_Alfajores_Testnet"] = "Celo_Alfajores_Testnet";
|
|
78
79
|
Blockchain["Codex"] = "Codex";
|
|
79
80
|
Blockchain["Codex_Testnet"] = "Codex_Testnet";
|
|
81
|
+
Blockchain["Cronos"] = "Cronos";
|
|
82
|
+
Blockchain["Cronos_Testnet"] = "Cronos_Testnet";
|
|
80
83
|
Blockchain["Edge"] = "Edge";
|
|
81
84
|
Blockchain["Edge_Testnet"] = "Edge_Testnet";
|
|
82
85
|
Blockchain["Ethereum"] = "Ethereum";
|
|
@@ -159,6 +162,7 @@ var BridgeChain;
|
|
|
159
162
|
BridgeChain["Avalanche"] = "Avalanche";
|
|
160
163
|
BridgeChain["Base"] = "Base";
|
|
161
164
|
BridgeChain["Codex"] = "Codex";
|
|
165
|
+
BridgeChain["Cronos"] = "Cronos";
|
|
162
166
|
BridgeChain["Edge"] = "Edge";
|
|
163
167
|
BridgeChain["Ethereum"] = "Ethereum";
|
|
164
168
|
BridgeChain["HyperEVM"] = "HyperEVM";
|
|
@@ -183,6 +187,7 @@ var BridgeChain;
|
|
|
183
187
|
BridgeChain["Avalanche_Fuji"] = "Avalanche_Fuji";
|
|
184
188
|
BridgeChain["Base_Sepolia"] = "Base_Sepolia";
|
|
185
189
|
BridgeChain["Codex_Testnet"] = "Codex_Testnet";
|
|
190
|
+
BridgeChain["Cronos_Testnet"] = "Cronos_Testnet";
|
|
186
191
|
BridgeChain["Edge_Testnet"] = "Edge_Testnet";
|
|
187
192
|
BridgeChain["Ethereum_Sepolia"] = "Ethereum_Sepolia";
|
|
188
193
|
BridgeChain["HyperEVM_Testnet"] = "HyperEVM_Testnet";
|
|
@@ -1228,6 +1233,96 @@ var EarnChain;
|
|
|
1228
1233
|
}
|
|
1229
1234
|
});
|
|
1230
1235
|
|
|
1236
|
+
/**
|
|
1237
|
+
* Cronos Mainnet chain definition
|
|
1238
|
+
* @remarks
|
|
1239
|
+
* This represents the official production network for the Cronos blockchain.
|
|
1240
|
+
* Cronos is an EVM-compatible blockchain.
|
|
1241
|
+
*/ const Cronos = defineChain({
|
|
1242
|
+
type: 'evm',
|
|
1243
|
+
chain: Blockchain.Cronos,
|
|
1244
|
+
name: 'Cronos',
|
|
1245
|
+
title: 'Cronos Mainnet',
|
|
1246
|
+
nativeCurrency: {
|
|
1247
|
+
name: 'Cronos',
|
|
1248
|
+
symbol: 'CRO',
|
|
1249
|
+
decimals: 18
|
|
1250
|
+
},
|
|
1251
|
+
chainId: 25,
|
|
1252
|
+
isTestnet: false,
|
|
1253
|
+
explorerUrl: 'https://cronoscan.com/tx/{hash}',
|
|
1254
|
+
rpcEndpoints: [
|
|
1255
|
+
'https://evm.cronos.org'
|
|
1256
|
+
],
|
|
1257
|
+
eurcAddress: '0xA6dE01a2d62C6B5f3525d768f34d276652C554c8',
|
|
1258
|
+
usdcAddress: '0x3D7F2C478aAfdB65542BCB44bCeeC05849999d2D',
|
|
1259
|
+
usdtAddress: null,
|
|
1260
|
+
cctp: {
|
|
1261
|
+
domain: 32,
|
|
1262
|
+
contracts: {
|
|
1263
|
+
v2: {
|
|
1264
|
+
type: 'split',
|
|
1265
|
+
tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
|
|
1266
|
+
messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
|
|
1267
|
+
confirmations: 1,
|
|
1268
|
+
fastConfirmations: 1
|
|
1269
|
+
}
|
|
1270
|
+
},
|
|
1271
|
+
forwarderSupported: {
|
|
1272
|
+
source: false,
|
|
1273
|
+
destination: false
|
|
1274
|
+
}
|
|
1275
|
+
},
|
|
1276
|
+
kitContracts: {
|
|
1277
|
+
bridge: BRIDGE_CONTRACT_EVM_MAINNET
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
/**
|
|
1282
|
+
* Cronos Testnet chain definition
|
|
1283
|
+
* @remarks
|
|
1284
|
+
* This represents the official test network for the Cronos blockchain.
|
|
1285
|
+
* Cronos is an EVM-compatible blockchain.
|
|
1286
|
+
*/ const CronosTestnet = defineChain({
|
|
1287
|
+
type: 'evm',
|
|
1288
|
+
chain: Blockchain.Cronos_Testnet,
|
|
1289
|
+
name: 'Cronos Testnet',
|
|
1290
|
+
title: 'Cronos Testnet',
|
|
1291
|
+
nativeCurrency: {
|
|
1292
|
+
name: 'CRO',
|
|
1293
|
+
symbol: 'tCRO',
|
|
1294
|
+
decimals: 18
|
|
1295
|
+
},
|
|
1296
|
+
chainId: 338,
|
|
1297
|
+
isTestnet: true,
|
|
1298
|
+
explorerUrl: 'https://explorer.cronos.org/testnet/tx/{hash}',
|
|
1299
|
+
rpcEndpoints: [
|
|
1300
|
+
'https://evm-t3.cronos.org'
|
|
1301
|
+
],
|
|
1302
|
+
eurcAddress: '0x31f7538adb53cF16350e6B0c89d03D91b7D12c46',
|
|
1303
|
+
usdcAddress: '0xEb33dc5fac03833e132593659e1dE7256aB59794',
|
|
1304
|
+
usdtAddress: null,
|
|
1305
|
+
cctp: {
|
|
1306
|
+
domain: 32,
|
|
1307
|
+
contracts: {
|
|
1308
|
+
v2: {
|
|
1309
|
+
type: 'split',
|
|
1310
|
+
tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
|
|
1311
|
+
messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
|
|
1312
|
+
confirmations: 1,
|
|
1313
|
+
fastConfirmations: 1
|
|
1314
|
+
}
|
|
1315
|
+
},
|
|
1316
|
+
forwarderSupported: {
|
|
1317
|
+
source: false,
|
|
1318
|
+
destination: false
|
|
1319
|
+
}
|
|
1320
|
+
},
|
|
1321
|
+
kitContracts: {
|
|
1322
|
+
bridge: BRIDGE_CONTRACT_EVM_TESTNET
|
|
1323
|
+
}
|
|
1324
|
+
});
|
|
1325
|
+
|
|
1231
1326
|
/**
|
|
1232
1327
|
* Edge Mainnet chain definition
|
|
1233
1328
|
* @remarks
|
|
@@ -3579,6 +3674,8 @@ var Chains = /*#__PURE__*/Object.freeze({
|
|
|
3579
3674
|
CeloAlfajoresTestnet: CeloAlfajoresTestnet,
|
|
3580
3675
|
Codex: Codex,
|
|
3581
3676
|
CodexTestnet: CodexTestnet,
|
|
3677
|
+
Cronos: Cronos,
|
|
3678
|
+
CronosTestnet: CronosTestnet,
|
|
3582
3679
|
Edge: Edge,
|
|
3583
3680
|
EdgeTestnet: EdgeTestnet,
|
|
3584
3681
|
Ethereum: Ethereum,
|
|
@@ -7353,6 +7450,7 @@ class KitError extends Error {
|
|
|
7353
7450
|
[Blockchain.Base]: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
|
|
7354
7451
|
[Blockchain.Celo]: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C',
|
|
7355
7452
|
[Blockchain.Codex]: '0xd996633a415985DBd7D6D12f4A4343E31f5037cf',
|
|
7453
|
+
[Blockchain.Cronos]: '0x3D7F2C478aAfdB65542BCB44bCeeC05849999d2D',
|
|
7356
7454
|
[Blockchain.Edge]: '0x98d2919b9A214E6Fa5384AC81E6864bA686Ad74c',
|
|
7357
7455
|
[Blockchain.Ethereum]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
|
7358
7456
|
[Blockchain.Hedera]: '0.0.456858',
|
|
@@ -7386,6 +7484,7 @@ class KitError extends Error {
|
|
|
7386
7484
|
[Blockchain.Avalanche_Fuji]: '0x5425890298aed601595a70AB815c96711a31Bc65',
|
|
7387
7485
|
[Blockchain.Base_Sepolia]: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
|
|
7388
7486
|
[Blockchain.Codex_Testnet]: '0x6d7f141b6819C2c9CC2f818e6ad549E7Ca090F8f',
|
|
7487
|
+
[Blockchain.Cronos_Testnet]: '0xEb33dc5fac03833e132593659e1dE7256aB59794',
|
|
7389
7488
|
[Blockchain.Edge_Testnet]: '0x2d9F7CAD728051AA35Ecdc472a14cf8cDF5CFD6B',
|
|
7390
7489
|
[Blockchain.Ethereum_Sepolia]: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',
|
|
7391
7490
|
[Blockchain.Hedera_Testnet]: '0.0.429274',
|
|
@@ -7453,13 +7552,22 @@ class KitError extends Error {
|
|
|
7453
7552
|
symbol: 'EURC',
|
|
7454
7553
|
decimals: 6,
|
|
7455
7554
|
locators: {
|
|
7555
|
+
// =========================================================================
|
|
7556
|
+
// Mainnets
|
|
7557
|
+
// =========================================================================
|
|
7456
7558
|
[Blockchain.Avalanche]: '0xc891EB4cbdEFf6e073e859e987815Ed1505c2ACD',
|
|
7457
7559
|
[Blockchain.Base]: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42',
|
|
7560
|
+
[Blockchain.Cronos]: '0xA6dE01a2d62C6B5f3525d768f34d276652C554c8',
|
|
7458
7561
|
[Blockchain.Ethereum]: '0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c',
|
|
7459
7562
|
[Blockchain.Solana]: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr',
|
|
7460
7563
|
[Blockchain.World_Chain]: '0x1C60ba0A0eD1019e8Eb035E6daF4155A5cE2380B',
|
|
7564
|
+
// =========================================================================
|
|
7461
7565
|
// Testnets
|
|
7462
|
-
|
|
7566
|
+
// =========================================================================
|
|
7567
|
+
[Blockchain.Arc_Testnet]: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
|
|
7568
|
+
[Blockchain.Base_Sepolia]: '0x808456652fdb597867f38412077A9182bf77359F',
|
|
7569
|
+
[Blockchain.Cronos_Testnet]: '0x31f7538adb53cF16350e6B0c89d03D91b7D12c46',
|
|
7570
|
+
[Blockchain.Ethereum_Sepolia]: '0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4'
|
|
7463
7571
|
}
|
|
7464
7572
|
};
|
|
7465
7573
|
|
|
@@ -7669,8 +7777,7 @@ class KitError extends Error {
|
|
|
7669
7777
|
* cirBTC (Circle Bitcoin) token definition with addresses and metadata.
|
|
7670
7778
|
*
|
|
7671
7779
|
* @remarks
|
|
7672
|
-
* Built-in cirBTC definition for the TokenRegistry.
|
|
7673
|
-
* on Arc Testnet.
|
|
7780
|
+
* Built-in cirBTC definition for the TokenRegistry.
|
|
7674
7781
|
*
|
|
7675
7782
|
* @example
|
|
7676
7783
|
* ```typescript
|
|
@@ -7686,7 +7793,15 @@ class KitError extends Error {
|
|
|
7686
7793
|
symbol: 'cirBTC',
|
|
7687
7794
|
decimals: 8,
|
|
7688
7795
|
locators: {
|
|
7689
|
-
|
|
7796
|
+
// =========================================================================
|
|
7797
|
+
// Mainnets
|
|
7798
|
+
// =========================================================================
|
|
7799
|
+
[Blockchain.Ethereum]: '0x72DFB2E44f59C5AD2bAFE84314E5b99a7cd5075E',
|
|
7800
|
+
// =========================================================================
|
|
7801
|
+
// Testnets
|
|
7802
|
+
// =========================================================================
|
|
7803
|
+
[Blockchain.Arc_Testnet]: '0xf0C4a4CE82A5746AbAAd9425360Ab04fbBA432BF',
|
|
7804
|
+
[Blockchain.Ethereum_Sepolia]: '0x3a3fe695F684Bf9b9e43CF43C2b895Ea5e392bB3'
|
|
7690
7805
|
}
|
|
7691
7806
|
};
|
|
7692
7807
|
|
|
@@ -9161,6 +9276,15 @@ const CUSTOM_BURN_GAS_ESTIMATE_EVM = 201_525n // p99 and max are same here: 201_
|
|
|
9161
9276
|
;
|
|
9162
9277
|
const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_839n) / 2 = 237_401n
|
|
9163
9278
|
;
|
|
9279
|
+
// Hard execution caps: observed max + ~30% buffer, used as gasLimit overrides on
|
|
9280
|
+
// chains whose eth_estimateGas under-reports (e.g. Cronos EIP-7623 calldata floor).
|
|
9281
|
+
// Kept separate from the fee-estimate averages above.
|
|
9282
|
+
const APPROVE_GAS_LIMIT_EVM = 100_000n // ERC-20 approve observed max ~46k
|
|
9283
|
+
;
|
|
9284
|
+
const DEPOSIT_FOR_BURN_GAS_LIMIT_EVM = 300_000n // observed max 226_506 + ~30%
|
|
9285
|
+
;
|
|
9286
|
+
const RECEIVE_MESSAGE_GAS_LIMIT_EVM = 400_000n // observed max 310_839 + ~30%
|
|
9287
|
+
;
|
|
9164
9288
|
/**
|
|
9165
9289
|
* The minimum finality threshold for CCTPv2 transfers.
|
|
9166
9290
|
*
|
|
@@ -9189,6 +9313,27 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9189
9313
|
'Content-Type': 'application/json'
|
|
9190
9314
|
}
|
|
9191
9315
|
};
|
|
9316
|
+
/**
|
|
9317
|
+
* Merges caller-provided polling overrides on top of {@link DEFAULT_CONFIG}.
|
|
9318
|
+
*
|
|
9319
|
+
* Headers are merged independently so caller-supplied headers augment the
|
|
9320
|
+
* defaults (such as `Content-Type`) rather than replacing them wholesale.
|
|
9321
|
+
*
|
|
9322
|
+
* @param config - Caller-provided polling configuration overrides
|
|
9323
|
+
* @param internalDefaults - Internal defaults applied before `config` (for example a
|
|
9324
|
+
* reduced `maxRetries` for one-shot requests); `config` still wins on conflict
|
|
9325
|
+
* @returns The effective polling configuration
|
|
9326
|
+
* @internal
|
|
9327
|
+
*/ const mergeAttestationConfig = (config, internalDefaults = {})=>({
|
|
9328
|
+
...DEFAULT_CONFIG,
|
|
9329
|
+
...internalDefaults,
|
|
9330
|
+
...config,
|
|
9331
|
+
headers: {
|
|
9332
|
+
...DEFAULT_CONFIG.headers,
|
|
9333
|
+
...internalDefaults.headers,
|
|
9334
|
+
...config.headers
|
|
9335
|
+
}
|
|
9336
|
+
});
|
|
9192
9337
|
/**
|
|
9193
9338
|
* Type guard that verifies if an unknown value matches the AttestationMessage shape
|
|
9194
9339
|
* and has all required properties.
|
|
@@ -9335,10 +9480,7 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9335
9480
|
* ```
|
|
9336
9481
|
*/ const fetchAttestation = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
9337
9482
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
9338
|
-
const effectiveConfig =
|
|
9339
|
-
...DEFAULT_CONFIG,
|
|
9340
|
-
...config
|
|
9341
|
-
};
|
|
9483
|
+
const effectiveConfig = mergeAttestationConfig(config);
|
|
9342
9484
|
return await pollApiGet(url, isAttestationResponse, effectiveConfig);
|
|
9343
9485
|
};
|
|
9344
9486
|
/**
|
|
@@ -9381,11 +9523,9 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9381
9523
|
*/ const fetchAttestationWithoutStatusCheck = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
9382
9524
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
9383
9525
|
// Use minimal retries since we're just fetching existing data
|
|
9384
|
-
const effectiveConfig = {
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
...config
|
|
9388
|
-
};
|
|
9526
|
+
const effectiveConfig = mergeAttestationConfig(config, {
|
|
9527
|
+
maxRetries: 3
|
|
9528
|
+
});
|
|
9389
9529
|
return await pollApiGet(url, isAttestationResponseWithoutStatusCheck, effectiveConfig);
|
|
9390
9530
|
};
|
|
9391
9531
|
/**
|
|
@@ -9445,10 +9585,7 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9445
9585
|
* ```
|
|
9446
9586
|
*/ const fetchReAttestedAttestation = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
9447
9587
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
9448
|
-
const effectiveConfig =
|
|
9449
|
-
...DEFAULT_CONFIG,
|
|
9450
|
-
...config
|
|
9451
|
-
};
|
|
9588
|
+
const effectiveConfig = mergeAttestationConfig(config);
|
|
9452
9589
|
return await pollApiGet(url, isReAttestedAttestationResponse, effectiveConfig);
|
|
9453
9590
|
};
|
|
9454
9591
|
/**
|
|
@@ -9514,14 +9651,139 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9514
9651
|
*/ const requestReAttestation = async (nonce, isTestnet, config = {})=>{
|
|
9515
9652
|
const url = buildReAttestUrl(nonce, isTestnet);
|
|
9516
9653
|
// Use minimal retries since we're just submitting a request, not polling for state
|
|
9517
|
-
const effectiveConfig = {
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
...config
|
|
9521
|
-
};
|
|
9654
|
+
const effectiveConfig = mergeAttestationConfig(config, {
|
|
9655
|
+
maxRetries: 3
|
|
9656
|
+
});
|
|
9522
9657
|
return await pollApiPost(url, {}, isReAttestationResponse, effectiveConfig);
|
|
9523
9658
|
};
|
|
9524
9659
|
|
|
9660
|
+
/**
|
|
9661
|
+
* Type guard that checks if the relayer has confirmed the mint transaction.
|
|
9662
|
+
*
|
|
9663
|
+
* This function validates that:
|
|
9664
|
+
* 1. The response has valid AttestationResponse structure
|
|
9665
|
+
* 2. At least one message has forwardState === 'CONFIRMED' (or 'COMPLETE') and a valid forwardTxHash
|
|
9666
|
+
*
|
|
9667
|
+
* If forwardState is 'FAILED', throws a non-retryable KitError.
|
|
9668
|
+
* If forwardState is 'PENDING' or not present, throws a RETRYABLE KitError to continue polling.
|
|
9669
|
+
*
|
|
9670
|
+
* @param obj - The value to check, typically a parsed JSON response
|
|
9671
|
+
* @returns True if the relayer has confirmed the mint
|
|
9672
|
+
* @throws {KitError} With FATAL recoverability if structure is invalid
|
|
9673
|
+
* @throws {KitError} With RESUMABLE recoverability if forwardState is 'FAILED'
|
|
9674
|
+
* @throws {KitError} With RETRYABLE recoverability if still pending
|
|
9675
|
+
* @internal
|
|
9676
|
+
*/ const isRelayerMintConfirmed = (obj)=>{
|
|
9677
|
+
// First check if the structure is valid
|
|
9678
|
+
if (!hasValidAttestationStructure(obj)) {
|
|
9679
|
+
throw new KitError({
|
|
9680
|
+
...InputError.VALIDATION_FAILED,
|
|
9681
|
+
recoverability: 'FATAL',
|
|
9682
|
+
message: 'Invalid attestation response structure from IRIS API.'
|
|
9683
|
+
});
|
|
9684
|
+
}
|
|
9685
|
+
// Find the first message (typically there's only one)
|
|
9686
|
+
const message = obj.messages[0];
|
|
9687
|
+
if (!message) {
|
|
9688
|
+
throw new KitError({
|
|
9689
|
+
...InputError.VALIDATION_FAILED,
|
|
9690
|
+
recoverability: 'FATAL',
|
|
9691
|
+
message: 'No attestation messages found in IRIS API response.'
|
|
9692
|
+
});
|
|
9693
|
+
}
|
|
9694
|
+
// Check for FAILED state - this is a permanent failure
|
|
9695
|
+
if (message.forwardState === 'FAILED') {
|
|
9696
|
+
throw new KitError({
|
|
9697
|
+
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
9698
|
+
recoverability: 'RESUMABLE',
|
|
9699
|
+
message: 'Circle relayer failed to forward the mint transaction. The mint may still have succeeded if another party submitted it. Check the recipient wallet balance before retrying. If the mint did not occur, you can manually submit it using the attestation data in the error cause.',
|
|
9700
|
+
cause: {
|
|
9701
|
+
trace: {
|
|
9702
|
+
eventNonce: message.eventNonce,
|
|
9703
|
+
attestation: message.attestation,
|
|
9704
|
+
message: message.message
|
|
9705
|
+
}
|
|
9706
|
+
}
|
|
9707
|
+
});
|
|
9708
|
+
}
|
|
9709
|
+
// Check if mint is confirmed (or complete) with a valid transaction hash
|
|
9710
|
+
// We accept both CONFIRMED and COMPLETE since COMPLETE implies CONFIRMED
|
|
9711
|
+
if ((message.forwardState === 'CONFIRMED' || message.forwardState === 'COMPLETE') && typeof message.forwardTxHash === 'string' && message.forwardTxHash.trim().length > 0) {
|
|
9712
|
+
return true;
|
|
9713
|
+
}
|
|
9714
|
+
// Still pending or not yet processed - throw RETRYABLE error to continue polling
|
|
9715
|
+
throw new KitError({
|
|
9716
|
+
...NetworkError.RELAYER_PENDING,
|
|
9717
|
+
recoverability: 'RETRYABLE',
|
|
9718
|
+
message: 'Relayer mint not ready. Waiting for confirmation.'
|
|
9719
|
+
});
|
|
9720
|
+
};
|
|
9721
|
+
/**
|
|
9722
|
+
* Polls the attestation API until the relayer's mint transaction is confirmed.
|
|
9723
|
+
*
|
|
9724
|
+
* This function is used when `useForwarder` is enabled. Instead of the user
|
|
9725
|
+
* submitting the mint transaction, Circle's Orbit relayer handles it automatically.
|
|
9726
|
+
* This function polls until the relayer has submitted and confirmed the mint transaction.
|
|
9727
|
+
*
|
|
9728
|
+
* @remarks
|
|
9729
|
+
* - Uses a 20-minute timeout by default (600 retries × 2 seconds)
|
|
9730
|
+
* - Throws immediately if `forwardState` is 'FAILED'
|
|
9731
|
+
* - Waits for `forwardState` to be 'CONFIRMED' or 'COMPLETE' (COMPLETE implies CONFIRMED)
|
|
9732
|
+
* - Returns the attestation message with `forwardTxHash` populated
|
|
9733
|
+
*
|
|
9734
|
+
* @param sourceDomainId - The CCTP domain ID of the source chain
|
|
9735
|
+
* @param transactionHash - The transaction hash of the burn operation
|
|
9736
|
+
* @param isTestnet - Whether this is for a testnet chain (true) or mainnet (false)
|
|
9737
|
+
* @param config - Optional configuration overrides for polling behavior
|
|
9738
|
+
* @returns The attestation message with confirmed forwardTxHash
|
|
9739
|
+
* @throws {KitError} With code 'NETWORK_RELAYER_FORWARD_FAILED' if relayer failed
|
|
9740
|
+
* @throws {KitError} If timeout is reached while still pending
|
|
9741
|
+
*
|
|
9742
|
+
* @example
|
|
9743
|
+
* ```typescript
|
|
9744
|
+
* const attestation = await fetchRelayerMint(0, '0xabc...', false)
|
|
9745
|
+
* console.log('Relayer mint tx:', attestation.forwardTxHash)
|
|
9746
|
+
* ```
|
|
9747
|
+
*/ const fetchRelayerMint = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
9748
|
+
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
9749
|
+
const effectiveConfig = mergeAttestationConfig(config);
|
|
9750
|
+
let response;
|
|
9751
|
+
try {
|
|
9752
|
+
response = await pollApiGet(url, isRelayerMintConfirmed, effectiveConfig);
|
|
9753
|
+
} catch (error) {
|
|
9754
|
+
// Enrich RELAYER_FORWARD_FAILED errors with the burn transaction hash
|
|
9755
|
+
if (error instanceof KitError && error.name === 'NETWORK_RELAYER_FORWARD_FAILED') {
|
|
9756
|
+
throw new KitError({
|
|
9757
|
+
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
9758
|
+
recoverability: error.recoverability,
|
|
9759
|
+
message: error.message,
|
|
9760
|
+
cause: {
|
|
9761
|
+
...error.cause,
|
|
9762
|
+
trace: {
|
|
9763
|
+
...error.cause?.trace,
|
|
9764
|
+
burnTxHash: transactionHash
|
|
9765
|
+
}
|
|
9766
|
+
}
|
|
9767
|
+
});
|
|
9768
|
+
}
|
|
9769
|
+
throw error;
|
|
9770
|
+
}
|
|
9771
|
+
// Return the first message (which should have forwardTxHash)
|
|
9772
|
+
// Note: This check is needed for TypeScript type safety even though
|
|
9773
|
+
// isRelayerMintConfirmed validates messages[0] exists. The type guard
|
|
9774
|
+
// narrows the type at the call site, but TypeScript can't infer that
|
|
9775
|
+
// the array still has elements after pollApiGet returns.
|
|
9776
|
+
const message = response.messages[0];
|
|
9777
|
+
if (!message) {
|
|
9778
|
+
throw new KitError({
|
|
9779
|
+
...InputError.VALIDATION_FAILED,
|
|
9780
|
+
recoverability: 'FATAL',
|
|
9781
|
+
message: 'No attestation messages found in response after polling.'
|
|
9782
|
+
});
|
|
9783
|
+
}
|
|
9784
|
+
return message;
|
|
9785
|
+
};
|
|
9786
|
+
|
|
9525
9787
|
const assertCCTPv2WalletContextSymbol = Symbol('assertCCTPv2WalletContext');
|
|
9526
9788
|
/**
|
|
9527
9789
|
* Asserts that the provided parameters match the CCTPv2 wallet context interface.
|
|
@@ -10396,6 +10658,8 @@ function hasPendingState(analysis, result) {
|
|
|
10396
10658
|
* - `adapter`: The adapter that will execute the transaction
|
|
10397
10659
|
* - `confirmations`: The number of confirmations to wait for (defaults to 1)
|
|
10398
10660
|
* - `timeout`: The timeout for the request in milliseconds
|
|
10661
|
+
* - `gasLimit`: Optional explicit gas limit (number) forwarded to EVM execute,
|
|
10662
|
+
* bypassing `eth_estimateGas`; ignored for non-EVM requests
|
|
10399
10663
|
* @returns The bridge step with the transaction details and explorer URL
|
|
10400
10664
|
* @throws If the transaction execution fails
|
|
10401
10665
|
*
|
|
@@ -10410,7 +10674,7 @@ function hasPendingState(analysis, result) {
|
|
|
10410
10674
|
* })
|
|
10411
10675
|
* console.log('Transaction hash:', step.txHash)
|
|
10412
10676
|
* ```
|
|
10413
|
-
*/ async function executePreparedChainRequest({ name, request, adapter, chain, confirmations = 1, timeout }) {
|
|
10677
|
+
*/ async function executePreparedChainRequest({ name, request, adapter, chain, confirmations = 1, timeout, gasLimit }) {
|
|
10414
10678
|
const step = {
|
|
10415
10679
|
name,
|
|
10416
10680
|
state: 'pending'
|
|
@@ -10423,7 +10687,9 @@ function hasPendingState(analysis, result) {
|
|
|
10423
10687
|
step.state = 'noop';
|
|
10424
10688
|
return step;
|
|
10425
10689
|
}
|
|
10426
|
-
const txHash = await request.execute(
|
|
10690
|
+
const txHash = request.type === 'evm' && gasLimit !== undefined ? await request.execute({
|
|
10691
|
+
gasLimit
|
|
10692
|
+
}) : await request.execute();
|
|
10427
10693
|
step.txHash = txHash;
|
|
10428
10694
|
const retryOptions = {
|
|
10429
10695
|
isRetryable: (err)=>isRetryableError$1(parseBlockchainError(err, {
|
|
@@ -10495,7 +10761,8 @@ function hasPendingState(analysis, result) {
|
|
|
10495
10761
|
name: 'approve',
|
|
10496
10762
|
adapter: params.source.adapter,
|
|
10497
10763
|
chain: params.source.chain,
|
|
10498
|
-
request: await provider.approve(params.source, approvalAmount)
|
|
10764
|
+
request: await provider.approve(params.source, approvalAmount),
|
|
10765
|
+
gasLimit: Number(APPROVE_GAS_LIMIT_EVM)
|
|
10499
10766
|
});
|
|
10500
10767
|
}
|
|
10501
10768
|
|
|
@@ -10522,7 +10789,8 @@ function hasPendingState(analysis, result) {
|
|
|
10522
10789
|
name: 'burn',
|
|
10523
10790
|
adapter: params.source.adapter,
|
|
10524
10791
|
chain: params.source.chain,
|
|
10525
|
-
request: await provider.burn(params)
|
|
10792
|
+
request: await provider.burn(params),
|
|
10793
|
+
gasLimit: Number(DEPOSIT_FOR_BURN_GAS_LIMIT_EVM)
|
|
10526
10794
|
});
|
|
10527
10795
|
}
|
|
10528
10796
|
|
|
@@ -10608,11 +10876,18 @@ function hasPendingState(analysis, result) {
|
|
|
10608
10876
|
*/ async function bridgeMint({ params, provider }, attestation) {
|
|
10609
10877
|
// Validate attestation message matches transfer params
|
|
10610
10878
|
await assertCCTPv2AttestationParams(attestation, params);
|
|
10879
|
+
const mintRequest = await provider.mint(params.source, params.destination, attestation);
|
|
10611
10880
|
const step = await executePreparedChainRequest({
|
|
10612
10881
|
name: 'mint',
|
|
10613
10882
|
adapter: params.destination.adapter,
|
|
10614
10883
|
chain: params.destination.chain,
|
|
10615
|
-
request:
|
|
10884
|
+
request: mintRequest,
|
|
10885
|
+
// Some chains (e.g. Cronos) enforce an EIP-7623 calldata gas floor that
|
|
10886
|
+
// eth_estimateGas does not account for, returning a below-floor value
|
|
10887
|
+
// without reverting. Pinning to a value above the observed execution max
|
|
10888
|
+
// (310_839) bypasses re-estimation and guarantees we clear both the floor
|
|
10889
|
+
// and the actual execution cost.
|
|
10890
|
+
gasLimit: Number(RECEIVE_MESSAGE_GAS_LIMIT_EVM)
|
|
10616
10891
|
});
|
|
10617
10892
|
// Add forwarded: false for non-relayer mints
|
|
10618
10893
|
return {
|
|
@@ -10650,136 +10925,6 @@ const mockAttestationMessage = {
|
|
|
10650
10925
|
delayReason: null
|
|
10651
10926
|
};
|
|
10652
10927
|
|
|
10653
|
-
/**
|
|
10654
|
-
* Type guard that checks if the relayer has confirmed the mint transaction.
|
|
10655
|
-
*
|
|
10656
|
-
* This function validates that:
|
|
10657
|
-
* 1. The response has valid AttestationResponse structure
|
|
10658
|
-
* 2. At least one message has forwardState === 'CONFIRMED' (or 'COMPLETE') and a valid forwardTxHash
|
|
10659
|
-
*
|
|
10660
|
-
* If forwardState is 'FAILED', throws a non-retryable KitError.
|
|
10661
|
-
* If forwardState is 'PENDING' or not present, throws a RETRYABLE KitError to continue polling.
|
|
10662
|
-
*
|
|
10663
|
-
* @param obj - The value to check, typically a parsed JSON response
|
|
10664
|
-
* @returns True if the relayer has confirmed the mint
|
|
10665
|
-
* @throws {KitError} With FATAL recoverability if structure is invalid
|
|
10666
|
-
* @throws {KitError} With RESUMABLE recoverability if forwardState is 'FAILED'
|
|
10667
|
-
* @throws {KitError} With RETRYABLE recoverability if still pending
|
|
10668
|
-
* @internal
|
|
10669
|
-
*/ const isRelayerMintConfirmed = (obj)=>{
|
|
10670
|
-
// First check if the structure is valid
|
|
10671
|
-
if (!hasValidAttestationStructure(obj)) {
|
|
10672
|
-
throw new KitError({
|
|
10673
|
-
...InputError.VALIDATION_FAILED,
|
|
10674
|
-
recoverability: 'FATAL',
|
|
10675
|
-
message: 'Invalid attestation response structure from IRIS API.'
|
|
10676
|
-
});
|
|
10677
|
-
}
|
|
10678
|
-
// Find the first message (typically there's only one)
|
|
10679
|
-
const message = obj.messages[0];
|
|
10680
|
-
if (!message) {
|
|
10681
|
-
throw new KitError({
|
|
10682
|
-
...InputError.VALIDATION_FAILED,
|
|
10683
|
-
recoverability: 'FATAL',
|
|
10684
|
-
message: 'No attestation messages found in IRIS API response.'
|
|
10685
|
-
});
|
|
10686
|
-
}
|
|
10687
|
-
// Check for FAILED state - this is a permanent failure
|
|
10688
|
-
if (message.forwardState === 'FAILED') {
|
|
10689
|
-
throw new KitError({
|
|
10690
|
-
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
10691
|
-
recoverability: 'RESUMABLE',
|
|
10692
|
-
message: 'Circle relayer failed to forward the mint transaction. The mint may still have succeeded if another party submitted it. Check the recipient wallet balance before retrying. If the mint did not occur, you can manually submit it using the attestation data in the error cause.',
|
|
10693
|
-
cause: {
|
|
10694
|
-
trace: {
|
|
10695
|
-
eventNonce: message.eventNonce,
|
|
10696
|
-
attestation: message.attestation,
|
|
10697
|
-
message: message.message
|
|
10698
|
-
}
|
|
10699
|
-
}
|
|
10700
|
-
});
|
|
10701
|
-
}
|
|
10702
|
-
// Check if mint is confirmed (or complete) with a valid transaction hash
|
|
10703
|
-
// We accept both CONFIRMED and COMPLETE since COMPLETE implies CONFIRMED
|
|
10704
|
-
if ((message.forwardState === 'CONFIRMED' || message.forwardState === 'COMPLETE') && typeof message.forwardTxHash === 'string' && message.forwardTxHash.trim().length > 0) {
|
|
10705
|
-
return true;
|
|
10706
|
-
}
|
|
10707
|
-
// Still pending or not yet processed - throw RETRYABLE error to continue polling
|
|
10708
|
-
throw new KitError({
|
|
10709
|
-
...NetworkError.RELAYER_PENDING,
|
|
10710
|
-
recoverability: 'RETRYABLE',
|
|
10711
|
-
message: 'Relayer mint not ready. Waiting for confirmation.'
|
|
10712
|
-
});
|
|
10713
|
-
};
|
|
10714
|
-
/**
|
|
10715
|
-
* Polls the attestation API until the relayer's mint transaction is confirmed.
|
|
10716
|
-
*
|
|
10717
|
-
* This function is used when `useForwarder` is enabled. Instead of the user
|
|
10718
|
-
* submitting the mint transaction, Circle's Orbit relayer handles it automatically.
|
|
10719
|
-
* This function polls until the relayer has submitted and confirmed the mint transaction.
|
|
10720
|
-
*
|
|
10721
|
-
* @remarks
|
|
10722
|
-
* - Uses a 20-minute timeout by default (600 retries × 2 seconds)
|
|
10723
|
-
* - Throws immediately if `forwardState` is 'FAILED'
|
|
10724
|
-
* - Waits for `forwardState` to be 'CONFIRMED' or 'COMPLETE' (COMPLETE implies CONFIRMED)
|
|
10725
|
-
* - Returns the attestation message with `forwardTxHash` populated
|
|
10726
|
-
*
|
|
10727
|
-
* @param sourceDomainId - The CCTP domain ID of the source chain
|
|
10728
|
-
* @param transactionHash - The transaction hash of the burn operation
|
|
10729
|
-
* @param isTestnet - Whether this is for a testnet chain (true) or mainnet (false)
|
|
10730
|
-
* @param config - Optional configuration overrides for polling behavior
|
|
10731
|
-
* @returns The attestation message with confirmed forwardTxHash
|
|
10732
|
-
* @throws {KitError} With code 'NETWORK_RELAYER_FORWARD_FAILED' if relayer failed
|
|
10733
|
-
* @throws {KitError} If timeout is reached while still pending
|
|
10734
|
-
*
|
|
10735
|
-
* @example
|
|
10736
|
-
* ```typescript
|
|
10737
|
-
* const attestation = await fetchRelayerMint(0, '0xabc...', false)
|
|
10738
|
-
* console.log('Relayer mint tx:', attestation.forwardTxHash)
|
|
10739
|
-
* ```
|
|
10740
|
-
*/ const fetchRelayerMint = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
10741
|
-
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
10742
|
-
const effectiveConfig = {
|
|
10743
|
-
...DEFAULT_CONFIG,
|
|
10744
|
-
...config
|
|
10745
|
-
};
|
|
10746
|
-
let response;
|
|
10747
|
-
try {
|
|
10748
|
-
response = await pollApiGet(url, isRelayerMintConfirmed, effectiveConfig);
|
|
10749
|
-
} catch (error) {
|
|
10750
|
-
// Enrich RELAYER_FORWARD_FAILED errors with the burn transaction hash
|
|
10751
|
-
if (error instanceof KitError && error.name === 'NETWORK_RELAYER_FORWARD_FAILED') {
|
|
10752
|
-
throw new KitError({
|
|
10753
|
-
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
10754
|
-
recoverability: error.recoverability,
|
|
10755
|
-
message: error.message,
|
|
10756
|
-
cause: {
|
|
10757
|
-
...error.cause,
|
|
10758
|
-
trace: {
|
|
10759
|
-
...error.cause?.trace,
|
|
10760
|
-
burnTxHash: transactionHash
|
|
10761
|
-
}
|
|
10762
|
-
}
|
|
10763
|
-
});
|
|
10764
|
-
}
|
|
10765
|
-
throw error;
|
|
10766
|
-
}
|
|
10767
|
-
// Return the first message (which should have forwardTxHash)
|
|
10768
|
-
// Note: This check is needed for TypeScript type safety even though
|
|
10769
|
-
// isRelayerMintConfirmed validates messages[0] exists. The type guard
|
|
10770
|
-
// narrows the type at the call site, but TypeScript can't infer that
|
|
10771
|
-
// the array still has elements after pollApiGet returns.
|
|
10772
|
-
const message = response.messages[0];
|
|
10773
|
-
if (!message) {
|
|
10774
|
-
throw new KitError({
|
|
10775
|
-
...InputError.VALIDATION_FAILED,
|
|
10776
|
-
recoverability: 'FATAL',
|
|
10777
|
-
message: 'No attestation messages found in response after polling.'
|
|
10778
|
-
});
|
|
10779
|
-
}
|
|
10780
|
-
return message;
|
|
10781
|
-
};
|
|
10782
|
-
|
|
10783
10928
|
/**
|
|
10784
10929
|
* Executes the mint step for forwarding mode where Circle's relayer handles the mint.
|
|
10785
10930
|
*
|
|
@@ -10793,6 +10938,7 @@ const mockAttestationMessage = {
|
|
|
10793
10938
|
* immediately after the API confirms the mint.
|
|
10794
10939
|
*
|
|
10795
10940
|
* @param params - The bridge parameters containing source, destination, amount and config
|
|
10941
|
+
* @param provider - The CCTP v2 bridging provider
|
|
10796
10942
|
* @param context - The step context containing burnTxHash from the burn step
|
|
10797
10943
|
* @returns Promise resolving to the bridge step with transaction details
|
|
10798
10944
|
* @throws {KitError} If burnTxHash is not available in context
|
|
@@ -10801,10 +10947,13 @@ const mockAttestationMessage = {
|
|
|
10801
10947
|
*
|
|
10802
10948
|
* @example
|
|
10803
10949
|
* ```typescript
|
|
10804
|
-
* const step = await bridgeRelayerMint(
|
|
10950
|
+
* const step = await bridgeRelayerMint(
|
|
10951
|
+
* { params, provider },
|
|
10952
|
+
* { burnTxHash: '0x...' }
|
|
10953
|
+
* )
|
|
10805
10954
|
* console.log('Relayer mint tx:', step.txHash)
|
|
10806
10955
|
* ```
|
|
10807
|
-
*/ async function bridgeRelayerMint(params, context) {
|
|
10956
|
+
*/ async function bridgeRelayerMint({ params, provider }, context) {
|
|
10808
10957
|
const step = {
|
|
10809
10958
|
name: 'mint',
|
|
10810
10959
|
state: 'pending',
|
|
@@ -10820,7 +10969,7 @@ const mockAttestationMessage = {
|
|
|
10820
10969
|
}
|
|
10821
10970
|
try {
|
|
10822
10971
|
// Poll attestation API until relayer confirms the mint
|
|
10823
|
-
const attestation = await fetchRelayerMint(params.source
|
|
10972
|
+
const attestation = await provider.fetchRelayerMint(params.source, context.burnTxHash);
|
|
10824
10973
|
// Extract the relayer's mint transaction hash
|
|
10825
10974
|
const forwardTxHash = attestation.forwardTxHash;
|
|
10826
10975
|
if (!forwardTxHash) {
|
|
@@ -10914,7 +11063,7 @@ const mockAttestationMessage = {
|
|
|
10914
11063
|
name: 'mint',
|
|
10915
11064
|
// bridgeRelayerMint validates context.burnTxHash internally with a KitError
|
|
10916
11065
|
// bridgeMint requires attestationData which is validated here
|
|
10917
|
-
executor: useForwarder ? async (params,
|
|
11066
|
+
executor: useForwarder ? async (params, provider, context)=>{
|
|
10918
11067
|
if (!context) {
|
|
10919
11068
|
throw new KitError({
|
|
10920
11069
|
...InputError.VALIDATION_FAILED,
|
|
@@ -10922,7 +11071,10 @@ const mockAttestationMessage = {
|
|
|
10922
11071
|
message: 'Step context is required for relayer mint'
|
|
10923
11072
|
});
|
|
10924
11073
|
}
|
|
10925
|
-
return bridgeRelayerMint(
|
|
11074
|
+
return bridgeRelayerMint({
|
|
11075
|
+
params,
|
|
11076
|
+
provider
|
|
11077
|
+
}, context);
|
|
10926
11078
|
} : async (params, provider, context)=>{
|
|
10927
11079
|
if (!context?.attestationData) {
|
|
10928
11080
|
throw new KitError({
|
|
@@ -12131,7 +12283,7 @@ const mockAttestationMessage = {
|
|
|
12131
12283
|
return step;
|
|
12132
12284
|
}
|
|
12133
12285
|
|
|
12134
|
-
var version = "1.
|
|
12286
|
+
var version = "1.9.0";
|
|
12135
12287
|
var pkg = {
|
|
12136
12288
|
version: version};
|
|
12137
12289
|
|
|
@@ -13157,6 +13309,39 @@ var pkg = {
|
|
|
13157
13309
|
}
|
|
13158
13310
|
}
|
|
13159
13311
|
|
|
13312
|
+
function isPlainObject(value) {
|
|
13313
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
13314
|
+
return false;
|
|
13315
|
+
}
|
|
13316
|
+
const prototype = Object.getPrototypeOf(value);
|
|
13317
|
+
return prototype === Object.prototype || prototype === null;
|
|
13318
|
+
}
|
|
13319
|
+
function assertHeadersConfig(headers, field) {
|
|
13320
|
+
if (headers === undefined) {
|
|
13321
|
+
return;
|
|
13322
|
+
}
|
|
13323
|
+
if (!isPlainObject(headers)) {
|
|
13324
|
+
throw createValidationFailedError(field, headers, `${field} must be a plain object with string header values when provided`);
|
|
13325
|
+
}
|
|
13326
|
+
for (const [name, value] of Object.entries(headers)){
|
|
13327
|
+
if (typeof value !== 'string') {
|
|
13328
|
+
throw createValidationFailedError(`${field}.${name}`, value, 'header values must be strings');
|
|
13329
|
+
}
|
|
13330
|
+
}
|
|
13331
|
+
}
|
|
13332
|
+
function assertCCTPV2Config(config) {
|
|
13333
|
+
if (!isPlainObject(config)) {
|
|
13334
|
+
throw createValidationFailedError('config', config, 'config must be a plain object when provided');
|
|
13335
|
+
}
|
|
13336
|
+
assertHeadersConfig(config['headers'], 'config.headers');
|
|
13337
|
+
const attestation = config['attestation'];
|
|
13338
|
+
if (attestation !== undefined) {
|
|
13339
|
+
if (!isPlainObject(attestation)) {
|
|
13340
|
+
throw createValidationFailedError('config.attestation', attestation, 'config.attestation must be a plain object when provided');
|
|
13341
|
+
}
|
|
13342
|
+
assertHeadersConfig(attestation['headers'], 'config.attestation.headers');
|
|
13343
|
+
}
|
|
13344
|
+
}
|
|
13160
13345
|
/**
|
|
13161
13346
|
* All chains that are supported by the CCTP v2 provider.
|
|
13162
13347
|
*
|
|
@@ -13225,9 +13410,38 @@ var pkg = {
|
|
|
13225
13410
|
* @param config - Optional configuration overrides for the provider
|
|
13226
13411
|
*/ constructor(config = {}){
|
|
13227
13412
|
super();
|
|
13413
|
+
assertCCTPV2Config(config);
|
|
13228
13414
|
this.config = config;
|
|
13229
13415
|
}
|
|
13230
13416
|
/**
|
|
13417
|
+
* Resolves the effective polling configuration for an attestation request.
|
|
13418
|
+
*
|
|
13419
|
+
* Precedence (lowest to highest): provider `config.attestation`, then the
|
|
13420
|
+
* per-call `config`. Headers merge independently across
|
|
13421
|
+
* `config.attestation.headers`, the provider-level `config.headers`, and any
|
|
13422
|
+
* per-call `config.headers`, so a more specific header augments rather than
|
|
13423
|
+
* replaces the broader ones. The `headers` key is omitted entirely when no
|
|
13424
|
+
* headers are configured, leaving the attestation fetchers' defaults intact.
|
|
13425
|
+
*
|
|
13426
|
+
* @param config - Optional per-call polling configuration overrides
|
|
13427
|
+
* @returns The merged polling configuration passed to the attestation fetchers
|
|
13428
|
+
*/ resolveAttestationConfig(config) {
|
|
13429
|
+
const headers = {
|
|
13430
|
+
...this.config?.attestation?.headers,
|
|
13431
|
+
...this.config?.headers,
|
|
13432
|
+
...config?.headers
|
|
13433
|
+
};
|
|
13434
|
+
// Polling fields follow normal precedence; headers are merged separately
|
|
13435
|
+
// below so narrower config layers augment rather than replace broader ones.
|
|
13436
|
+
return {
|
|
13437
|
+
...this.config?.attestation,
|
|
13438
|
+
...config,
|
|
13439
|
+
...Object.keys(headers).length > 0 ? {
|
|
13440
|
+
headers
|
|
13441
|
+
} : {}
|
|
13442
|
+
};
|
|
13443
|
+
}
|
|
13444
|
+
/**
|
|
13231
13445
|
* Execute a cross-chain USDC bridge operation using the CCTP v2 protocol.
|
|
13232
13446
|
*
|
|
13233
13447
|
* This method orchestrates the complete CCTP v2 bridge flow including validation,
|
|
@@ -13653,11 +13867,7 @@ var pkg = {
|
|
|
13653
13867
|
*/ async fetchAttestation(source, transactionHash, config) {
|
|
13654
13868
|
assertCCTPv2WalletContext(source);
|
|
13655
13869
|
try {
|
|
13656
|
-
|
|
13657
|
-
const effectiveConfig = {
|
|
13658
|
-
...this.config?.attestation,
|
|
13659
|
-
...config
|
|
13660
|
-
};
|
|
13870
|
+
const effectiveConfig = this.resolveAttestationConfig(config);
|
|
13661
13871
|
const response = await fetchAttestation(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
|
|
13662
13872
|
const message = response.messages[0];
|
|
13663
13873
|
if (!message) {
|
|
@@ -13674,6 +13884,49 @@ var pkg = {
|
|
|
13674
13884
|
}
|
|
13675
13885
|
}
|
|
13676
13886
|
/**
|
|
13887
|
+
* Polls attestation data until Circle's relayer mint transaction is confirmed.
|
|
13888
|
+
*
|
|
13889
|
+
* This method is used by forwarded transfers. It polls the same Iris
|
|
13890
|
+
* attestation endpoint as {@link CCTPV2BridgingProvider.fetchAttestation},
|
|
13891
|
+
* but waits for a completed relayer forward state and returns the attestation
|
|
13892
|
+
* message containing `forwardTxHash`.
|
|
13893
|
+
*
|
|
13894
|
+
* @typeParam TFromAdapterCapabilities - The type representing the capabilities of the source adapter
|
|
13895
|
+
* @param source - The source wallet context containing the chain definition and wallet address
|
|
13896
|
+
* @param transactionHash - The transaction hash of the burn operation
|
|
13897
|
+
* @param config - Optional polling configuration overrides for timeout, retries, delay, and headers
|
|
13898
|
+
* @returns A promise that resolves to the attestation message with `forwardTxHash`
|
|
13899
|
+
* @throws KitError If the relayer forward fails, the response is invalid, or polling times out
|
|
13900
|
+
*
|
|
13901
|
+
* @example
|
|
13902
|
+
* ```typescript
|
|
13903
|
+
* import { CCTPV2BridgingProvider } from '@circle-fin/provider-cctp-v2'
|
|
13904
|
+
* import { Chains } from '@core/chains'
|
|
13905
|
+
*
|
|
13906
|
+
* const provider = new CCTPV2BridgingProvider({
|
|
13907
|
+
* headers: { 'X-Partner-UUID': '00000000-0000-0000-0000-000000000000' },
|
|
13908
|
+
* })
|
|
13909
|
+
*
|
|
13910
|
+
* const attestation = await provider.fetchRelayerMint(
|
|
13911
|
+
* {
|
|
13912
|
+
* adapter,
|
|
13913
|
+
* chain: Chains.EthereumSepolia,
|
|
13914
|
+
* address: '0x1234...',
|
|
13915
|
+
* },
|
|
13916
|
+
* '0xabc123...',
|
|
13917
|
+
* )
|
|
13918
|
+
*
|
|
13919
|
+
* console.log('Relayer mint tx:', attestation.forwardTxHash)
|
|
13920
|
+
* ```
|
|
13921
|
+
*/ async fetchRelayerMint(source, transactionHash, config) {
|
|
13922
|
+
assertCCTPv2WalletContext(source);
|
|
13923
|
+
if (typeof transactionHash !== 'string' || transactionHash.trim() === '') {
|
|
13924
|
+
throw createValidationFailedError('transactionHash', transactionHash, 'transactionHash must be a non-empty string');
|
|
13925
|
+
}
|
|
13926
|
+
const effectiveConfig = this.resolveAttestationConfig(config);
|
|
13927
|
+
return await fetchRelayerMint(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
|
|
13928
|
+
}
|
|
13929
|
+
/**
|
|
13677
13930
|
* Requests a fresh attestation for an expired attestation.
|
|
13678
13931
|
*
|
|
13679
13932
|
* This method is used when the original attestation has expired before the mint
|
|
@@ -13727,11 +13980,7 @@ var pkg = {
|
|
|
13727
13980
|
throw new Error('Failed to re-attest: Invalid transaction hash');
|
|
13728
13981
|
}
|
|
13729
13982
|
try {
|
|
13730
|
-
|
|
13731
|
-
const effectiveConfig = {
|
|
13732
|
-
...this.config?.attestation,
|
|
13733
|
-
...config
|
|
13734
|
-
};
|
|
13983
|
+
const effectiveConfig = this.resolveAttestationConfig(config);
|
|
13735
13984
|
// Step 1: Get existing attestation data to extract nonce
|
|
13736
13985
|
const existingAttestation = await fetchAttestationWithoutStatusCheck(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
|
|
13737
13986
|
const nonce = existingAttestation.messages[0]?.eventNonce;
|