@circle-fin/provider-cctp-v2 1.8.5 → 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 +38 -0
- package/README.md +5 -5
- package/index.cjs +404 -169
- package/index.d.cts +103 -0
- package/index.d.mts +103 -0
- package/index.d.ts +103 -0
- package/index.mjs +404 -169
- package/package.json +3 -1
package/index.cjs
CHANGED
|
@@ -35,6 +35,7 @@ var units = require('@ethersproject/units');
|
|
|
35
35
|
var bytes = require('@ethersproject/bytes');
|
|
36
36
|
var address = require('@ethersproject/address');
|
|
37
37
|
var bs58 = require('bs58');
|
|
38
|
+
require('@ethersproject/abi');
|
|
38
39
|
|
|
39
40
|
function _interopDefault (e) { return e && e.__esModule ? e.default : e; }
|
|
40
41
|
|
|
@@ -84,6 +85,8 @@ var bs58__default = /*#__PURE__*/_interopDefault(bs58);
|
|
|
84
85
|
Blockchain["Celo_Alfajores_Testnet"] = "Celo_Alfajores_Testnet";
|
|
85
86
|
Blockchain["Codex"] = "Codex";
|
|
86
87
|
Blockchain["Codex_Testnet"] = "Codex_Testnet";
|
|
88
|
+
Blockchain["Cronos"] = "Cronos";
|
|
89
|
+
Blockchain["Cronos_Testnet"] = "Cronos_Testnet";
|
|
87
90
|
Blockchain["Edge"] = "Edge";
|
|
88
91
|
Blockchain["Edge_Testnet"] = "Edge_Testnet";
|
|
89
92
|
Blockchain["Ethereum"] = "Ethereum";
|
|
@@ -166,6 +169,7 @@ var BridgeChain;
|
|
|
166
169
|
BridgeChain["Avalanche"] = "Avalanche";
|
|
167
170
|
BridgeChain["Base"] = "Base";
|
|
168
171
|
BridgeChain["Codex"] = "Codex";
|
|
172
|
+
BridgeChain["Cronos"] = "Cronos";
|
|
169
173
|
BridgeChain["Edge"] = "Edge";
|
|
170
174
|
BridgeChain["Ethereum"] = "Ethereum";
|
|
171
175
|
BridgeChain["HyperEVM"] = "HyperEVM";
|
|
@@ -190,6 +194,7 @@ var BridgeChain;
|
|
|
190
194
|
BridgeChain["Avalanche_Fuji"] = "Avalanche_Fuji";
|
|
191
195
|
BridgeChain["Base_Sepolia"] = "Base_Sepolia";
|
|
192
196
|
BridgeChain["Codex_Testnet"] = "Codex_Testnet";
|
|
197
|
+
BridgeChain["Cronos_Testnet"] = "Cronos_Testnet";
|
|
193
198
|
BridgeChain["Edge_Testnet"] = "Edge_Testnet";
|
|
194
199
|
BridgeChain["Ethereum_Sepolia"] = "Ethereum_Sepolia";
|
|
195
200
|
BridgeChain["HyperEVM_Testnet"] = "HyperEVM_Testnet";
|
|
@@ -1235,6 +1240,96 @@ var EarnChain;
|
|
|
1235
1240
|
}
|
|
1236
1241
|
});
|
|
1237
1242
|
|
|
1243
|
+
/**
|
|
1244
|
+
* Cronos Mainnet chain definition
|
|
1245
|
+
* @remarks
|
|
1246
|
+
* This represents the official production network for the Cronos blockchain.
|
|
1247
|
+
* Cronos is an EVM-compatible blockchain.
|
|
1248
|
+
*/ const Cronos = defineChain({
|
|
1249
|
+
type: 'evm',
|
|
1250
|
+
chain: Blockchain.Cronos,
|
|
1251
|
+
name: 'Cronos',
|
|
1252
|
+
title: 'Cronos Mainnet',
|
|
1253
|
+
nativeCurrency: {
|
|
1254
|
+
name: 'Cronos',
|
|
1255
|
+
symbol: 'CRO',
|
|
1256
|
+
decimals: 18
|
|
1257
|
+
},
|
|
1258
|
+
chainId: 25,
|
|
1259
|
+
isTestnet: false,
|
|
1260
|
+
explorerUrl: 'https://cronoscan.com/tx/{hash}',
|
|
1261
|
+
rpcEndpoints: [
|
|
1262
|
+
'https://evm.cronos.org'
|
|
1263
|
+
],
|
|
1264
|
+
eurcAddress: '0xA6dE01a2d62C6B5f3525d768f34d276652C554c8',
|
|
1265
|
+
usdcAddress: '0x3D7F2C478aAfdB65542BCB44bCeeC05849999d2D',
|
|
1266
|
+
usdtAddress: null,
|
|
1267
|
+
cctp: {
|
|
1268
|
+
domain: 32,
|
|
1269
|
+
contracts: {
|
|
1270
|
+
v2: {
|
|
1271
|
+
type: 'split',
|
|
1272
|
+
tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
|
|
1273
|
+
messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
|
|
1274
|
+
confirmations: 1,
|
|
1275
|
+
fastConfirmations: 1
|
|
1276
|
+
}
|
|
1277
|
+
},
|
|
1278
|
+
forwarderSupported: {
|
|
1279
|
+
source: false,
|
|
1280
|
+
destination: false
|
|
1281
|
+
}
|
|
1282
|
+
},
|
|
1283
|
+
kitContracts: {
|
|
1284
|
+
bridge: BRIDGE_CONTRACT_EVM_MAINNET
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* Cronos Testnet chain definition
|
|
1290
|
+
* @remarks
|
|
1291
|
+
* This represents the official test network for the Cronos blockchain.
|
|
1292
|
+
* Cronos is an EVM-compatible blockchain.
|
|
1293
|
+
*/ const CronosTestnet = defineChain({
|
|
1294
|
+
type: 'evm',
|
|
1295
|
+
chain: Blockchain.Cronos_Testnet,
|
|
1296
|
+
name: 'Cronos Testnet',
|
|
1297
|
+
title: 'Cronos Testnet',
|
|
1298
|
+
nativeCurrency: {
|
|
1299
|
+
name: 'CRO',
|
|
1300
|
+
symbol: 'tCRO',
|
|
1301
|
+
decimals: 18
|
|
1302
|
+
},
|
|
1303
|
+
chainId: 338,
|
|
1304
|
+
isTestnet: true,
|
|
1305
|
+
explorerUrl: 'https://explorer.cronos.org/testnet/tx/{hash}',
|
|
1306
|
+
rpcEndpoints: [
|
|
1307
|
+
'https://evm-t3.cronos.org'
|
|
1308
|
+
],
|
|
1309
|
+
eurcAddress: '0x31f7538adb53cF16350e6B0c89d03D91b7D12c46',
|
|
1310
|
+
usdcAddress: '0xEb33dc5fac03833e132593659e1dE7256aB59794',
|
|
1311
|
+
usdtAddress: null,
|
|
1312
|
+
cctp: {
|
|
1313
|
+
domain: 32,
|
|
1314
|
+
contracts: {
|
|
1315
|
+
v2: {
|
|
1316
|
+
type: 'split',
|
|
1317
|
+
tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
|
|
1318
|
+
messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
|
|
1319
|
+
confirmations: 1,
|
|
1320
|
+
fastConfirmations: 1
|
|
1321
|
+
}
|
|
1322
|
+
},
|
|
1323
|
+
forwarderSupported: {
|
|
1324
|
+
source: false,
|
|
1325
|
+
destination: false
|
|
1326
|
+
}
|
|
1327
|
+
},
|
|
1328
|
+
kitContracts: {
|
|
1329
|
+
bridge: BRIDGE_CONTRACT_EVM_TESTNET
|
|
1330
|
+
}
|
|
1331
|
+
});
|
|
1332
|
+
|
|
1238
1333
|
/**
|
|
1239
1334
|
* Edge Mainnet chain definition
|
|
1240
1335
|
* @remarks
|
|
@@ -3586,6 +3681,8 @@ var Chains = {
|
|
|
3586
3681
|
CeloAlfajoresTestnet: CeloAlfajoresTestnet,
|
|
3587
3682
|
Codex: Codex,
|
|
3588
3683
|
CodexTestnet: CodexTestnet,
|
|
3684
|
+
Cronos: Cronos,
|
|
3685
|
+
CronosTestnet: CronosTestnet,
|
|
3589
3686
|
Edge: Edge,
|
|
3590
3687
|
EdgeTestnet: EdgeTestnet,
|
|
3591
3688
|
Ethereum: Ethereum,
|
|
@@ -7360,6 +7457,7 @@ class KitError extends Error {
|
|
|
7360
7457
|
[Blockchain.Base]: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
|
|
7361
7458
|
[Blockchain.Celo]: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C',
|
|
7362
7459
|
[Blockchain.Codex]: '0xd996633a415985DBd7D6D12f4A4343E31f5037cf',
|
|
7460
|
+
[Blockchain.Cronos]: '0x3D7F2C478aAfdB65542BCB44bCeeC05849999d2D',
|
|
7363
7461
|
[Blockchain.Edge]: '0x98d2919b9A214E6Fa5384AC81E6864bA686Ad74c',
|
|
7364
7462
|
[Blockchain.Ethereum]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
|
7365
7463
|
[Blockchain.Hedera]: '0.0.456858',
|
|
@@ -7393,6 +7491,7 @@ class KitError extends Error {
|
|
|
7393
7491
|
[Blockchain.Avalanche_Fuji]: '0x5425890298aed601595a70AB815c96711a31Bc65',
|
|
7394
7492
|
[Blockchain.Base_Sepolia]: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
|
|
7395
7493
|
[Blockchain.Codex_Testnet]: '0x6d7f141b6819C2c9CC2f818e6ad549E7Ca090F8f',
|
|
7494
|
+
[Blockchain.Cronos_Testnet]: '0xEb33dc5fac03833e132593659e1dE7256aB59794',
|
|
7396
7495
|
[Blockchain.Edge_Testnet]: '0x2d9F7CAD728051AA35Ecdc472a14cf8cDF5CFD6B',
|
|
7397
7496
|
[Blockchain.Ethereum_Sepolia]: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',
|
|
7398
7497
|
[Blockchain.Hedera_Testnet]: '0.0.429274',
|
|
@@ -7465,6 +7564,7 @@ class KitError extends Error {
|
|
|
7465
7564
|
// =========================================================================
|
|
7466
7565
|
[Blockchain.Avalanche]: '0xc891EB4cbdEFf6e073e859e987815Ed1505c2ACD',
|
|
7467
7566
|
[Blockchain.Base]: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42',
|
|
7567
|
+
[Blockchain.Cronos]: '0xA6dE01a2d62C6B5f3525d768f34d276652C554c8',
|
|
7468
7568
|
[Blockchain.Ethereum]: '0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c',
|
|
7469
7569
|
[Blockchain.Solana]: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr',
|
|
7470
7570
|
[Blockchain.World_Chain]: '0x1C60ba0A0eD1019e8Eb035E6daF4155A5cE2380B',
|
|
@@ -7473,6 +7573,7 @@ class KitError extends Error {
|
|
|
7473
7573
|
// =========================================================================
|
|
7474
7574
|
[Blockchain.Arc_Testnet]: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
|
|
7475
7575
|
[Blockchain.Base_Sepolia]: '0x808456652fdb597867f38412077A9182bf77359F',
|
|
7576
|
+
[Blockchain.Cronos_Testnet]: '0x31f7538adb53cF16350e6B0c89d03D91b7D12c46',
|
|
7476
7577
|
[Blockchain.Ethereum_Sepolia]: '0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4'
|
|
7477
7578
|
}
|
|
7478
7579
|
};
|
|
@@ -9182,6 +9283,15 @@ const CUSTOM_BURN_GAS_ESTIMATE_EVM = 201_525n // p99 and max are same here: 201_
|
|
|
9182
9283
|
;
|
|
9183
9284
|
const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_839n) / 2 = 237_401n
|
|
9184
9285
|
;
|
|
9286
|
+
// Hard execution caps: observed max + ~30% buffer, used as gasLimit overrides on
|
|
9287
|
+
// chains whose eth_estimateGas under-reports (e.g. Cronos EIP-7623 calldata floor).
|
|
9288
|
+
// Kept separate from the fee-estimate averages above.
|
|
9289
|
+
const APPROVE_GAS_LIMIT_EVM = 100_000n // ERC-20 approve observed max ~46k
|
|
9290
|
+
;
|
|
9291
|
+
const DEPOSIT_FOR_BURN_GAS_LIMIT_EVM = 300_000n // observed max 226_506 + ~30%
|
|
9292
|
+
;
|
|
9293
|
+
const RECEIVE_MESSAGE_GAS_LIMIT_EVM = 400_000n // observed max 310_839 + ~30%
|
|
9294
|
+
;
|
|
9185
9295
|
/**
|
|
9186
9296
|
* The minimum finality threshold for CCTPv2 transfers.
|
|
9187
9297
|
*
|
|
@@ -9210,6 +9320,27 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9210
9320
|
'Content-Type': 'application/json'
|
|
9211
9321
|
}
|
|
9212
9322
|
};
|
|
9323
|
+
/**
|
|
9324
|
+
* Merges caller-provided polling overrides on top of {@link DEFAULT_CONFIG}.
|
|
9325
|
+
*
|
|
9326
|
+
* Headers are merged independently so caller-supplied headers augment the
|
|
9327
|
+
* defaults (such as `Content-Type`) rather than replacing them wholesale.
|
|
9328
|
+
*
|
|
9329
|
+
* @param config - Caller-provided polling configuration overrides
|
|
9330
|
+
* @param internalDefaults - Internal defaults applied before `config` (for example a
|
|
9331
|
+
* reduced `maxRetries` for one-shot requests); `config` still wins on conflict
|
|
9332
|
+
* @returns The effective polling configuration
|
|
9333
|
+
* @internal
|
|
9334
|
+
*/ const mergeAttestationConfig = (config, internalDefaults = {})=>({
|
|
9335
|
+
...DEFAULT_CONFIG,
|
|
9336
|
+
...internalDefaults,
|
|
9337
|
+
...config,
|
|
9338
|
+
headers: {
|
|
9339
|
+
...DEFAULT_CONFIG.headers,
|
|
9340
|
+
...internalDefaults.headers,
|
|
9341
|
+
...config.headers
|
|
9342
|
+
}
|
|
9343
|
+
});
|
|
9213
9344
|
/**
|
|
9214
9345
|
* Type guard that verifies if an unknown value matches the AttestationMessage shape
|
|
9215
9346
|
* and has all required properties.
|
|
@@ -9356,10 +9487,7 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9356
9487
|
* ```
|
|
9357
9488
|
*/ const fetchAttestation = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
9358
9489
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
9359
|
-
const effectiveConfig =
|
|
9360
|
-
...DEFAULT_CONFIG,
|
|
9361
|
-
...config
|
|
9362
|
-
};
|
|
9490
|
+
const effectiveConfig = mergeAttestationConfig(config);
|
|
9363
9491
|
return await pollApiGet(url, isAttestationResponse, effectiveConfig);
|
|
9364
9492
|
};
|
|
9365
9493
|
/**
|
|
@@ -9402,11 +9530,9 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9402
9530
|
*/ const fetchAttestationWithoutStatusCheck = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
9403
9531
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
9404
9532
|
// Use minimal retries since we're just fetching existing data
|
|
9405
|
-
const effectiveConfig = {
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
...config
|
|
9409
|
-
};
|
|
9533
|
+
const effectiveConfig = mergeAttestationConfig(config, {
|
|
9534
|
+
maxRetries: 3
|
|
9535
|
+
});
|
|
9410
9536
|
return await pollApiGet(url, isAttestationResponseWithoutStatusCheck, effectiveConfig);
|
|
9411
9537
|
};
|
|
9412
9538
|
/**
|
|
@@ -9466,10 +9592,7 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9466
9592
|
* ```
|
|
9467
9593
|
*/ const fetchReAttestedAttestation = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
9468
9594
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
9469
|
-
const effectiveConfig =
|
|
9470
|
-
...DEFAULT_CONFIG,
|
|
9471
|
-
...config
|
|
9472
|
-
};
|
|
9595
|
+
const effectiveConfig = mergeAttestationConfig(config);
|
|
9473
9596
|
return await pollApiGet(url, isReAttestedAttestationResponse, effectiveConfig);
|
|
9474
9597
|
};
|
|
9475
9598
|
/**
|
|
@@ -9535,14 +9658,139 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9535
9658
|
*/ const requestReAttestation = async (nonce, isTestnet, config = {})=>{
|
|
9536
9659
|
const url = buildReAttestUrl(nonce, isTestnet);
|
|
9537
9660
|
// Use minimal retries since we're just submitting a request, not polling for state
|
|
9538
|
-
const effectiveConfig = {
|
|
9539
|
-
|
|
9540
|
-
|
|
9541
|
-
...config
|
|
9542
|
-
};
|
|
9661
|
+
const effectiveConfig = mergeAttestationConfig(config, {
|
|
9662
|
+
maxRetries: 3
|
|
9663
|
+
});
|
|
9543
9664
|
return await pollApiPost(url, {}, isReAttestationResponse, effectiveConfig);
|
|
9544
9665
|
};
|
|
9545
9666
|
|
|
9667
|
+
/**
|
|
9668
|
+
* Type guard that checks if the relayer has confirmed the mint transaction.
|
|
9669
|
+
*
|
|
9670
|
+
* This function validates that:
|
|
9671
|
+
* 1. The response has valid AttestationResponse structure
|
|
9672
|
+
* 2. At least one message has forwardState === 'CONFIRMED' (or 'COMPLETE') and a valid forwardTxHash
|
|
9673
|
+
*
|
|
9674
|
+
* If forwardState is 'FAILED', throws a non-retryable KitError.
|
|
9675
|
+
* If forwardState is 'PENDING' or not present, throws a RETRYABLE KitError to continue polling.
|
|
9676
|
+
*
|
|
9677
|
+
* @param obj - The value to check, typically a parsed JSON response
|
|
9678
|
+
* @returns True if the relayer has confirmed the mint
|
|
9679
|
+
* @throws {KitError} With FATAL recoverability if structure is invalid
|
|
9680
|
+
* @throws {KitError} With RESUMABLE recoverability if forwardState is 'FAILED'
|
|
9681
|
+
* @throws {KitError} With RETRYABLE recoverability if still pending
|
|
9682
|
+
* @internal
|
|
9683
|
+
*/ const isRelayerMintConfirmed = (obj)=>{
|
|
9684
|
+
// First check if the structure is valid
|
|
9685
|
+
if (!hasValidAttestationStructure(obj)) {
|
|
9686
|
+
throw new KitError({
|
|
9687
|
+
...InputError.VALIDATION_FAILED,
|
|
9688
|
+
recoverability: 'FATAL',
|
|
9689
|
+
message: 'Invalid attestation response structure from IRIS API.'
|
|
9690
|
+
});
|
|
9691
|
+
}
|
|
9692
|
+
// Find the first message (typically there's only one)
|
|
9693
|
+
const message = obj.messages[0];
|
|
9694
|
+
if (!message) {
|
|
9695
|
+
throw new KitError({
|
|
9696
|
+
...InputError.VALIDATION_FAILED,
|
|
9697
|
+
recoverability: 'FATAL',
|
|
9698
|
+
message: 'No attestation messages found in IRIS API response.'
|
|
9699
|
+
});
|
|
9700
|
+
}
|
|
9701
|
+
// Check for FAILED state - this is a permanent failure
|
|
9702
|
+
if (message.forwardState === 'FAILED') {
|
|
9703
|
+
throw new KitError({
|
|
9704
|
+
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
9705
|
+
recoverability: 'RESUMABLE',
|
|
9706
|
+
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.',
|
|
9707
|
+
cause: {
|
|
9708
|
+
trace: {
|
|
9709
|
+
eventNonce: message.eventNonce,
|
|
9710
|
+
attestation: message.attestation,
|
|
9711
|
+
message: message.message
|
|
9712
|
+
}
|
|
9713
|
+
}
|
|
9714
|
+
});
|
|
9715
|
+
}
|
|
9716
|
+
// Check if mint is confirmed (or complete) with a valid transaction hash
|
|
9717
|
+
// We accept both CONFIRMED and COMPLETE since COMPLETE implies CONFIRMED
|
|
9718
|
+
if ((message.forwardState === 'CONFIRMED' || message.forwardState === 'COMPLETE') && typeof message.forwardTxHash === 'string' && message.forwardTxHash.trim().length > 0) {
|
|
9719
|
+
return true;
|
|
9720
|
+
}
|
|
9721
|
+
// Still pending or not yet processed - throw RETRYABLE error to continue polling
|
|
9722
|
+
throw new KitError({
|
|
9723
|
+
...NetworkError.RELAYER_PENDING,
|
|
9724
|
+
recoverability: 'RETRYABLE',
|
|
9725
|
+
message: 'Relayer mint not ready. Waiting for confirmation.'
|
|
9726
|
+
});
|
|
9727
|
+
};
|
|
9728
|
+
/**
|
|
9729
|
+
* Polls the attestation API until the relayer's mint transaction is confirmed.
|
|
9730
|
+
*
|
|
9731
|
+
* This function is used when `useForwarder` is enabled. Instead of the user
|
|
9732
|
+
* submitting the mint transaction, Circle's Orbit relayer handles it automatically.
|
|
9733
|
+
* This function polls until the relayer has submitted and confirmed the mint transaction.
|
|
9734
|
+
*
|
|
9735
|
+
* @remarks
|
|
9736
|
+
* - Uses a 20-minute timeout by default (600 retries × 2 seconds)
|
|
9737
|
+
* - Throws immediately if `forwardState` is 'FAILED'
|
|
9738
|
+
* - Waits for `forwardState` to be 'CONFIRMED' or 'COMPLETE' (COMPLETE implies CONFIRMED)
|
|
9739
|
+
* - Returns the attestation message with `forwardTxHash` populated
|
|
9740
|
+
*
|
|
9741
|
+
* @param sourceDomainId - The CCTP domain ID of the source chain
|
|
9742
|
+
* @param transactionHash - The transaction hash of the burn operation
|
|
9743
|
+
* @param isTestnet - Whether this is for a testnet chain (true) or mainnet (false)
|
|
9744
|
+
* @param config - Optional configuration overrides for polling behavior
|
|
9745
|
+
* @returns The attestation message with confirmed forwardTxHash
|
|
9746
|
+
* @throws {KitError} With code 'NETWORK_RELAYER_FORWARD_FAILED' if relayer failed
|
|
9747
|
+
* @throws {KitError} If timeout is reached while still pending
|
|
9748
|
+
*
|
|
9749
|
+
* @example
|
|
9750
|
+
* ```typescript
|
|
9751
|
+
* const attestation = await fetchRelayerMint(0, '0xabc...', false)
|
|
9752
|
+
* console.log('Relayer mint tx:', attestation.forwardTxHash)
|
|
9753
|
+
* ```
|
|
9754
|
+
*/ const fetchRelayerMint = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
9755
|
+
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
9756
|
+
const effectiveConfig = mergeAttestationConfig(config);
|
|
9757
|
+
let response;
|
|
9758
|
+
try {
|
|
9759
|
+
response = await pollApiGet(url, isRelayerMintConfirmed, effectiveConfig);
|
|
9760
|
+
} catch (error) {
|
|
9761
|
+
// Enrich RELAYER_FORWARD_FAILED errors with the burn transaction hash
|
|
9762
|
+
if (error instanceof KitError && error.name === 'NETWORK_RELAYER_FORWARD_FAILED') {
|
|
9763
|
+
throw new KitError({
|
|
9764
|
+
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
9765
|
+
recoverability: error.recoverability,
|
|
9766
|
+
message: error.message,
|
|
9767
|
+
cause: {
|
|
9768
|
+
...error.cause,
|
|
9769
|
+
trace: {
|
|
9770
|
+
...error.cause?.trace,
|
|
9771
|
+
burnTxHash: transactionHash
|
|
9772
|
+
}
|
|
9773
|
+
}
|
|
9774
|
+
});
|
|
9775
|
+
}
|
|
9776
|
+
throw error;
|
|
9777
|
+
}
|
|
9778
|
+
// Return the first message (which should have forwardTxHash)
|
|
9779
|
+
// Note: This check is needed for TypeScript type safety even though
|
|
9780
|
+
// isRelayerMintConfirmed validates messages[0] exists. The type guard
|
|
9781
|
+
// narrows the type at the call site, but TypeScript can't infer that
|
|
9782
|
+
// the array still has elements after pollApiGet returns.
|
|
9783
|
+
const message = response.messages[0];
|
|
9784
|
+
if (!message) {
|
|
9785
|
+
throw new KitError({
|
|
9786
|
+
...InputError.VALIDATION_FAILED,
|
|
9787
|
+
recoverability: 'FATAL',
|
|
9788
|
+
message: 'No attestation messages found in response after polling.'
|
|
9789
|
+
});
|
|
9790
|
+
}
|
|
9791
|
+
return message;
|
|
9792
|
+
};
|
|
9793
|
+
|
|
9546
9794
|
const assertCCTPv2WalletContextSymbol = Symbol('assertCCTPv2WalletContext');
|
|
9547
9795
|
/**
|
|
9548
9796
|
* Asserts that the provided parameters match the CCTPv2 wallet context interface.
|
|
@@ -10417,6 +10665,8 @@ function hasPendingState(analysis, result) {
|
|
|
10417
10665
|
* - `adapter`: The adapter that will execute the transaction
|
|
10418
10666
|
* - `confirmations`: The number of confirmations to wait for (defaults to 1)
|
|
10419
10667
|
* - `timeout`: The timeout for the request in milliseconds
|
|
10668
|
+
* - `gasLimit`: Optional explicit gas limit (number) forwarded to EVM execute,
|
|
10669
|
+
* bypassing `eth_estimateGas`; ignored for non-EVM requests
|
|
10420
10670
|
* @returns The bridge step with the transaction details and explorer URL
|
|
10421
10671
|
* @throws If the transaction execution fails
|
|
10422
10672
|
*
|
|
@@ -10431,7 +10681,7 @@ function hasPendingState(analysis, result) {
|
|
|
10431
10681
|
* })
|
|
10432
10682
|
* console.log('Transaction hash:', step.txHash)
|
|
10433
10683
|
* ```
|
|
10434
|
-
*/ async function executePreparedChainRequest({ name, request, adapter, chain, confirmations = 1, timeout }) {
|
|
10684
|
+
*/ async function executePreparedChainRequest({ name, request, adapter, chain, confirmations = 1, timeout, gasLimit }) {
|
|
10435
10685
|
const step = {
|
|
10436
10686
|
name,
|
|
10437
10687
|
state: 'pending'
|
|
@@ -10444,7 +10694,9 @@ function hasPendingState(analysis, result) {
|
|
|
10444
10694
|
step.state = 'noop';
|
|
10445
10695
|
return step;
|
|
10446
10696
|
}
|
|
10447
|
-
const txHash = await request.execute(
|
|
10697
|
+
const txHash = request.type === 'evm' && gasLimit !== undefined ? await request.execute({
|
|
10698
|
+
gasLimit
|
|
10699
|
+
}) : await request.execute();
|
|
10448
10700
|
step.txHash = txHash;
|
|
10449
10701
|
const retryOptions = {
|
|
10450
10702
|
isRetryable: (err)=>isRetryableError$1(parseBlockchainError(err, {
|
|
@@ -10516,7 +10768,8 @@ function hasPendingState(analysis, result) {
|
|
|
10516
10768
|
name: 'approve',
|
|
10517
10769
|
adapter: params.source.adapter,
|
|
10518
10770
|
chain: params.source.chain,
|
|
10519
|
-
request: await provider.approve(params.source, approvalAmount)
|
|
10771
|
+
request: await provider.approve(params.source, approvalAmount),
|
|
10772
|
+
gasLimit: Number(APPROVE_GAS_LIMIT_EVM)
|
|
10520
10773
|
});
|
|
10521
10774
|
}
|
|
10522
10775
|
|
|
@@ -10543,7 +10796,8 @@ function hasPendingState(analysis, result) {
|
|
|
10543
10796
|
name: 'burn',
|
|
10544
10797
|
adapter: params.source.adapter,
|
|
10545
10798
|
chain: params.source.chain,
|
|
10546
|
-
request: await provider.burn(params)
|
|
10799
|
+
request: await provider.burn(params),
|
|
10800
|
+
gasLimit: Number(DEPOSIT_FOR_BURN_GAS_LIMIT_EVM)
|
|
10547
10801
|
});
|
|
10548
10802
|
}
|
|
10549
10803
|
|
|
@@ -10629,11 +10883,18 @@ function hasPendingState(analysis, result) {
|
|
|
10629
10883
|
*/ async function bridgeMint({ params, provider }, attestation) {
|
|
10630
10884
|
// Validate attestation message matches transfer params
|
|
10631
10885
|
await assertCCTPv2AttestationParams(attestation, params);
|
|
10886
|
+
const mintRequest = await provider.mint(params.source, params.destination, attestation);
|
|
10632
10887
|
const step = await executePreparedChainRequest({
|
|
10633
10888
|
name: 'mint',
|
|
10634
10889
|
adapter: params.destination.adapter,
|
|
10635
10890
|
chain: params.destination.chain,
|
|
10636
|
-
request:
|
|
10891
|
+
request: mintRequest,
|
|
10892
|
+
// Some chains (e.g. Cronos) enforce an EIP-7623 calldata gas floor that
|
|
10893
|
+
// eth_estimateGas does not account for, returning a below-floor value
|
|
10894
|
+
// without reverting. Pinning to a value above the observed execution max
|
|
10895
|
+
// (310_839) bypasses re-estimation and guarantees we clear both the floor
|
|
10896
|
+
// and the actual execution cost.
|
|
10897
|
+
gasLimit: Number(RECEIVE_MESSAGE_GAS_LIMIT_EVM)
|
|
10637
10898
|
});
|
|
10638
10899
|
// Add forwarded: false for non-relayer mints
|
|
10639
10900
|
return {
|
|
@@ -10671,136 +10932,6 @@ const mockAttestationMessage = {
|
|
|
10671
10932
|
delayReason: null
|
|
10672
10933
|
};
|
|
10673
10934
|
|
|
10674
|
-
/**
|
|
10675
|
-
* Type guard that checks if the relayer has confirmed the mint transaction.
|
|
10676
|
-
*
|
|
10677
|
-
* This function validates that:
|
|
10678
|
-
* 1. The response has valid AttestationResponse structure
|
|
10679
|
-
* 2. At least one message has forwardState === 'CONFIRMED' (or 'COMPLETE') and a valid forwardTxHash
|
|
10680
|
-
*
|
|
10681
|
-
* If forwardState is 'FAILED', throws a non-retryable KitError.
|
|
10682
|
-
* If forwardState is 'PENDING' or not present, throws a RETRYABLE KitError to continue polling.
|
|
10683
|
-
*
|
|
10684
|
-
* @param obj - The value to check, typically a parsed JSON response
|
|
10685
|
-
* @returns True if the relayer has confirmed the mint
|
|
10686
|
-
* @throws {KitError} With FATAL recoverability if structure is invalid
|
|
10687
|
-
* @throws {KitError} With RESUMABLE recoverability if forwardState is 'FAILED'
|
|
10688
|
-
* @throws {KitError} With RETRYABLE recoverability if still pending
|
|
10689
|
-
* @internal
|
|
10690
|
-
*/ const isRelayerMintConfirmed = (obj)=>{
|
|
10691
|
-
// First check if the structure is valid
|
|
10692
|
-
if (!hasValidAttestationStructure(obj)) {
|
|
10693
|
-
throw new KitError({
|
|
10694
|
-
...InputError.VALIDATION_FAILED,
|
|
10695
|
-
recoverability: 'FATAL',
|
|
10696
|
-
message: 'Invalid attestation response structure from IRIS API.'
|
|
10697
|
-
});
|
|
10698
|
-
}
|
|
10699
|
-
// Find the first message (typically there's only one)
|
|
10700
|
-
const message = obj.messages[0];
|
|
10701
|
-
if (!message) {
|
|
10702
|
-
throw new KitError({
|
|
10703
|
-
...InputError.VALIDATION_FAILED,
|
|
10704
|
-
recoverability: 'FATAL',
|
|
10705
|
-
message: 'No attestation messages found in IRIS API response.'
|
|
10706
|
-
});
|
|
10707
|
-
}
|
|
10708
|
-
// Check for FAILED state - this is a permanent failure
|
|
10709
|
-
if (message.forwardState === 'FAILED') {
|
|
10710
|
-
throw new KitError({
|
|
10711
|
-
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
10712
|
-
recoverability: 'RESUMABLE',
|
|
10713
|
-
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.',
|
|
10714
|
-
cause: {
|
|
10715
|
-
trace: {
|
|
10716
|
-
eventNonce: message.eventNonce,
|
|
10717
|
-
attestation: message.attestation,
|
|
10718
|
-
message: message.message
|
|
10719
|
-
}
|
|
10720
|
-
}
|
|
10721
|
-
});
|
|
10722
|
-
}
|
|
10723
|
-
// Check if mint is confirmed (or complete) with a valid transaction hash
|
|
10724
|
-
// We accept both CONFIRMED and COMPLETE since COMPLETE implies CONFIRMED
|
|
10725
|
-
if ((message.forwardState === 'CONFIRMED' || message.forwardState === 'COMPLETE') && typeof message.forwardTxHash === 'string' && message.forwardTxHash.trim().length > 0) {
|
|
10726
|
-
return true;
|
|
10727
|
-
}
|
|
10728
|
-
// Still pending or not yet processed - throw RETRYABLE error to continue polling
|
|
10729
|
-
throw new KitError({
|
|
10730
|
-
...NetworkError.RELAYER_PENDING,
|
|
10731
|
-
recoverability: 'RETRYABLE',
|
|
10732
|
-
message: 'Relayer mint not ready. Waiting for confirmation.'
|
|
10733
|
-
});
|
|
10734
|
-
};
|
|
10735
|
-
/**
|
|
10736
|
-
* Polls the attestation API until the relayer's mint transaction is confirmed.
|
|
10737
|
-
*
|
|
10738
|
-
* This function is used when `useForwarder` is enabled. Instead of the user
|
|
10739
|
-
* submitting the mint transaction, Circle's Orbit relayer handles it automatically.
|
|
10740
|
-
* This function polls until the relayer has submitted and confirmed the mint transaction.
|
|
10741
|
-
*
|
|
10742
|
-
* @remarks
|
|
10743
|
-
* - Uses a 20-minute timeout by default (600 retries × 2 seconds)
|
|
10744
|
-
* - Throws immediately if `forwardState` is 'FAILED'
|
|
10745
|
-
* - Waits for `forwardState` to be 'CONFIRMED' or 'COMPLETE' (COMPLETE implies CONFIRMED)
|
|
10746
|
-
* - Returns the attestation message with `forwardTxHash` populated
|
|
10747
|
-
*
|
|
10748
|
-
* @param sourceDomainId - The CCTP domain ID of the source chain
|
|
10749
|
-
* @param transactionHash - The transaction hash of the burn operation
|
|
10750
|
-
* @param isTestnet - Whether this is for a testnet chain (true) or mainnet (false)
|
|
10751
|
-
* @param config - Optional configuration overrides for polling behavior
|
|
10752
|
-
* @returns The attestation message with confirmed forwardTxHash
|
|
10753
|
-
* @throws {KitError} With code 'NETWORK_RELAYER_FORWARD_FAILED' if relayer failed
|
|
10754
|
-
* @throws {KitError} If timeout is reached while still pending
|
|
10755
|
-
*
|
|
10756
|
-
* @example
|
|
10757
|
-
* ```typescript
|
|
10758
|
-
* const attestation = await fetchRelayerMint(0, '0xabc...', false)
|
|
10759
|
-
* console.log('Relayer mint tx:', attestation.forwardTxHash)
|
|
10760
|
-
* ```
|
|
10761
|
-
*/ const fetchRelayerMint = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
10762
|
-
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
10763
|
-
const effectiveConfig = {
|
|
10764
|
-
...DEFAULT_CONFIG,
|
|
10765
|
-
...config
|
|
10766
|
-
};
|
|
10767
|
-
let response;
|
|
10768
|
-
try {
|
|
10769
|
-
response = await pollApiGet(url, isRelayerMintConfirmed, effectiveConfig);
|
|
10770
|
-
} catch (error) {
|
|
10771
|
-
// Enrich RELAYER_FORWARD_FAILED errors with the burn transaction hash
|
|
10772
|
-
if (error instanceof KitError && error.name === 'NETWORK_RELAYER_FORWARD_FAILED') {
|
|
10773
|
-
throw new KitError({
|
|
10774
|
-
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
10775
|
-
recoverability: error.recoverability,
|
|
10776
|
-
message: error.message,
|
|
10777
|
-
cause: {
|
|
10778
|
-
...error.cause,
|
|
10779
|
-
trace: {
|
|
10780
|
-
...error.cause?.trace,
|
|
10781
|
-
burnTxHash: transactionHash
|
|
10782
|
-
}
|
|
10783
|
-
}
|
|
10784
|
-
});
|
|
10785
|
-
}
|
|
10786
|
-
throw error;
|
|
10787
|
-
}
|
|
10788
|
-
// Return the first message (which should have forwardTxHash)
|
|
10789
|
-
// Note: This check is needed for TypeScript type safety even though
|
|
10790
|
-
// isRelayerMintConfirmed validates messages[0] exists. The type guard
|
|
10791
|
-
// narrows the type at the call site, but TypeScript can't infer that
|
|
10792
|
-
// the array still has elements after pollApiGet returns.
|
|
10793
|
-
const message = response.messages[0];
|
|
10794
|
-
if (!message) {
|
|
10795
|
-
throw new KitError({
|
|
10796
|
-
...InputError.VALIDATION_FAILED,
|
|
10797
|
-
recoverability: 'FATAL',
|
|
10798
|
-
message: 'No attestation messages found in response after polling.'
|
|
10799
|
-
});
|
|
10800
|
-
}
|
|
10801
|
-
return message;
|
|
10802
|
-
};
|
|
10803
|
-
|
|
10804
10935
|
/**
|
|
10805
10936
|
* Executes the mint step for forwarding mode where Circle's relayer handles the mint.
|
|
10806
10937
|
*
|
|
@@ -10814,6 +10945,7 @@ const mockAttestationMessage = {
|
|
|
10814
10945
|
* immediately after the API confirms the mint.
|
|
10815
10946
|
*
|
|
10816
10947
|
* @param params - The bridge parameters containing source, destination, amount and config
|
|
10948
|
+
* @param provider - The CCTP v2 bridging provider
|
|
10817
10949
|
* @param context - The step context containing burnTxHash from the burn step
|
|
10818
10950
|
* @returns Promise resolving to the bridge step with transaction details
|
|
10819
10951
|
* @throws {KitError} If burnTxHash is not available in context
|
|
@@ -10822,10 +10954,13 @@ const mockAttestationMessage = {
|
|
|
10822
10954
|
*
|
|
10823
10955
|
* @example
|
|
10824
10956
|
* ```typescript
|
|
10825
|
-
* const step = await bridgeRelayerMint(
|
|
10957
|
+
* const step = await bridgeRelayerMint(
|
|
10958
|
+
* { params, provider },
|
|
10959
|
+
* { burnTxHash: '0x...' }
|
|
10960
|
+
* )
|
|
10826
10961
|
* console.log('Relayer mint tx:', step.txHash)
|
|
10827
10962
|
* ```
|
|
10828
|
-
*/ async function bridgeRelayerMint(params, context) {
|
|
10963
|
+
*/ async function bridgeRelayerMint({ params, provider }, context) {
|
|
10829
10964
|
const step = {
|
|
10830
10965
|
name: 'mint',
|
|
10831
10966
|
state: 'pending',
|
|
@@ -10841,7 +10976,7 @@ const mockAttestationMessage = {
|
|
|
10841
10976
|
}
|
|
10842
10977
|
try {
|
|
10843
10978
|
// Poll attestation API until relayer confirms the mint
|
|
10844
|
-
const attestation = await fetchRelayerMint(params.source
|
|
10979
|
+
const attestation = await provider.fetchRelayerMint(params.source, context.burnTxHash);
|
|
10845
10980
|
// Extract the relayer's mint transaction hash
|
|
10846
10981
|
const forwardTxHash = attestation.forwardTxHash;
|
|
10847
10982
|
if (!forwardTxHash) {
|
|
@@ -10935,7 +11070,7 @@ const mockAttestationMessage = {
|
|
|
10935
11070
|
name: 'mint',
|
|
10936
11071
|
// bridgeRelayerMint validates context.burnTxHash internally with a KitError
|
|
10937
11072
|
// bridgeMint requires attestationData which is validated here
|
|
10938
|
-
executor: useForwarder ? async (params,
|
|
11073
|
+
executor: useForwarder ? async (params, provider, context)=>{
|
|
10939
11074
|
if (!context) {
|
|
10940
11075
|
throw new KitError({
|
|
10941
11076
|
...InputError.VALIDATION_FAILED,
|
|
@@ -10943,7 +11078,10 @@ const mockAttestationMessage = {
|
|
|
10943
11078
|
message: 'Step context is required for relayer mint'
|
|
10944
11079
|
});
|
|
10945
11080
|
}
|
|
10946
|
-
return bridgeRelayerMint(
|
|
11081
|
+
return bridgeRelayerMint({
|
|
11082
|
+
params,
|
|
11083
|
+
provider
|
|
11084
|
+
}, context);
|
|
10947
11085
|
} : async (params, provider, context)=>{
|
|
10948
11086
|
if (!context?.attestationData) {
|
|
10949
11087
|
throw new KitError({
|
|
@@ -12152,7 +12290,7 @@ const mockAttestationMessage = {
|
|
|
12152
12290
|
return step;
|
|
12153
12291
|
}
|
|
12154
12292
|
|
|
12155
|
-
var version = "1.
|
|
12293
|
+
var version = "1.9.0";
|
|
12156
12294
|
var pkg = {
|
|
12157
12295
|
version: version};
|
|
12158
12296
|
|
|
@@ -13178,6 +13316,39 @@ var pkg = {
|
|
|
13178
13316
|
}
|
|
13179
13317
|
}
|
|
13180
13318
|
|
|
13319
|
+
function isPlainObject(value) {
|
|
13320
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
13321
|
+
return false;
|
|
13322
|
+
}
|
|
13323
|
+
const prototype = Object.getPrototypeOf(value);
|
|
13324
|
+
return prototype === Object.prototype || prototype === null;
|
|
13325
|
+
}
|
|
13326
|
+
function assertHeadersConfig(headers, field) {
|
|
13327
|
+
if (headers === undefined) {
|
|
13328
|
+
return;
|
|
13329
|
+
}
|
|
13330
|
+
if (!isPlainObject(headers)) {
|
|
13331
|
+
throw createValidationFailedError(field, headers, `${field} must be a plain object with string header values when provided`);
|
|
13332
|
+
}
|
|
13333
|
+
for (const [name, value] of Object.entries(headers)){
|
|
13334
|
+
if (typeof value !== 'string') {
|
|
13335
|
+
throw createValidationFailedError(`${field}.${name}`, value, 'header values must be strings');
|
|
13336
|
+
}
|
|
13337
|
+
}
|
|
13338
|
+
}
|
|
13339
|
+
function assertCCTPV2Config(config) {
|
|
13340
|
+
if (!isPlainObject(config)) {
|
|
13341
|
+
throw createValidationFailedError('config', config, 'config must be a plain object when provided');
|
|
13342
|
+
}
|
|
13343
|
+
assertHeadersConfig(config['headers'], 'config.headers');
|
|
13344
|
+
const attestation = config['attestation'];
|
|
13345
|
+
if (attestation !== undefined) {
|
|
13346
|
+
if (!isPlainObject(attestation)) {
|
|
13347
|
+
throw createValidationFailedError('config.attestation', attestation, 'config.attestation must be a plain object when provided');
|
|
13348
|
+
}
|
|
13349
|
+
assertHeadersConfig(attestation['headers'], 'config.attestation.headers');
|
|
13350
|
+
}
|
|
13351
|
+
}
|
|
13181
13352
|
/**
|
|
13182
13353
|
* All chains that are supported by the CCTP v2 provider.
|
|
13183
13354
|
*
|
|
@@ -13246,9 +13417,38 @@ var pkg = {
|
|
|
13246
13417
|
* @param config - Optional configuration overrides for the provider
|
|
13247
13418
|
*/ constructor(config = {}){
|
|
13248
13419
|
super();
|
|
13420
|
+
assertCCTPV2Config(config);
|
|
13249
13421
|
this.config = config;
|
|
13250
13422
|
}
|
|
13251
13423
|
/**
|
|
13424
|
+
* Resolves the effective polling configuration for an attestation request.
|
|
13425
|
+
*
|
|
13426
|
+
* Precedence (lowest to highest): provider `config.attestation`, then the
|
|
13427
|
+
* per-call `config`. Headers merge independently across
|
|
13428
|
+
* `config.attestation.headers`, the provider-level `config.headers`, and any
|
|
13429
|
+
* per-call `config.headers`, so a more specific header augments rather than
|
|
13430
|
+
* replaces the broader ones. The `headers` key is omitted entirely when no
|
|
13431
|
+
* headers are configured, leaving the attestation fetchers' defaults intact.
|
|
13432
|
+
*
|
|
13433
|
+
* @param config - Optional per-call polling configuration overrides
|
|
13434
|
+
* @returns The merged polling configuration passed to the attestation fetchers
|
|
13435
|
+
*/ resolveAttestationConfig(config) {
|
|
13436
|
+
const headers = {
|
|
13437
|
+
...this.config?.attestation?.headers,
|
|
13438
|
+
...this.config?.headers,
|
|
13439
|
+
...config?.headers
|
|
13440
|
+
};
|
|
13441
|
+
// Polling fields follow normal precedence; headers are merged separately
|
|
13442
|
+
// below so narrower config layers augment rather than replace broader ones.
|
|
13443
|
+
return {
|
|
13444
|
+
...this.config?.attestation,
|
|
13445
|
+
...config,
|
|
13446
|
+
...Object.keys(headers).length > 0 ? {
|
|
13447
|
+
headers
|
|
13448
|
+
} : {}
|
|
13449
|
+
};
|
|
13450
|
+
}
|
|
13451
|
+
/**
|
|
13252
13452
|
* Execute a cross-chain USDC bridge operation using the CCTP v2 protocol.
|
|
13253
13453
|
*
|
|
13254
13454
|
* This method orchestrates the complete CCTP v2 bridge flow including validation,
|
|
@@ -13674,11 +13874,7 @@ var pkg = {
|
|
|
13674
13874
|
*/ async fetchAttestation(source, transactionHash, config) {
|
|
13675
13875
|
assertCCTPv2WalletContext(source);
|
|
13676
13876
|
try {
|
|
13677
|
-
|
|
13678
|
-
const effectiveConfig = {
|
|
13679
|
-
...this.config?.attestation,
|
|
13680
|
-
...config
|
|
13681
|
-
};
|
|
13877
|
+
const effectiveConfig = this.resolveAttestationConfig(config);
|
|
13682
13878
|
const response = await fetchAttestation(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
|
|
13683
13879
|
const message = response.messages[0];
|
|
13684
13880
|
if (!message) {
|
|
@@ -13695,6 +13891,49 @@ var pkg = {
|
|
|
13695
13891
|
}
|
|
13696
13892
|
}
|
|
13697
13893
|
/**
|
|
13894
|
+
* Polls attestation data until Circle's relayer mint transaction is confirmed.
|
|
13895
|
+
*
|
|
13896
|
+
* This method is used by forwarded transfers. It polls the same Iris
|
|
13897
|
+
* attestation endpoint as {@link CCTPV2BridgingProvider.fetchAttestation},
|
|
13898
|
+
* but waits for a completed relayer forward state and returns the attestation
|
|
13899
|
+
* message containing `forwardTxHash`.
|
|
13900
|
+
*
|
|
13901
|
+
* @typeParam TFromAdapterCapabilities - The type representing the capabilities of the source adapter
|
|
13902
|
+
* @param source - The source wallet context containing the chain definition and wallet address
|
|
13903
|
+
* @param transactionHash - The transaction hash of the burn operation
|
|
13904
|
+
* @param config - Optional polling configuration overrides for timeout, retries, delay, and headers
|
|
13905
|
+
* @returns A promise that resolves to the attestation message with `forwardTxHash`
|
|
13906
|
+
* @throws KitError If the relayer forward fails, the response is invalid, or polling times out
|
|
13907
|
+
*
|
|
13908
|
+
* @example
|
|
13909
|
+
* ```typescript
|
|
13910
|
+
* import { CCTPV2BridgingProvider } from '@circle-fin/provider-cctp-v2'
|
|
13911
|
+
* import { Chains } from '@core/chains'
|
|
13912
|
+
*
|
|
13913
|
+
* const provider = new CCTPV2BridgingProvider({
|
|
13914
|
+
* headers: { 'X-Partner-UUID': '00000000-0000-0000-0000-000000000000' },
|
|
13915
|
+
* })
|
|
13916
|
+
*
|
|
13917
|
+
* const attestation = await provider.fetchRelayerMint(
|
|
13918
|
+
* {
|
|
13919
|
+
* adapter,
|
|
13920
|
+
* chain: Chains.EthereumSepolia,
|
|
13921
|
+
* address: '0x1234...',
|
|
13922
|
+
* },
|
|
13923
|
+
* '0xabc123...',
|
|
13924
|
+
* )
|
|
13925
|
+
*
|
|
13926
|
+
* console.log('Relayer mint tx:', attestation.forwardTxHash)
|
|
13927
|
+
* ```
|
|
13928
|
+
*/ async fetchRelayerMint(source, transactionHash, config) {
|
|
13929
|
+
assertCCTPv2WalletContext(source);
|
|
13930
|
+
if (typeof transactionHash !== 'string' || transactionHash.trim() === '') {
|
|
13931
|
+
throw createValidationFailedError('transactionHash', transactionHash, 'transactionHash must be a non-empty string');
|
|
13932
|
+
}
|
|
13933
|
+
const effectiveConfig = this.resolveAttestationConfig(config);
|
|
13934
|
+
return await fetchRelayerMint(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
|
|
13935
|
+
}
|
|
13936
|
+
/**
|
|
13698
13937
|
* Requests a fresh attestation for an expired attestation.
|
|
13699
13938
|
*
|
|
13700
13939
|
* This method is used when the original attestation has expired before the mint
|
|
@@ -13748,11 +13987,7 @@ var pkg = {
|
|
|
13748
13987
|
throw new Error('Failed to re-attest: Invalid transaction hash');
|
|
13749
13988
|
}
|
|
13750
13989
|
try {
|
|
13751
|
-
|
|
13752
|
-
const effectiveConfig = {
|
|
13753
|
-
...this.config?.attestation,
|
|
13754
|
-
...config
|
|
13755
|
-
};
|
|
13990
|
+
const effectiveConfig = this.resolveAttestationConfig(config);
|
|
13756
13991
|
// Step 1: Get existing attestation data to extract nonce
|
|
13757
13992
|
const existingAttestation = await fetchAttestationWithoutStatusCheck(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
|
|
13758
13993
|
const nonce = existingAttestation.messages[0]?.eventNonce;
|