@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.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',
|
|
@@ -7458,6 +7557,7 @@ class KitError extends Error {
|
|
|
7458
7557
|
// =========================================================================
|
|
7459
7558
|
[Blockchain.Avalanche]: '0xc891EB4cbdEFf6e073e859e987815Ed1505c2ACD',
|
|
7460
7559
|
[Blockchain.Base]: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42',
|
|
7560
|
+
[Blockchain.Cronos]: '0xA6dE01a2d62C6B5f3525d768f34d276652C554c8',
|
|
7461
7561
|
[Blockchain.Ethereum]: '0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c',
|
|
7462
7562
|
[Blockchain.Solana]: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr',
|
|
7463
7563
|
[Blockchain.World_Chain]: '0x1C60ba0A0eD1019e8Eb035E6daF4155A5cE2380B',
|
|
@@ -7466,6 +7566,7 @@ class KitError extends Error {
|
|
|
7466
7566
|
// =========================================================================
|
|
7467
7567
|
[Blockchain.Arc_Testnet]: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
|
|
7468
7568
|
[Blockchain.Base_Sepolia]: '0x808456652fdb597867f38412077A9182bf77359F',
|
|
7569
|
+
[Blockchain.Cronos_Testnet]: '0x31f7538adb53cF16350e6B0c89d03D91b7D12c46',
|
|
7469
7570
|
[Blockchain.Ethereum_Sepolia]: '0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4'
|
|
7470
7571
|
}
|
|
7471
7572
|
};
|
|
@@ -9175,6 +9276,15 @@ const CUSTOM_BURN_GAS_ESTIMATE_EVM = 201_525n // p99 and max are same here: 201_
|
|
|
9175
9276
|
;
|
|
9176
9277
|
const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_839n) / 2 = 237_401n
|
|
9177
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
|
+
;
|
|
9178
9288
|
/**
|
|
9179
9289
|
* The minimum finality threshold for CCTPv2 transfers.
|
|
9180
9290
|
*
|
|
@@ -9203,6 +9313,27 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9203
9313
|
'Content-Type': 'application/json'
|
|
9204
9314
|
}
|
|
9205
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
|
+
});
|
|
9206
9337
|
/**
|
|
9207
9338
|
* Type guard that verifies if an unknown value matches the AttestationMessage shape
|
|
9208
9339
|
* and has all required properties.
|
|
@@ -9349,10 +9480,7 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9349
9480
|
* ```
|
|
9350
9481
|
*/ const fetchAttestation = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
9351
9482
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
9352
|
-
const effectiveConfig =
|
|
9353
|
-
...DEFAULT_CONFIG,
|
|
9354
|
-
...config
|
|
9355
|
-
};
|
|
9483
|
+
const effectiveConfig = mergeAttestationConfig(config);
|
|
9356
9484
|
return await pollApiGet(url, isAttestationResponse, effectiveConfig);
|
|
9357
9485
|
};
|
|
9358
9486
|
/**
|
|
@@ -9395,11 +9523,9 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9395
9523
|
*/ const fetchAttestationWithoutStatusCheck = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
9396
9524
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
9397
9525
|
// Use minimal retries since we're just fetching existing data
|
|
9398
|
-
const effectiveConfig = {
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
...config
|
|
9402
|
-
};
|
|
9526
|
+
const effectiveConfig = mergeAttestationConfig(config, {
|
|
9527
|
+
maxRetries: 3
|
|
9528
|
+
});
|
|
9403
9529
|
return await pollApiGet(url, isAttestationResponseWithoutStatusCheck, effectiveConfig);
|
|
9404
9530
|
};
|
|
9405
9531
|
/**
|
|
@@ -9459,10 +9585,7 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9459
9585
|
* ```
|
|
9460
9586
|
*/ const fetchReAttestedAttestation = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
9461
9587
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
9462
|
-
const effectiveConfig =
|
|
9463
|
-
...DEFAULT_CONFIG,
|
|
9464
|
-
...config
|
|
9465
|
-
};
|
|
9588
|
+
const effectiveConfig = mergeAttestationConfig(config);
|
|
9466
9589
|
return await pollApiGet(url, isReAttestedAttestationResponse, effectiveConfig);
|
|
9467
9590
|
};
|
|
9468
9591
|
/**
|
|
@@ -9528,14 +9651,139 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9528
9651
|
*/ const requestReAttestation = async (nonce, isTestnet, config = {})=>{
|
|
9529
9652
|
const url = buildReAttestUrl(nonce, isTestnet);
|
|
9530
9653
|
// Use minimal retries since we're just submitting a request, not polling for state
|
|
9531
|
-
const effectiveConfig = {
|
|
9532
|
-
|
|
9533
|
-
|
|
9534
|
-
...config
|
|
9535
|
-
};
|
|
9654
|
+
const effectiveConfig = mergeAttestationConfig(config, {
|
|
9655
|
+
maxRetries: 3
|
|
9656
|
+
});
|
|
9536
9657
|
return await pollApiPost(url, {}, isReAttestationResponse, effectiveConfig);
|
|
9537
9658
|
};
|
|
9538
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
|
+
|
|
9539
9787
|
const assertCCTPv2WalletContextSymbol = Symbol('assertCCTPv2WalletContext');
|
|
9540
9788
|
/**
|
|
9541
9789
|
* Asserts that the provided parameters match the CCTPv2 wallet context interface.
|
|
@@ -10410,6 +10658,8 @@ function hasPendingState(analysis, result) {
|
|
|
10410
10658
|
* - `adapter`: The adapter that will execute the transaction
|
|
10411
10659
|
* - `confirmations`: The number of confirmations to wait for (defaults to 1)
|
|
10412
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
|
|
10413
10663
|
* @returns The bridge step with the transaction details and explorer URL
|
|
10414
10664
|
* @throws If the transaction execution fails
|
|
10415
10665
|
*
|
|
@@ -10424,7 +10674,7 @@ function hasPendingState(analysis, result) {
|
|
|
10424
10674
|
* })
|
|
10425
10675
|
* console.log('Transaction hash:', step.txHash)
|
|
10426
10676
|
* ```
|
|
10427
|
-
*/ async function executePreparedChainRequest({ name, request, adapter, chain, confirmations = 1, timeout }) {
|
|
10677
|
+
*/ async function executePreparedChainRequest({ name, request, adapter, chain, confirmations = 1, timeout, gasLimit }) {
|
|
10428
10678
|
const step = {
|
|
10429
10679
|
name,
|
|
10430
10680
|
state: 'pending'
|
|
@@ -10437,7 +10687,9 @@ function hasPendingState(analysis, result) {
|
|
|
10437
10687
|
step.state = 'noop';
|
|
10438
10688
|
return step;
|
|
10439
10689
|
}
|
|
10440
|
-
const txHash = await request.execute(
|
|
10690
|
+
const txHash = request.type === 'evm' && gasLimit !== undefined ? await request.execute({
|
|
10691
|
+
gasLimit
|
|
10692
|
+
}) : await request.execute();
|
|
10441
10693
|
step.txHash = txHash;
|
|
10442
10694
|
const retryOptions = {
|
|
10443
10695
|
isRetryable: (err)=>isRetryableError$1(parseBlockchainError(err, {
|
|
@@ -10509,7 +10761,8 @@ function hasPendingState(analysis, result) {
|
|
|
10509
10761
|
name: 'approve',
|
|
10510
10762
|
adapter: params.source.adapter,
|
|
10511
10763
|
chain: params.source.chain,
|
|
10512
|
-
request: await provider.approve(params.source, approvalAmount)
|
|
10764
|
+
request: await provider.approve(params.source, approvalAmount),
|
|
10765
|
+
gasLimit: Number(APPROVE_GAS_LIMIT_EVM)
|
|
10513
10766
|
});
|
|
10514
10767
|
}
|
|
10515
10768
|
|
|
@@ -10536,7 +10789,8 @@ function hasPendingState(analysis, result) {
|
|
|
10536
10789
|
name: 'burn',
|
|
10537
10790
|
adapter: params.source.adapter,
|
|
10538
10791
|
chain: params.source.chain,
|
|
10539
|
-
request: await provider.burn(params)
|
|
10792
|
+
request: await provider.burn(params),
|
|
10793
|
+
gasLimit: Number(DEPOSIT_FOR_BURN_GAS_LIMIT_EVM)
|
|
10540
10794
|
});
|
|
10541
10795
|
}
|
|
10542
10796
|
|
|
@@ -10622,11 +10876,18 @@ function hasPendingState(analysis, result) {
|
|
|
10622
10876
|
*/ async function bridgeMint({ params, provider }, attestation) {
|
|
10623
10877
|
// Validate attestation message matches transfer params
|
|
10624
10878
|
await assertCCTPv2AttestationParams(attestation, params);
|
|
10879
|
+
const mintRequest = await provider.mint(params.source, params.destination, attestation);
|
|
10625
10880
|
const step = await executePreparedChainRequest({
|
|
10626
10881
|
name: 'mint',
|
|
10627
10882
|
adapter: params.destination.adapter,
|
|
10628
10883
|
chain: params.destination.chain,
|
|
10629
|
-
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)
|
|
10630
10891
|
});
|
|
10631
10892
|
// Add forwarded: false for non-relayer mints
|
|
10632
10893
|
return {
|
|
@@ -10664,136 +10925,6 @@ const mockAttestationMessage = {
|
|
|
10664
10925
|
delayReason: null
|
|
10665
10926
|
};
|
|
10666
10927
|
|
|
10667
|
-
/**
|
|
10668
|
-
* Type guard that checks if the relayer has confirmed the mint transaction.
|
|
10669
|
-
*
|
|
10670
|
-
* This function validates that:
|
|
10671
|
-
* 1. The response has valid AttestationResponse structure
|
|
10672
|
-
* 2. At least one message has forwardState === 'CONFIRMED' (or 'COMPLETE') and a valid forwardTxHash
|
|
10673
|
-
*
|
|
10674
|
-
* If forwardState is 'FAILED', throws a non-retryable KitError.
|
|
10675
|
-
* If forwardState is 'PENDING' or not present, throws a RETRYABLE KitError to continue polling.
|
|
10676
|
-
*
|
|
10677
|
-
* @param obj - The value to check, typically a parsed JSON response
|
|
10678
|
-
* @returns True if the relayer has confirmed the mint
|
|
10679
|
-
* @throws {KitError} With FATAL recoverability if structure is invalid
|
|
10680
|
-
* @throws {KitError} With RESUMABLE recoverability if forwardState is 'FAILED'
|
|
10681
|
-
* @throws {KitError} With RETRYABLE recoverability if still pending
|
|
10682
|
-
* @internal
|
|
10683
|
-
*/ const isRelayerMintConfirmed = (obj)=>{
|
|
10684
|
-
// First check if the structure is valid
|
|
10685
|
-
if (!hasValidAttestationStructure(obj)) {
|
|
10686
|
-
throw new KitError({
|
|
10687
|
-
...InputError.VALIDATION_FAILED,
|
|
10688
|
-
recoverability: 'FATAL',
|
|
10689
|
-
message: 'Invalid attestation response structure from IRIS API.'
|
|
10690
|
-
});
|
|
10691
|
-
}
|
|
10692
|
-
// Find the first message (typically there's only one)
|
|
10693
|
-
const message = obj.messages[0];
|
|
10694
|
-
if (!message) {
|
|
10695
|
-
throw new KitError({
|
|
10696
|
-
...InputError.VALIDATION_FAILED,
|
|
10697
|
-
recoverability: 'FATAL',
|
|
10698
|
-
message: 'No attestation messages found in IRIS API response.'
|
|
10699
|
-
});
|
|
10700
|
-
}
|
|
10701
|
-
// Check for FAILED state - this is a permanent failure
|
|
10702
|
-
if (message.forwardState === 'FAILED') {
|
|
10703
|
-
throw new KitError({
|
|
10704
|
-
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
10705
|
-
recoverability: 'RESUMABLE',
|
|
10706
|
-
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.',
|
|
10707
|
-
cause: {
|
|
10708
|
-
trace: {
|
|
10709
|
-
eventNonce: message.eventNonce,
|
|
10710
|
-
attestation: message.attestation,
|
|
10711
|
-
message: message.message
|
|
10712
|
-
}
|
|
10713
|
-
}
|
|
10714
|
-
});
|
|
10715
|
-
}
|
|
10716
|
-
// Check if mint is confirmed (or complete) with a valid transaction hash
|
|
10717
|
-
// We accept both CONFIRMED and COMPLETE since COMPLETE implies CONFIRMED
|
|
10718
|
-
if ((message.forwardState === 'CONFIRMED' || message.forwardState === 'COMPLETE') && typeof message.forwardTxHash === 'string' && message.forwardTxHash.trim().length > 0) {
|
|
10719
|
-
return true;
|
|
10720
|
-
}
|
|
10721
|
-
// Still pending or not yet processed - throw RETRYABLE error to continue polling
|
|
10722
|
-
throw new KitError({
|
|
10723
|
-
...NetworkError.RELAYER_PENDING,
|
|
10724
|
-
recoverability: 'RETRYABLE',
|
|
10725
|
-
message: 'Relayer mint not ready. Waiting for confirmation.'
|
|
10726
|
-
});
|
|
10727
|
-
};
|
|
10728
|
-
/**
|
|
10729
|
-
* Polls the attestation API until the relayer's mint transaction is confirmed.
|
|
10730
|
-
*
|
|
10731
|
-
* This function is used when `useForwarder` is enabled. Instead of the user
|
|
10732
|
-
* submitting the mint transaction, Circle's Orbit relayer handles it automatically.
|
|
10733
|
-
* This function polls until the relayer has submitted and confirmed the mint transaction.
|
|
10734
|
-
*
|
|
10735
|
-
* @remarks
|
|
10736
|
-
* - Uses a 20-minute timeout by default (600 retries × 2 seconds)
|
|
10737
|
-
* - Throws immediately if `forwardState` is 'FAILED'
|
|
10738
|
-
* - Waits for `forwardState` to be 'CONFIRMED' or 'COMPLETE' (COMPLETE implies CONFIRMED)
|
|
10739
|
-
* - Returns the attestation message with `forwardTxHash` populated
|
|
10740
|
-
*
|
|
10741
|
-
* @param sourceDomainId - The CCTP domain ID of the source chain
|
|
10742
|
-
* @param transactionHash - The transaction hash of the burn operation
|
|
10743
|
-
* @param isTestnet - Whether this is for a testnet chain (true) or mainnet (false)
|
|
10744
|
-
* @param config - Optional configuration overrides for polling behavior
|
|
10745
|
-
* @returns The attestation message with confirmed forwardTxHash
|
|
10746
|
-
* @throws {KitError} With code 'NETWORK_RELAYER_FORWARD_FAILED' if relayer failed
|
|
10747
|
-
* @throws {KitError} If timeout is reached while still pending
|
|
10748
|
-
*
|
|
10749
|
-
* @example
|
|
10750
|
-
* ```typescript
|
|
10751
|
-
* const attestation = await fetchRelayerMint(0, '0xabc...', false)
|
|
10752
|
-
* console.log('Relayer mint tx:', attestation.forwardTxHash)
|
|
10753
|
-
* ```
|
|
10754
|
-
*/ const fetchRelayerMint = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
10755
|
-
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
10756
|
-
const effectiveConfig = {
|
|
10757
|
-
...DEFAULT_CONFIG,
|
|
10758
|
-
...config
|
|
10759
|
-
};
|
|
10760
|
-
let response;
|
|
10761
|
-
try {
|
|
10762
|
-
response = await pollApiGet(url, isRelayerMintConfirmed, effectiveConfig);
|
|
10763
|
-
} catch (error) {
|
|
10764
|
-
// Enrich RELAYER_FORWARD_FAILED errors with the burn transaction hash
|
|
10765
|
-
if (error instanceof KitError && error.name === 'NETWORK_RELAYER_FORWARD_FAILED') {
|
|
10766
|
-
throw new KitError({
|
|
10767
|
-
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
10768
|
-
recoverability: error.recoverability,
|
|
10769
|
-
message: error.message,
|
|
10770
|
-
cause: {
|
|
10771
|
-
...error.cause,
|
|
10772
|
-
trace: {
|
|
10773
|
-
...error.cause?.trace,
|
|
10774
|
-
burnTxHash: transactionHash
|
|
10775
|
-
}
|
|
10776
|
-
}
|
|
10777
|
-
});
|
|
10778
|
-
}
|
|
10779
|
-
throw error;
|
|
10780
|
-
}
|
|
10781
|
-
// Return the first message (which should have forwardTxHash)
|
|
10782
|
-
// Note: This check is needed for TypeScript type safety even though
|
|
10783
|
-
// isRelayerMintConfirmed validates messages[0] exists. The type guard
|
|
10784
|
-
// narrows the type at the call site, but TypeScript can't infer that
|
|
10785
|
-
// the array still has elements after pollApiGet returns.
|
|
10786
|
-
const message = response.messages[0];
|
|
10787
|
-
if (!message) {
|
|
10788
|
-
throw new KitError({
|
|
10789
|
-
...InputError.VALIDATION_FAILED,
|
|
10790
|
-
recoverability: 'FATAL',
|
|
10791
|
-
message: 'No attestation messages found in response after polling.'
|
|
10792
|
-
});
|
|
10793
|
-
}
|
|
10794
|
-
return message;
|
|
10795
|
-
};
|
|
10796
|
-
|
|
10797
10928
|
/**
|
|
10798
10929
|
* Executes the mint step for forwarding mode where Circle's relayer handles the mint.
|
|
10799
10930
|
*
|
|
@@ -10807,6 +10938,7 @@ const mockAttestationMessage = {
|
|
|
10807
10938
|
* immediately after the API confirms the mint.
|
|
10808
10939
|
*
|
|
10809
10940
|
* @param params - The bridge parameters containing source, destination, amount and config
|
|
10941
|
+
* @param provider - The CCTP v2 bridging provider
|
|
10810
10942
|
* @param context - The step context containing burnTxHash from the burn step
|
|
10811
10943
|
* @returns Promise resolving to the bridge step with transaction details
|
|
10812
10944
|
* @throws {KitError} If burnTxHash is not available in context
|
|
@@ -10815,10 +10947,13 @@ const mockAttestationMessage = {
|
|
|
10815
10947
|
*
|
|
10816
10948
|
* @example
|
|
10817
10949
|
* ```typescript
|
|
10818
|
-
* const step = await bridgeRelayerMint(
|
|
10950
|
+
* const step = await bridgeRelayerMint(
|
|
10951
|
+
* { params, provider },
|
|
10952
|
+
* { burnTxHash: '0x...' }
|
|
10953
|
+
* )
|
|
10819
10954
|
* console.log('Relayer mint tx:', step.txHash)
|
|
10820
10955
|
* ```
|
|
10821
|
-
*/ async function bridgeRelayerMint(params, context) {
|
|
10956
|
+
*/ async function bridgeRelayerMint({ params, provider }, context) {
|
|
10822
10957
|
const step = {
|
|
10823
10958
|
name: 'mint',
|
|
10824
10959
|
state: 'pending',
|
|
@@ -10834,7 +10969,7 @@ const mockAttestationMessage = {
|
|
|
10834
10969
|
}
|
|
10835
10970
|
try {
|
|
10836
10971
|
// Poll attestation API until relayer confirms the mint
|
|
10837
|
-
const attestation = await fetchRelayerMint(params.source
|
|
10972
|
+
const attestation = await provider.fetchRelayerMint(params.source, context.burnTxHash);
|
|
10838
10973
|
// Extract the relayer's mint transaction hash
|
|
10839
10974
|
const forwardTxHash = attestation.forwardTxHash;
|
|
10840
10975
|
if (!forwardTxHash) {
|
|
@@ -10928,7 +11063,7 @@ const mockAttestationMessage = {
|
|
|
10928
11063
|
name: 'mint',
|
|
10929
11064
|
// bridgeRelayerMint validates context.burnTxHash internally with a KitError
|
|
10930
11065
|
// bridgeMint requires attestationData which is validated here
|
|
10931
|
-
executor: useForwarder ? async (params,
|
|
11066
|
+
executor: useForwarder ? async (params, provider, context)=>{
|
|
10932
11067
|
if (!context) {
|
|
10933
11068
|
throw new KitError({
|
|
10934
11069
|
...InputError.VALIDATION_FAILED,
|
|
@@ -10936,7 +11071,10 @@ const mockAttestationMessage = {
|
|
|
10936
11071
|
message: 'Step context is required for relayer mint'
|
|
10937
11072
|
});
|
|
10938
11073
|
}
|
|
10939
|
-
return bridgeRelayerMint(
|
|
11074
|
+
return bridgeRelayerMint({
|
|
11075
|
+
params,
|
|
11076
|
+
provider
|
|
11077
|
+
}, context);
|
|
10940
11078
|
} : async (params, provider, context)=>{
|
|
10941
11079
|
if (!context?.attestationData) {
|
|
10942
11080
|
throw new KitError({
|
|
@@ -12145,7 +12283,7 @@ const mockAttestationMessage = {
|
|
|
12145
12283
|
return step;
|
|
12146
12284
|
}
|
|
12147
12285
|
|
|
12148
|
-
var version = "1.
|
|
12286
|
+
var version = "1.9.0";
|
|
12149
12287
|
var pkg = {
|
|
12150
12288
|
version: version};
|
|
12151
12289
|
|
|
@@ -13171,6 +13309,39 @@ var pkg = {
|
|
|
13171
13309
|
}
|
|
13172
13310
|
}
|
|
13173
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
|
+
}
|
|
13174
13345
|
/**
|
|
13175
13346
|
* All chains that are supported by the CCTP v2 provider.
|
|
13176
13347
|
*
|
|
@@ -13239,9 +13410,38 @@ var pkg = {
|
|
|
13239
13410
|
* @param config - Optional configuration overrides for the provider
|
|
13240
13411
|
*/ constructor(config = {}){
|
|
13241
13412
|
super();
|
|
13413
|
+
assertCCTPV2Config(config);
|
|
13242
13414
|
this.config = config;
|
|
13243
13415
|
}
|
|
13244
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
|
+
/**
|
|
13245
13445
|
* Execute a cross-chain USDC bridge operation using the CCTP v2 protocol.
|
|
13246
13446
|
*
|
|
13247
13447
|
* This method orchestrates the complete CCTP v2 bridge flow including validation,
|
|
@@ -13667,11 +13867,7 @@ var pkg = {
|
|
|
13667
13867
|
*/ async fetchAttestation(source, transactionHash, config) {
|
|
13668
13868
|
assertCCTPv2WalletContext(source);
|
|
13669
13869
|
try {
|
|
13670
|
-
|
|
13671
|
-
const effectiveConfig = {
|
|
13672
|
-
...this.config?.attestation,
|
|
13673
|
-
...config
|
|
13674
|
-
};
|
|
13870
|
+
const effectiveConfig = this.resolveAttestationConfig(config);
|
|
13675
13871
|
const response = await fetchAttestation(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
|
|
13676
13872
|
const message = response.messages[0];
|
|
13677
13873
|
if (!message) {
|
|
@@ -13688,6 +13884,49 @@ var pkg = {
|
|
|
13688
13884
|
}
|
|
13689
13885
|
}
|
|
13690
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
|
+
/**
|
|
13691
13930
|
* Requests a fresh attestation for an expired attestation.
|
|
13692
13931
|
*
|
|
13693
13932
|
* This method is used when the original attestation has expired before the mint
|
|
@@ -13741,11 +13980,7 @@ var pkg = {
|
|
|
13741
13980
|
throw new Error('Failed to re-attest: Invalid transaction hash');
|
|
13742
13981
|
}
|
|
13743
13982
|
try {
|
|
13744
|
-
|
|
13745
|
-
const effectiveConfig = {
|
|
13746
|
-
...this.config?.attestation,
|
|
13747
|
-
...config
|
|
13748
|
-
};
|
|
13983
|
+
const effectiveConfig = this.resolveAttestationConfig(config);
|
|
13749
13984
|
// Step 1: Get existing attestation data to extract nonce
|
|
13750
13985
|
const existingAttestation = await fetchAttestationWithoutStatusCheck(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
|
|
13751
13986
|
const nonce = existingAttestation.messages[0]?.eventNonce;
|