@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.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',
|
|
@@ -7460,13 +7559,22 @@ class KitError extends Error {
|
|
|
7460
7559
|
symbol: 'EURC',
|
|
7461
7560
|
decimals: 6,
|
|
7462
7561
|
locators: {
|
|
7562
|
+
// =========================================================================
|
|
7563
|
+
// Mainnets
|
|
7564
|
+
// =========================================================================
|
|
7463
7565
|
[Blockchain.Avalanche]: '0xc891EB4cbdEFf6e073e859e987815Ed1505c2ACD',
|
|
7464
7566
|
[Blockchain.Base]: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42',
|
|
7567
|
+
[Blockchain.Cronos]: '0xA6dE01a2d62C6B5f3525d768f34d276652C554c8',
|
|
7465
7568
|
[Blockchain.Ethereum]: '0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c',
|
|
7466
7569
|
[Blockchain.Solana]: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr',
|
|
7467
7570
|
[Blockchain.World_Chain]: '0x1C60ba0A0eD1019e8Eb035E6daF4155A5cE2380B',
|
|
7571
|
+
// =========================================================================
|
|
7468
7572
|
// Testnets
|
|
7469
|
-
|
|
7573
|
+
// =========================================================================
|
|
7574
|
+
[Blockchain.Arc_Testnet]: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
|
|
7575
|
+
[Blockchain.Base_Sepolia]: '0x808456652fdb597867f38412077A9182bf77359F',
|
|
7576
|
+
[Blockchain.Cronos_Testnet]: '0x31f7538adb53cF16350e6B0c89d03D91b7D12c46',
|
|
7577
|
+
[Blockchain.Ethereum_Sepolia]: '0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4'
|
|
7470
7578
|
}
|
|
7471
7579
|
};
|
|
7472
7580
|
|
|
@@ -7676,8 +7784,7 @@ class KitError extends Error {
|
|
|
7676
7784
|
* cirBTC (Circle Bitcoin) token definition with addresses and metadata.
|
|
7677
7785
|
*
|
|
7678
7786
|
* @remarks
|
|
7679
|
-
* Built-in cirBTC definition for the TokenRegistry.
|
|
7680
|
-
* on Arc Testnet.
|
|
7787
|
+
* Built-in cirBTC definition for the TokenRegistry.
|
|
7681
7788
|
*
|
|
7682
7789
|
* @example
|
|
7683
7790
|
* ```typescript
|
|
@@ -7693,7 +7800,15 @@ class KitError extends Error {
|
|
|
7693
7800
|
symbol: 'cirBTC',
|
|
7694
7801
|
decimals: 8,
|
|
7695
7802
|
locators: {
|
|
7696
|
-
|
|
7803
|
+
// =========================================================================
|
|
7804
|
+
// Mainnets
|
|
7805
|
+
// =========================================================================
|
|
7806
|
+
[Blockchain.Ethereum]: '0x72DFB2E44f59C5AD2bAFE84314E5b99a7cd5075E',
|
|
7807
|
+
// =========================================================================
|
|
7808
|
+
// Testnets
|
|
7809
|
+
// =========================================================================
|
|
7810
|
+
[Blockchain.Arc_Testnet]: '0xf0C4a4CE82A5746AbAAd9425360Ab04fbBA432BF',
|
|
7811
|
+
[Blockchain.Ethereum_Sepolia]: '0x3a3fe695F684Bf9b9e43CF43C2b895Ea5e392bB3'
|
|
7697
7812
|
}
|
|
7698
7813
|
};
|
|
7699
7814
|
|
|
@@ -9168,6 +9283,15 @@ const CUSTOM_BURN_GAS_ESTIMATE_EVM = 201_525n // p99 and max are same here: 201_
|
|
|
9168
9283
|
;
|
|
9169
9284
|
const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_839n) / 2 = 237_401n
|
|
9170
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
|
+
;
|
|
9171
9295
|
/**
|
|
9172
9296
|
* The minimum finality threshold for CCTPv2 transfers.
|
|
9173
9297
|
*
|
|
@@ -9196,6 +9320,27 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9196
9320
|
'Content-Type': 'application/json'
|
|
9197
9321
|
}
|
|
9198
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
|
+
});
|
|
9199
9344
|
/**
|
|
9200
9345
|
* Type guard that verifies if an unknown value matches the AttestationMessage shape
|
|
9201
9346
|
* and has all required properties.
|
|
@@ -9342,10 +9487,7 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9342
9487
|
* ```
|
|
9343
9488
|
*/ const fetchAttestation = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
9344
9489
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
9345
|
-
const effectiveConfig =
|
|
9346
|
-
...DEFAULT_CONFIG,
|
|
9347
|
-
...config
|
|
9348
|
-
};
|
|
9490
|
+
const effectiveConfig = mergeAttestationConfig(config);
|
|
9349
9491
|
return await pollApiGet(url, isAttestationResponse, effectiveConfig);
|
|
9350
9492
|
};
|
|
9351
9493
|
/**
|
|
@@ -9388,11 +9530,9 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9388
9530
|
*/ const fetchAttestationWithoutStatusCheck = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
9389
9531
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
9390
9532
|
// Use minimal retries since we're just fetching existing data
|
|
9391
|
-
const effectiveConfig = {
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
...config
|
|
9395
|
-
};
|
|
9533
|
+
const effectiveConfig = mergeAttestationConfig(config, {
|
|
9534
|
+
maxRetries: 3
|
|
9535
|
+
});
|
|
9396
9536
|
return await pollApiGet(url, isAttestationResponseWithoutStatusCheck, effectiveConfig);
|
|
9397
9537
|
};
|
|
9398
9538
|
/**
|
|
@@ -9452,10 +9592,7 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9452
9592
|
* ```
|
|
9453
9593
|
*/ const fetchReAttestedAttestation = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
9454
9594
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
9455
|
-
const effectiveConfig =
|
|
9456
|
-
...DEFAULT_CONFIG,
|
|
9457
|
-
...config
|
|
9458
|
-
};
|
|
9595
|
+
const effectiveConfig = mergeAttestationConfig(config);
|
|
9459
9596
|
return await pollApiGet(url, isReAttestedAttestationResponse, effectiveConfig);
|
|
9460
9597
|
};
|
|
9461
9598
|
/**
|
|
@@ -9521,14 +9658,139 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
9521
9658
|
*/ const requestReAttestation = async (nonce, isTestnet, config = {})=>{
|
|
9522
9659
|
const url = buildReAttestUrl(nonce, isTestnet);
|
|
9523
9660
|
// Use minimal retries since we're just submitting a request, not polling for state
|
|
9524
|
-
const effectiveConfig = {
|
|
9525
|
-
|
|
9526
|
-
|
|
9527
|
-
...config
|
|
9528
|
-
};
|
|
9661
|
+
const effectiveConfig = mergeAttestationConfig(config, {
|
|
9662
|
+
maxRetries: 3
|
|
9663
|
+
});
|
|
9529
9664
|
return await pollApiPost(url, {}, isReAttestationResponse, effectiveConfig);
|
|
9530
9665
|
};
|
|
9531
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
|
+
|
|
9532
9794
|
const assertCCTPv2WalletContextSymbol = Symbol('assertCCTPv2WalletContext');
|
|
9533
9795
|
/**
|
|
9534
9796
|
* Asserts that the provided parameters match the CCTPv2 wallet context interface.
|
|
@@ -10403,6 +10665,8 @@ function hasPendingState(analysis, result) {
|
|
|
10403
10665
|
* - `adapter`: The adapter that will execute the transaction
|
|
10404
10666
|
* - `confirmations`: The number of confirmations to wait for (defaults to 1)
|
|
10405
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
|
|
10406
10670
|
* @returns The bridge step with the transaction details and explorer URL
|
|
10407
10671
|
* @throws If the transaction execution fails
|
|
10408
10672
|
*
|
|
@@ -10417,7 +10681,7 @@ function hasPendingState(analysis, result) {
|
|
|
10417
10681
|
* })
|
|
10418
10682
|
* console.log('Transaction hash:', step.txHash)
|
|
10419
10683
|
* ```
|
|
10420
|
-
*/ async function executePreparedChainRequest({ name, request, adapter, chain, confirmations = 1, timeout }) {
|
|
10684
|
+
*/ async function executePreparedChainRequest({ name, request, adapter, chain, confirmations = 1, timeout, gasLimit }) {
|
|
10421
10685
|
const step = {
|
|
10422
10686
|
name,
|
|
10423
10687
|
state: 'pending'
|
|
@@ -10430,7 +10694,9 @@ function hasPendingState(analysis, result) {
|
|
|
10430
10694
|
step.state = 'noop';
|
|
10431
10695
|
return step;
|
|
10432
10696
|
}
|
|
10433
|
-
const txHash = await request.execute(
|
|
10697
|
+
const txHash = request.type === 'evm' && gasLimit !== undefined ? await request.execute({
|
|
10698
|
+
gasLimit
|
|
10699
|
+
}) : await request.execute();
|
|
10434
10700
|
step.txHash = txHash;
|
|
10435
10701
|
const retryOptions = {
|
|
10436
10702
|
isRetryable: (err)=>isRetryableError$1(parseBlockchainError(err, {
|
|
@@ -10502,7 +10768,8 @@ function hasPendingState(analysis, result) {
|
|
|
10502
10768
|
name: 'approve',
|
|
10503
10769
|
adapter: params.source.adapter,
|
|
10504
10770
|
chain: params.source.chain,
|
|
10505
|
-
request: await provider.approve(params.source, approvalAmount)
|
|
10771
|
+
request: await provider.approve(params.source, approvalAmount),
|
|
10772
|
+
gasLimit: Number(APPROVE_GAS_LIMIT_EVM)
|
|
10506
10773
|
});
|
|
10507
10774
|
}
|
|
10508
10775
|
|
|
@@ -10529,7 +10796,8 @@ function hasPendingState(analysis, result) {
|
|
|
10529
10796
|
name: 'burn',
|
|
10530
10797
|
adapter: params.source.adapter,
|
|
10531
10798
|
chain: params.source.chain,
|
|
10532
|
-
request: await provider.burn(params)
|
|
10799
|
+
request: await provider.burn(params),
|
|
10800
|
+
gasLimit: Number(DEPOSIT_FOR_BURN_GAS_LIMIT_EVM)
|
|
10533
10801
|
});
|
|
10534
10802
|
}
|
|
10535
10803
|
|
|
@@ -10615,11 +10883,18 @@ function hasPendingState(analysis, result) {
|
|
|
10615
10883
|
*/ async function bridgeMint({ params, provider }, attestation) {
|
|
10616
10884
|
// Validate attestation message matches transfer params
|
|
10617
10885
|
await assertCCTPv2AttestationParams(attestation, params);
|
|
10886
|
+
const mintRequest = await provider.mint(params.source, params.destination, attestation);
|
|
10618
10887
|
const step = await executePreparedChainRequest({
|
|
10619
10888
|
name: 'mint',
|
|
10620
10889
|
adapter: params.destination.adapter,
|
|
10621
10890
|
chain: params.destination.chain,
|
|
10622
|
-
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)
|
|
10623
10898
|
});
|
|
10624
10899
|
// Add forwarded: false for non-relayer mints
|
|
10625
10900
|
return {
|
|
@@ -10657,136 +10932,6 @@ const mockAttestationMessage = {
|
|
|
10657
10932
|
delayReason: null
|
|
10658
10933
|
};
|
|
10659
10934
|
|
|
10660
|
-
/**
|
|
10661
|
-
* Type guard that checks if the relayer has confirmed the mint transaction.
|
|
10662
|
-
*
|
|
10663
|
-
* This function validates that:
|
|
10664
|
-
* 1. The response has valid AttestationResponse structure
|
|
10665
|
-
* 2. At least one message has forwardState === 'CONFIRMED' (or 'COMPLETE') and a valid forwardTxHash
|
|
10666
|
-
*
|
|
10667
|
-
* If forwardState is 'FAILED', throws a non-retryable KitError.
|
|
10668
|
-
* If forwardState is 'PENDING' or not present, throws a RETRYABLE KitError to continue polling.
|
|
10669
|
-
*
|
|
10670
|
-
* @param obj - The value to check, typically a parsed JSON response
|
|
10671
|
-
* @returns True if the relayer has confirmed the mint
|
|
10672
|
-
* @throws {KitError} With FATAL recoverability if structure is invalid
|
|
10673
|
-
* @throws {KitError} With RESUMABLE recoverability if forwardState is 'FAILED'
|
|
10674
|
-
* @throws {KitError} With RETRYABLE recoverability if still pending
|
|
10675
|
-
* @internal
|
|
10676
|
-
*/ const isRelayerMintConfirmed = (obj)=>{
|
|
10677
|
-
// First check if the structure is valid
|
|
10678
|
-
if (!hasValidAttestationStructure(obj)) {
|
|
10679
|
-
throw new KitError({
|
|
10680
|
-
...InputError.VALIDATION_FAILED,
|
|
10681
|
-
recoverability: 'FATAL',
|
|
10682
|
-
message: 'Invalid attestation response structure from IRIS API.'
|
|
10683
|
-
});
|
|
10684
|
-
}
|
|
10685
|
-
// Find the first message (typically there's only one)
|
|
10686
|
-
const message = obj.messages[0];
|
|
10687
|
-
if (!message) {
|
|
10688
|
-
throw new KitError({
|
|
10689
|
-
...InputError.VALIDATION_FAILED,
|
|
10690
|
-
recoverability: 'FATAL',
|
|
10691
|
-
message: 'No attestation messages found in IRIS API response.'
|
|
10692
|
-
});
|
|
10693
|
-
}
|
|
10694
|
-
// Check for FAILED state - this is a permanent failure
|
|
10695
|
-
if (message.forwardState === 'FAILED') {
|
|
10696
|
-
throw new KitError({
|
|
10697
|
-
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
10698
|
-
recoverability: 'RESUMABLE',
|
|
10699
|
-
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.',
|
|
10700
|
-
cause: {
|
|
10701
|
-
trace: {
|
|
10702
|
-
eventNonce: message.eventNonce,
|
|
10703
|
-
attestation: message.attestation,
|
|
10704
|
-
message: message.message
|
|
10705
|
-
}
|
|
10706
|
-
}
|
|
10707
|
-
});
|
|
10708
|
-
}
|
|
10709
|
-
// Check if mint is confirmed (or complete) with a valid transaction hash
|
|
10710
|
-
// We accept both CONFIRMED and COMPLETE since COMPLETE implies CONFIRMED
|
|
10711
|
-
if ((message.forwardState === 'CONFIRMED' || message.forwardState === 'COMPLETE') && typeof message.forwardTxHash === 'string' && message.forwardTxHash.trim().length > 0) {
|
|
10712
|
-
return true;
|
|
10713
|
-
}
|
|
10714
|
-
// Still pending or not yet processed - throw RETRYABLE error to continue polling
|
|
10715
|
-
throw new KitError({
|
|
10716
|
-
...NetworkError.RELAYER_PENDING,
|
|
10717
|
-
recoverability: 'RETRYABLE',
|
|
10718
|
-
message: 'Relayer mint not ready. Waiting for confirmation.'
|
|
10719
|
-
});
|
|
10720
|
-
};
|
|
10721
|
-
/**
|
|
10722
|
-
* Polls the attestation API until the relayer's mint transaction is confirmed.
|
|
10723
|
-
*
|
|
10724
|
-
* This function is used when `useForwarder` is enabled. Instead of the user
|
|
10725
|
-
* submitting the mint transaction, Circle's Orbit relayer handles it automatically.
|
|
10726
|
-
* This function polls until the relayer has submitted and confirmed the mint transaction.
|
|
10727
|
-
*
|
|
10728
|
-
* @remarks
|
|
10729
|
-
* - Uses a 20-minute timeout by default (600 retries × 2 seconds)
|
|
10730
|
-
* - Throws immediately if `forwardState` is 'FAILED'
|
|
10731
|
-
* - Waits for `forwardState` to be 'CONFIRMED' or 'COMPLETE' (COMPLETE implies CONFIRMED)
|
|
10732
|
-
* - Returns the attestation message with `forwardTxHash` populated
|
|
10733
|
-
*
|
|
10734
|
-
* @param sourceDomainId - The CCTP domain ID of the source chain
|
|
10735
|
-
* @param transactionHash - The transaction hash of the burn operation
|
|
10736
|
-
* @param isTestnet - Whether this is for a testnet chain (true) or mainnet (false)
|
|
10737
|
-
* @param config - Optional configuration overrides for polling behavior
|
|
10738
|
-
* @returns The attestation message with confirmed forwardTxHash
|
|
10739
|
-
* @throws {KitError} With code 'NETWORK_RELAYER_FORWARD_FAILED' if relayer failed
|
|
10740
|
-
* @throws {KitError} If timeout is reached while still pending
|
|
10741
|
-
*
|
|
10742
|
-
* @example
|
|
10743
|
-
* ```typescript
|
|
10744
|
-
* const attestation = await fetchRelayerMint(0, '0xabc...', false)
|
|
10745
|
-
* console.log('Relayer mint tx:', attestation.forwardTxHash)
|
|
10746
|
-
* ```
|
|
10747
|
-
*/ const fetchRelayerMint = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
10748
|
-
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
10749
|
-
const effectiveConfig = {
|
|
10750
|
-
...DEFAULT_CONFIG,
|
|
10751
|
-
...config
|
|
10752
|
-
};
|
|
10753
|
-
let response;
|
|
10754
|
-
try {
|
|
10755
|
-
response = await pollApiGet(url, isRelayerMintConfirmed, effectiveConfig);
|
|
10756
|
-
} catch (error) {
|
|
10757
|
-
// Enrich RELAYER_FORWARD_FAILED errors with the burn transaction hash
|
|
10758
|
-
if (error instanceof KitError && error.name === 'NETWORK_RELAYER_FORWARD_FAILED') {
|
|
10759
|
-
throw new KitError({
|
|
10760
|
-
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
10761
|
-
recoverability: error.recoverability,
|
|
10762
|
-
message: error.message,
|
|
10763
|
-
cause: {
|
|
10764
|
-
...error.cause,
|
|
10765
|
-
trace: {
|
|
10766
|
-
...error.cause?.trace,
|
|
10767
|
-
burnTxHash: transactionHash
|
|
10768
|
-
}
|
|
10769
|
-
}
|
|
10770
|
-
});
|
|
10771
|
-
}
|
|
10772
|
-
throw error;
|
|
10773
|
-
}
|
|
10774
|
-
// Return the first message (which should have forwardTxHash)
|
|
10775
|
-
// Note: This check is needed for TypeScript type safety even though
|
|
10776
|
-
// isRelayerMintConfirmed validates messages[0] exists. The type guard
|
|
10777
|
-
// narrows the type at the call site, but TypeScript can't infer that
|
|
10778
|
-
// the array still has elements after pollApiGet returns.
|
|
10779
|
-
const message = response.messages[0];
|
|
10780
|
-
if (!message) {
|
|
10781
|
-
throw new KitError({
|
|
10782
|
-
...InputError.VALIDATION_FAILED,
|
|
10783
|
-
recoverability: 'FATAL',
|
|
10784
|
-
message: 'No attestation messages found in response after polling.'
|
|
10785
|
-
});
|
|
10786
|
-
}
|
|
10787
|
-
return message;
|
|
10788
|
-
};
|
|
10789
|
-
|
|
10790
10935
|
/**
|
|
10791
10936
|
* Executes the mint step for forwarding mode where Circle's relayer handles the mint.
|
|
10792
10937
|
*
|
|
@@ -10800,6 +10945,7 @@ const mockAttestationMessage = {
|
|
|
10800
10945
|
* immediately after the API confirms the mint.
|
|
10801
10946
|
*
|
|
10802
10947
|
* @param params - The bridge parameters containing source, destination, amount and config
|
|
10948
|
+
* @param provider - The CCTP v2 bridging provider
|
|
10803
10949
|
* @param context - The step context containing burnTxHash from the burn step
|
|
10804
10950
|
* @returns Promise resolving to the bridge step with transaction details
|
|
10805
10951
|
* @throws {KitError} If burnTxHash is not available in context
|
|
@@ -10808,10 +10954,13 @@ const mockAttestationMessage = {
|
|
|
10808
10954
|
*
|
|
10809
10955
|
* @example
|
|
10810
10956
|
* ```typescript
|
|
10811
|
-
* const step = await bridgeRelayerMint(
|
|
10957
|
+
* const step = await bridgeRelayerMint(
|
|
10958
|
+
* { params, provider },
|
|
10959
|
+
* { burnTxHash: '0x...' }
|
|
10960
|
+
* )
|
|
10812
10961
|
* console.log('Relayer mint tx:', step.txHash)
|
|
10813
10962
|
* ```
|
|
10814
|
-
*/ async function bridgeRelayerMint(params, context) {
|
|
10963
|
+
*/ async function bridgeRelayerMint({ params, provider }, context) {
|
|
10815
10964
|
const step = {
|
|
10816
10965
|
name: 'mint',
|
|
10817
10966
|
state: 'pending',
|
|
@@ -10827,7 +10976,7 @@ const mockAttestationMessage = {
|
|
|
10827
10976
|
}
|
|
10828
10977
|
try {
|
|
10829
10978
|
// Poll attestation API until relayer confirms the mint
|
|
10830
|
-
const attestation = await fetchRelayerMint(params.source
|
|
10979
|
+
const attestation = await provider.fetchRelayerMint(params.source, context.burnTxHash);
|
|
10831
10980
|
// Extract the relayer's mint transaction hash
|
|
10832
10981
|
const forwardTxHash = attestation.forwardTxHash;
|
|
10833
10982
|
if (!forwardTxHash) {
|
|
@@ -10921,7 +11070,7 @@ const mockAttestationMessage = {
|
|
|
10921
11070
|
name: 'mint',
|
|
10922
11071
|
// bridgeRelayerMint validates context.burnTxHash internally with a KitError
|
|
10923
11072
|
// bridgeMint requires attestationData which is validated here
|
|
10924
|
-
executor: useForwarder ? async (params,
|
|
11073
|
+
executor: useForwarder ? async (params, provider, context)=>{
|
|
10925
11074
|
if (!context) {
|
|
10926
11075
|
throw new KitError({
|
|
10927
11076
|
...InputError.VALIDATION_FAILED,
|
|
@@ -10929,7 +11078,10 @@ const mockAttestationMessage = {
|
|
|
10929
11078
|
message: 'Step context is required for relayer mint'
|
|
10930
11079
|
});
|
|
10931
11080
|
}
|
|
10932
|
-
return bridgeRelayerMint(
|
|
11081
|
+
return bridgeRelayerMint({
|
|
11082
|
+
params,
|
|
11083
|
+
provider
|
|
11084
|
+
}, context);
|
|
10933
11085
|
} : async (params, provider, context)=>{
|
|
10934
11086
|
if (!context?.attestationData) {
|
|
10935
11087
|
throw new KitError({
|
|
@@ -12138,7 +12290,7 @@ const mockAttestationMessage = {
|
|
|
12138
12290
|
return step;
|
|
12139
12291
|
}
|
|
12140
12292
|
|
|
12141
|
-
var version = "1.
|
|
12293
|
+
var version = "1.9.0";
|
|
12142
12294
|
var pkg = {
|
|
12143
12295
|
version: version};
|
|
12144
12296
|
|
|
@@ -13164,6 +13316,39 @@ var pkg = {
|
|
|
13164
13316
|
}
|
|
13165
13317
|
}
|
|
13166
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
|
+
}
|
|
13167
13352
|
/**
|
|
13168
13353
|
* All chains that are supported by the CCTP v2 provider.
|
|
13169
13354
|
*
|
|
@@ -13232,9 +13417,38 @@ var pkg = {
|
|
|
13232
13417
|
* @param config - Optional configuration overrides for the provider
|
|
13233
13418
|
*/ constructor(config = {}){
|
|
13234
13419
|
super();
|
|
13420
|
+
assertCCTPV2Config(config);
|
|
13235
13421
|
this.config = config;
|
|
13236
13422
|
}
|
|
13237
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
|
+
/**
|
|
13238
13452
|
* Execute a cross-chain USDC bridge operation using the CCTP v2 protocol.
|
|
13239
13453
|
*
|
|
13240
13454
|
* This method orchestrates the complete CCTP v2 bridge flow including validation,
|
|
@@ -13660,11 +13874,7 @@ var pkg = {
|
|
|
13660
13874
|
*/ async fetchAttestation(source, transactionHash, config) {
|
|
13661
13875
|
assertCCTPv2WalletContext(source);
|
|
13662
13876
|
try {
|
|
13663
|
-
|
|
13664
|
-
const effectiveConfig = {
|
|
13665
|
-
...this.config?.attestation,
|
|
13666
|
-
...config
|
|
13667
|
-
};
|
|
13877
|
+
const effectiveConfig = this.resolveAttestationConfig(config);
|
|
13668
13878
|
const response = await fetchAttestation(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
|
|
13669
13879
|
const message = response.messages[0];
|
|
13670
13880
|
if (!message) {
|
|
@@ -13681,6 +13891,49 @@ var pkg = {
|
|
|
13681
13891
|
}
|
|
13682
13892
|
}
|
|
13683
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
|
+
/**
|
|
13684
13937
|
* Requests a fresh attestation for an expired attestation.
|
|
13685
13938
|
*
|
|
13686
13939
|
* This method is used when the original attestation has expired before the mint
|
|
@@ -13734,11 +13987,7 @@ var pkg = {
|
|
|
13734
13987
|
throw new Error('Failed to re-attest: Invalid transaction hash');
|
|
13735
13988
|
}
|
|
13736
13989
|
try {
|
|
13737
|
-
|
|
13738
|
-
const effectiveConfig = {
|
|
13739
|
-
...this.config?.attestation,
|
|
13740
|
-
...config
|
|
13741
|
-
};
|
|
13990
|
+
const effectiveConfig = this.resolveAttestationConfig(config);
|
|
13742
13991
|
// Step 1: Get existing attestation data to extract nonce
|
|
13743
13992
|
const existingAttestation = await fetchAttestationWithoutStatusCheck(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
|
|
13744
13993
|
const nonce = existingAttestation.messages[0]?.eventNonce;
|