@circle-fin/provider-cctp-v2 1.0.0 → 1.0.2
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 +100 -0
- package/README.md +4 -2
- package/{index.cjs.js → index.cjs} +432 -203
- package/index.cjs.map +1 -0
- package/index.d.ts +167 -30
- package/index.mjs +430 -202
- package/package.json +7 -3
- package/index.cjs.js.map +0 -1
package/index.mjs
CHANGED
|
@@ -28,10 +28,10 @@ if (typeof window !== 'undefined' && typeof window.Buffer === 'undefined') {
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
import { z } from 'zod';
|
|
31
|
+
import { formatUnits as formatUnits$1 } from '@ethersproject/units';
|
|
31
32
|
import { hexlify, hexZeroPad } from '@ethersproject/bytes';
|
|
32
33
|
import { getAddress } from '@ethersproject/address';
|
|
33
34
|
import bs58 from 'bs58';
|
|
34
|
-
import { formatUnits as formatUnits$1 } from '@ethersproject/units';
|
|
35
35
|
|
|
36
36
|
// -----------------------------------------------------------------------------
|
|
37
37
|
// Blockchain Enum
|
|
@@ -48,6 +48,7 @@ var Blockchain;
|
|
|
48
48
|
Blockchain["Algorand_Testnet"] = "Algorand_Testnet";
|
|
49
49
|
Blockchain["Aptos"] = "Aptos";
|
|
50
50
|
Blockchain["Aptos_Testnet"] = "Aptos_Testnet";
|
|
51
|
+
Blockchain["Arc_Testnet"] = "Arc_Testnet";
|
|
51
52
|
Blockchain["Arbitrum"] = "Arbitrum";
|
|
52
53
|
Blockchain["Arbitrum_Sepolia"] = "Arbitrum_Sepolia";
|
|
53
54
|
Blockchain["Avalanche"] = "Avalanche";
|
|
@@ -268,6 +269,48 @@ const BRIDGE_CONTRACT_EVM_TESTNET = '0xC5567a5E3370d4DBfB0540025078e283e36A363d'
|
|
|
268
269
|
*/
|
|
269
270
|
const BRIDGE_CONTRACT_EVM_MAINNET = '0xB3FA262d0fB521cc93bE83d87b322b8A23DAf3F0';
|
|
270
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Arc Testnet chain definition
|
|
274
|
+
* @remarks
|
|
275
|
+
* This represents the test network for the Arc blockchain,
|
|
276
|
+
* Circle's EVM-compatible Layer-1 designed for stablecoin finance
|
|
277
|
+
* and asset tokenization. Arc uses USDC as the native gas token and
|
|
278
|
+
* features the Malachite Byzantine Fault Tolerant (BFT) consensus
|
|
279
|
+
* engine for sub-second finality.
|
|
280
|
+
*/
|
|
281
|
+
const ArcTestnet = defineChain({
|
|
282
|
+
type: 'evm',
|
|
283
|
+
chain: Blockchain.Arc_Testnet,
|
|
284
|
+
name: 'Arc Testnet',
|
|
285
|
+
title: 'ArcTestnet',
|
|
286
|
+
nativeCurrency: {
|
|
287
|
+
name: 'Arc',
|
|
288
|
+
symbol: 'Arc',
|
|
289
|
+
decimals: 18,
|
|
290
|
+
},
|
|
291
|
+
chainId: 5042002,
|
|
292
|
+
isTestnet: true,
|
|
293
|
+
explorerUrl: 'https://testnet.arcscan.app/tx/{hash}',
|
|
294
|
+
rpcEndpoints: ['https://rpc.testnet.arc.network/'],
|
|
295
|
+
eurcAddress: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
|
|
296
|
+
usdcAddress: '0x3600000000000000000000000000000000000000',
|
|
297
|
+
cctp: {
|
|
298
|
+
domain: 26,
|
|
299
|
+
contracts: {
|
|
300
|
+
v2: {
|
|
301
|
+
type: 'split',
|
|
302
|
+
tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
|
|
303
|
+
messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
|
|
304
|
+
confirmations: 1,
|
|
305
|
+
fastConfirmations: 1,
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
kitContracts: {
|
|
310
|
+
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
|
|
271
314
|
/**
|
|
272
315
|
* Arbitrum Mainnet chain definition
|
|
273
316
|
* @remarks
|
|
@@ -1559,26 +1602,26 @@ const Sonic = defineChain({
|
|
|
1559
1602
|
});
|
|
1560
1603
|
|
|
1561
1604
|
/**
|
|
1562
|
-
* Sonic
|
|
1605
|
+
* Sonic Testnet chain definition
|
|
1563
1606
|
* @remarks
|
|
1564
1607
|
* This represents the official test network for the Sonic blockchain.
|
|
1565
1608
|
*/
|
|
1566
1609
|
const SonicTestnet = defineChain({
|
|
1567
1610
|
type: 'evm',
|
|
1568
1611
|
chain: Blockchain.Sonic_Testnet,
|
|
1569
|
-
name: 'Sonic
|
|
1570
|
-
title: 'Sonic
|
|
1612
|
+
name: 'Sonic Testnet',
|
|
1613
|
+
title: 'Sonic Testnet',
|
|
1571
1614
|
nativeCurrency: {
|
|
1572
1615
|
name: 'Sonic',
|
|
1573
1616
|
symbol: 'S',
|
|
1574
1617
|
decimals: 18,
|
|
1575
1618
|
},
|
|
1576
|
-
chainId:
|
|
1619
|
+
chainId: 14601,
|
|
1577
1620
|
isTestnet: true,
|
|
1578
1621
|
explorerUrl: 'https://testnet.sonicscan.org/tx/{hash}',
|
|
1579
|
-
rpcEndpoints: ['https://rpc.
|
|
1622
|
+
rpcEndpoints: ['https://rpc.testnet.soniclabs.com'],
|
|
1580
1623
|
eurcAddress: null,
|
|
1581
|
-
usdcAddress: '
|
|
1624
|
+
usdcAddress: '0x0BA304580ee7c9a980CF72e55f5Ed2E9fd30Bc51',
|
|
1582
1625
|
cctp: {
|
|
1583
1626
|
domain: 13,
|
|
1584
1627
|
contracts: {
|
|
@@ -2095,6 +2138,7 @@ var Chains = /*#__PURE__*/Object.freeze({
|
|
|
2095
2138
|
AptosTestnet: AptosTestnet,
|
|
2096
2139
|
Arbitrum: Arbitrum,
|
|
2097
2140
|
ArbitrumSepolia: ArbitrumSepolia,
|
|
2141
|
+
ArcTestnet: ArcTestnet,
|
|
2098
2142
|
Avalanche: Avalanche,
|
|
2099
2143
|
AvalancheFuji: AvalancheFuji,
|
|
2100
2144
|
Base: Base,
|
|
@@ -2569,69 +2613,133 @@ class BridgingProvider {
|
|
|
2569
2613
|
}
|
|
2570
2614
|
|
|
2571
2615
|
/**
|
|
2572
|
-
*
|
|
2573
|
-
*
|
|
2574
|
-
* The user agent string is formatted as:
|
|
2575
|
-
* name/version (platform)
|
|
2576
|
-
*
|
|
2577
|
-
* - name: The SDK or kit name, set at runtime.
|
|
2578
|
-
* - version: The SDK version, set at runtime.
|
|
2579
|
-
* - platform: The runtime environment, e.g., "node/nodeVersion" or "browser/browserName".
|
|
2616
|
+
* Detect the runtime environment and return a shortened identifier.
|
|
2580
2617
|
*
|
|
2581
|
-
* @
|
|
2582
|
-
* The kit should call setKitIdentifier(name, version) once at startup.
|
|
2583
|
-
* A custom user agent will be automatically added to all HTTP requests made by the SDK.
|
|
2584
|
-
* This user agent includes the kit name, version, and platform information to help with
|
|
2585
|
-
* debugging and analytics.
|
|
2586
|
-
*
|
|
2587
|
-
* @example
|
|
2588
|
-
* ```typescript
|
|
2589
|
-
* setKitIdentifier('bridge-kit', '1.2.3')
|
|
2590
|
-
* getUserAgent() // "bridge-kit/1.2.3 (node/18.16.0)"
|
|
2591
|
-
* ```
|
|
2592
|
-
*
|
|
2593
|
-
* @returns The Stablecoin Kits SDK user agent string.
|
|
2618
|
+
* @returns Runtime string, e.g., "node/18", "browser/Chrome", or "unknown"
|
|
2594
2619
|
*/
|
|
2595
|
-
|
|
2596
|
-
|
|
2620
|
+
const getRuntime = () => {
|
|
2621
|
+
// Node.js environment
|
|
2597
2622
|
if (typeof process !== 'undefined' &&
|
|
2598
2623
|
typeof process.versions === 'object' &&
|
|
2599
2624
|
typeof process.versions.node === 'string') {
|
|
2600
|
-
|
|
2625
|
+
// Shorten to major version only
|
|
2626
|
+
const majorVersion = process.versions.node.split('.')[0] ?? 'unknown';
|
|
2627
|
+
return `node/${majorVersion}`;
|
|
2601
2628
|
}
|
|
2602
|
-
//
|
|
2629
|
+
// Browser environment
|
|
2603
2630
|
if (typeof window !== 'undefined' && typeof navigator !== 'undefined') {
|
|
2604
2631
|
const userAgent = navigator.userAgent;
|
|
2605
2632
|
const browserMatchers = {
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
'browser/Opera': (ua) => ua.includes('Opera') || ua.includes('OPR'),
|
|
2612
|
-
'browser/Brave': (ua) => ua.includes('Brave'),
|
|
2633
|
+
Edge: (ua) => ua.includes('Edg'),
|
|
2634
|
+
Chrome: (ua) => ua.includes('Chrome') && !ua.includes('Edg'),
|
|
2635
|
+
Firefox: (ua) => ua.includes('Firefox'),
|
|
2636
|
+
Safari: (ua) => ua.includes('Safari') && !ua.includes('Chrome'),
|
|
2637
|
+
Opera: (ua) => ua.includes('Opera') || ua.includes('OPR'),
|
|
2613
2638
|
};
|
|
2614
2639
|
for (const [browserName, matcher] of Object.entries(browserMatchers)) {
|
|
2615
2640
|
if (matcher(userAgent))
|
|
2616
|
-
return browserName
|
|
2641
|
+
return `browser/${browserName}`;
|
|
2617
2642
|
}
|
|
2618
2643
|
return 'browser/unknown';
|
|
2619
2644
|
}
|
|
2620
2645
|
return 'unknown';
|
|
2621
|
-
}
|
|
2646
|
+
};
|
|
2622
2647
|
/**
|
|
2623
|
-
*
|
|
2648
|
+
* Shorten package names by removing common prefixes.
|
|
2624
2649
|
*
|
|
2625
|
-
* @
|
|
2650
|
+
* @param fullName - Full package name, e.g., "\@circle-fin/bridge-kit"
|
|
2651
|
+
* @returns Shortened name, e.g., "bridge-kit"
|
|
2626
2652
|
*/
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2653
|
+
const shortenPackageName = (fullName) => {
|
|
2654
|
+
return fullName.replace('@circle-fin/', '');
|
|
2655
|
+
};
|
|
2656
|
+
/**
|
|
2657
|
+
* Format a component (name/version) with length optimizations.
|
|
2658
|
+
*
|
|
2659
|
+
* @param nameWithVersion - Component identifier, e.g., "\@circle-fin/bridge-kit/1.2.3"
|
|
2660
|
+
* @returns Formatted component, e.g., "bridge-kit/1.2"
|
|
2661
|
+
*/
|
|
2662
|
+
const formatComponent = (nameWithVersion) => {
|
|
2663
|
+
// Find the last / to split name and version
|
|
2664
|
+
// This handles scoped packages like @circle-fin/bridge-kit/1.0.0 correctly
|
|
2665
|
+
const lastSlashIndex = nameWithVersion.lastIndexOf('/');
|
|
2666
|
+
if (lastSlashIndex === -1) {
|
|
2667
|
+
// No version, just a name
|
|
2668
|
+
return shortenPackageName(nameWithVersion);
|
|
2669
|
+
}
|
|
2670
|
+
const name = nameWithVersion.substring(0, lastSlashIndex);
|
|
2671
|
+
const version = nameWithVersion.substring(lastSlashIndex + 1);
|
|
2672
|
+
if (name === '')
|
|
2673
|
+
return '';
|
|
2674
|
+
if (version === '')
|
|
2675
|
+
return shortenPackageName(name);
|
|
2676
|
+
return `${shortenPackageName(name)}/${version}`;
|
|
2677
|
+
};
|
|
2678
|
+
/**
|
|
2679
|
+
* Create a new request context from global state.
|
|
2680
|
+
*
|
|
2681
|
+
* Reads the current external prefix and kit identifier from globalThis.
|
|
2682
|
+
* This is used internally by HTTP utilities.
|
|
2683
|
+
*
|
|
2684
|
+
* @returns A new request context
|
|
2685
|
+
* @internal
|
|
2686
|
+
*/
|
|
2687
|
+
const createRequestContext = () => {
|
|
2688
|
+
const context = {};
|
|
2689
|
+
if (typeof globalThis !== 'undefined') {
|
|
2690
|
+
if (globalThis.__STABLECOIN_KITS_EXTERNAL_PREFIX__ !== undefined) {
|
|
2691
|
+
context.externalPrefix = globalThis.__STABLECOIN_KITS_EXTERNAL_PREFIX__;
|
|
2692
|
+
}
|
|
2693
|
+
if (globalThis.__STABLECOIN_KITS_CURRENT_KIT__ !== undefined) {
|
|
2694
|
+
context.kit = globalThis.__STABLECOIN_KITS_CURRENT_KIT__;
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
return context;
|
|
2698
|
+
};
|
|
2699
|
+
/**
|
|
2700
|
+
* Build a user agent string from a request context.
|
|
2701
|
+
*
|
|
2702
|
+
* Formats the context into a user agent string with automatic optimizations
|
|
2703
|
+
* (shortened package names and truncated versions). The current implementation
|
|
2704
|
+
* tracks a single kit globally.
|
|
2705
|
+
*
|
|
2706
|
+
* Format: `[external-prefix] kit (runtime)`
|
|
2707
|
+
*
|
|
2708
|
+
* @param context - Request context or undefined to use the global context
|
|
2709
|
+
* @returns Formatted user agent string
|
|
2710
|
+
*
|
|
2711
|
+
* @example
|
|
2712
|
+
* ```typescript
|
|
2713
|
+
* // With application prefix and kit
|
|
2714
|
+
* const ua = getUserAgent(context)
|
|
2715
|
+
* // → "my-app/1.0 bridge-kit/1.2 (node/18)"
|
|
2716
|
+
*
|
|
2717
|
+
* // Kit only (no application prefix set)
|
|
2718
|
+
* const ua = getUserAgent()
|
|
2719
|
+
* // → "bridge-kit/1.2 (node/18)"
|
|
2720
|
+
* ```
|
|
2721
|
+
*/
|
|
2722
|
+
const getUserAgent = (context) => {
|
|
2723
|
+
const ctx = createRequestContext();
|
|
2724
|
+
const runtime = getRuntime();
|
|
2725
|
+
// Build user agent string
|
|
2726
|
+
const parts = [];
|
|
2727
|
+
// Add external prefix if set
|
|
2728
|
+
if (ctx.externalPrefix !== undefined) {
|
|
2729
|
+
const formatted = formatComponent(ctx.externalPrefix);
|
|
2730
|
+
if (formatted)
|
|
2731
|
+
parts.push(formatted);
|
|
2732
|
+
}
|
|
2733
|
+
// Add kit if set
|
|
2734
|
+
if (ctx.kit !== undefined) {
|
|
2735
|
+
const formatted = formatComponent(ctx.kit);
|
|
2736
|
+
if (formatted)
|
|
2737
|
+
parts.push(formatted);
|
|
2738
|
+
}
|
|
2739
|
+
// Add runtime
|
|
2740
|
+
parts.push(`(${runtime})`);
|
|
2741
|
+
return parts.join(' ');
|
|
2742
|
+
};
|
|
2635
2743
|
|
|
2636
2744
|
/**
|
|
2637
2745
|
* Default configuration values for the API polling utility.
|
|
@@ -2883,6 +2991,84 @@ const pollApiGet = async (url, isValidType, config) => {
|
|
|
2883
2991
|
return pollApiWithValidation(url, 'GET', isValidType, config);
|
|
2884
2992
|
};
|
|
2885
2993
|
|
|
2994
|
+
/**
|
|
2995
|
+
* Convert a value from its smallest unit representation to a human-readable decimal string.
|
|
2996
|
+
*
|
|
2997
|
+
* This function normalizes token values from their blockchain representation (where
|
|
2998
|
+
* everything is stored as integers in the smallest denomination) to human-readable
|
|
2999
|
+
* decimal format. Uses the battle-tested implementation from @ethersproject/units.
|
|
3000
|
+
*
|
|
3001
|
+
* @param value - The value in smallest units (e.g., "1000000" for 1 USDC with 6 decimals)
|
|
3002
|
+
* @param decimals - The number of decimal places for the unit conversion
|
|
3003
|
+
* @returns A human-readable decimal string (e.g., "1.0")
|
|
3004
|
+
* @throws Error if the value is not a valid numeric string
|
|
3005
|
+
*
|
|
3006
|
+
* @example
|
|
3007
|
+
* ```typescript
|
|
3008
|
+
* import { formatUnits } from '@core/utils'
|
|
3009
|
+
*
|
|
3010
|
+
* // Format USDC (6 decimals)
|
|
3011
|
+
* const usdcFormatted = formatUnits('1000000', 6)
|
|
3012
|
+
* console.log(usdcFormatted) // "1.0"
|
|
3013
|
+
*
|
|
3014
|
+
* // Format ETH (18 decimals)
|
|
3015
|
+
* const ethFormatted = formatUnits('1000000000000000000', 18)
|
|
3016
|
+
* console.log(ethFormatted) // "1.0"
|
|
3017
|
+
*
|
|
3018
|
+
* // Format with fractional part
|
|
3019
|
+
* const fractionalFormatted = formatUnits('1500000', 6)
|
|
3020
|
+
* console.log(fractionalFormatted) // "1.5"
|
|
3021
|
+
* ```
|
|
3022
|
+
*/
|
|
3023
|
+
const formatUnits = (value, decimals) => {
|
|
3024
|
+
return formatUnits$1(value, decimals);
|
|
3025
|
+
};
|
|
3026
|
+
|
|
3027
|
+
/**
|
|
3028
|
+
* Format a token amount into a human-readable decimal string.
|
|
3029
|
+
*
|
|
3030
|
+
* Accepts a smallest-unit string and either assumes USDC's 6 decimals or derives the
|
|
3031
|
+
* native decimals from the provided chain definition. Delegates to {@link formatUnits}
|
|
3032
|
+
* to preserve consistent rounding and formatting behaviour across the SDK.
|
|
3033
|
+
*
|
|
3034
|
+
* @remarks
|
|
3035
|
+
* When `token` is `'native'`, supply a chain identifier that {@link resolveChainIdentifier}
|
|
3036
|
+
* can resolve so the native currency decimals can be determined.
|
|
3037
|
+
*
|
|
3038
|
+
* @param params - The formatting input including the raw value and token selector.
|
|
3039
|
+
* @returns The decimal string representation of the amount.
|
|
3040
|
+
* @throws Error if the value cannot be parsed or if the chain identifier is unknown.
|
|
3041
|
+
*
|
|
3042
|
+
* @example
|
|
3043
|
+
* ```typescript
|
|
3044
|
+
* import { formatAmount } from '@core/utils'
|
|
3045
|
+
* import { Ethereum } from '@core/chains'
|
|
3046
|
+
*
|
|
3047
|
+
* const usdcAmount = formatAmount({ value: '1000000', token: 'USDC' })
|
|
3048
|
+
* console.log(usdcAmount) // "1"
|
|
3049
|
+
*
|
|
3050
|
+
* const ethAmount = formatAmount({
|
|
3051
|
+
* value: '3141592000000000000',
|
|
3052
|
+
* token: 'native',
|
|
3053
|
+
* chain: Ethereum,
|
|
3054
|
+
* })
|
|
3055
|
+
* console.log(ethAmount) // "3.141592"
|
|
3056
|
+
* ```
|
|
3057
|
+
*/
|
|
3058
|
+
const formatAmount = (params) => {
|
|
3059
|
+
const { value, token } = params;
|
|
3060
|
+
switch (token) {
|
|
3061
|
+
case 'USDC':
|
|
3062
|
+
return formatUnits(value, 6);
|
|
3063
|
+
case 'native':
|
|
3064
|
+
return formatUnits(value, resolveChainIdentifier(params.chain).nativeCurrency.decimals);
|
|
3065
|
+
default:
|
|
3066
|
+
// This will cause a compile-time error if a new token type is added to
|
|
3067
|
+
// `FormatAmountParams` but not handled in this switch statement, ensuring exhaustiveness.
|
|
3068
|
+
throw new Error(`formatAmount: Unhandled token type: ${token}`);
|
|
3069
|
+
}
|
|
3070
|
+
};
|
|
3071
|
+
|
|
2886
3072
|
/**
|
|
2887
3073
|
* Custom error class for insufficient funds errors.
|
|
2888
3074
|
* Provides structured error information while hiding implementation details.
|
|
@@ -2890,7 +3076,7 @@ const pollApiGet = async (url, isValidType, config) => {
|
|
|
2890
3076
|
class InsufficientFundsError extends Error {
|
|
2891
3077
|
balance;
|
|
2892
3078
|
amount;
|
|
2893
|
-
|
|
3079
|
+
token;
|
|
2894
3080
|
tokenAddress;
|
|
2895
3081
|
chain;
|
|
2896
3082
|
/**
|
|
@@ -2898,16 +3084,27 @@ class InsufficientFundsError extends Error {
|
|
|
2898
3084
|
*
|
|
2899
3085
|
* @param balance - The current token balance in the wallet (smallest unit).
|
|
2900
3086
|
* @param amount - The required transaction amount (smallest unit).
|
|
2901
|
-
* @param
|
|
3087
|
+
* @param token - The token ('USDC' or 'native').
|
|
2902
3088
|
* @param tokenAddress - The contract address of the token.
|
|
2903
3089
|
* @param chain - The blockchain network name where the transaction failed.
|
|
2904
3090
|
*/
|
|
2905
|
-
constructor(balance, amount,
|
|
2906
|
-
const
|
|
3091
|
+
constructor(balance, amount, token, tokenAddress, chain) {
|
|
3092
|
+
const formattedBalance = formatAmount({
|
|
3093
|
+
value: balance,
|
|
3094
|
+
token,
|
|
3095
|
+
chain,
|
|
3096
|
+
});
|
|
3097
|
+
const formattedAmount = formatAmount({
|
|
3098
|
+
value: amount,
|
|
3099
|
+
token,
|
|
3100
|
+
chain,
|
|
3101
|
+
});
|
|
3102
|
+
const chainName = typeof chain === 'object' && 'name' in chain ? chain.name : chain;
|
|
3103
|
+
const message = `Insufficient funds for ${token} (${tokenAddress}) transaction on ${chainName}. Balance: ${formattedBalance} ${token}, is less than transaction amount: ${formattedAmount} ${token}.`;
|
|
2907
3104
|
super(message);
|
|
2908
3105
|
this.balance = balance;
|
|
2909
3106
|
this.amount = amount;
|
|
2910
|
-
this.
|
|
3107
|
+
this.token = token;
|
|
2911
3108
|
this.tokenAddress = tokenAddress;
|
|
2912
3109
|
this.chain = chain;
|
|
2913
3110
|
this.name = 'InsufficientFundsError';
|
|
@@ -3286,39 +3483,6 @@ const convertAddress = (address, targetFormat) => {
|
|
|
3286
3483
|
throw new Error(`Unsupported address format: ${address}`);
|
|
3287
3484
|
};
|
|
3288
3485
|
|
|
3289
|
-
/**
|
|
3290
|
-
* Convert a value from its smallest unit representation to a human-readable decimal string.
|
|
3291
|
-
*
|
|
3292
|
-
* This function normalizes token values from their blockchain representation (where
|
|
3293
|
-
* everything is stored as integers in the smallest denomination) to human-readable
|
|
3294
|
-
* decimal format. Uses the battle-tested implementation from @ethersproject/units.
|
|
3295
|
-
*
|
|
3296
|
-
* @param value - The value in smallest units (e.g., "1000000" for 1 USDC with 6 decimals)
|
|
3297
|
-
* @param decimals - The number of decimal places for the unit conversion
|
|
3298
|
-
* @returns A human-readable decimal string (e.g., "1.0")
|
|
3299
|
-
* @throws Error if the value is not a valid numeric string
|
|
3300
|
-
*
|
|
3301
|
-
* @example
|
|
3302
|
-
* ```typescript
|
|
3303
|
-
* import { formatUnits } from '@core/utils'
|
|
3304
|
-
*
|
|
3305
|
-
* // Format USDC (6 decimals)
|
|
3306
|
-
* const usdcFormatted = formatUnits('1000000', 6)
|
|
3307
|
-
* console.log(usdcFormatted) // "1.0"
|
|
3308
|
-
*
|
|
3309
|
-
* // Format ETH (18 decimals)
|
|
3310
|
-
* const ethFormatted = formatUnits('1000000000000000000', 18)
|
|
3311
|
-
* console.log(ethFormatted) // "1.0"
|
|
3312
|
-
*
|
|
3313
|
-
* // Format with fractional part
|
|
3314
|
-
* const fractionalFormatted = formatUnits('1500000', 6)
|
|
3315
|
-
* console.log(fractionalFormatted) // "1.5"
|
|
3316
|
-
* ```
|
|
3317
|
-
*/
|
|
3318
|
-
const formatUnits = (value, decimals) => {
|
|
3319
|
-
return formatUnits$1(value, decimals);
|
|
3320
|
-
};
|
|
3321
|
-
|
|
3322
3486
|
/**
|
|
3323
3487
|
* Build a complete explorer URL from a chain definition and transaction hash.
|
|
3324
3488
|
*
|
|
@@ -3580,9 +3744,9 @@ const customFeeSchema = z
|
|
|
3580
3744
|
* token: 'USDC',
|
|
3581
3745
|
* config: {
|
|
3582
3746
|
* transferSpeed: 'FAST',
|
|
3583
|
-
* maxFee: '
|
|
3747
|
+
* maxFee: '1.5', // Decimal format
|
|
3584
3748
|
* customFee: {
|
|
3585
|
-
* value: '
|
|
3749
|
+
* value: '0.5', // Decimal format
|
|
3586
3750
|
* recipientAddress: '0x1234567890123456789012345678901234567890'
|
|
3587
3751
|
* }
|
|
3588
3752
|
* }
|
|
@@ -3609,7 +3773,12 @@ const bridgeParamsSchema = z.object({
|
|
|
3609
3773
|
transferSpeed: z.nativeEnum(TransferSpeed).optional(),
|
|
3610
3774
|
maxFee: z
|
|
3611
3775
|
.string()
|
|
3612
|
-
.
|
|
3776
|
+
.pipe(createDecimalStringValidator({
|
|
3777
|
+
allowZero: true,
|
|
3778
|
+
regexMessage: 'maxFee must be a numeric string with optional decimal places (e.g., "1", "0.5", "1.5")',
|
|
3779
|
+
attributeName: 'maxFee',
|
|
3780
|
+
maxDecimals: 6,
|
|
3781
|
+
})(z.string()))
|
|
3613
3782
|
.optional(),
|
|
3614
3783
|
customFee: customFeeSchema.optional(),
|
|
3615
3784
|
}),
|
|
@@ -3739,9 +3908,11 @@ async function fetchUsdcFastBurnFee(sourceDomain, destinationDomain, isTestnet)
|
|
|
3739
3908
|
* amount and invalid atttestation messages.
|
|
3740
3909
|
*
|
|
3741
3910
|
* These values were obtained by averaging gas for the function call from the last 3 months.
|
|
3911
|
+
* see: https://dune.com/queries/6022210/9697710/
|
|
3742
3912
|
*/
|
|
3743
|
-
const DEPOSIT_FOR_BURN_GAS_ESTIMATE_EVM =
|
|
3744
|
-
const
|
|
3913
|
+
const DEPOSIT_FOR_BURN_GAS_ESTIMATE_EVM = 169914n; // (99p: 111_521n + max: 226_506n) / 2 = 169_914n
|
|
3914
|
+
const CUSTOM_BURN_GAS_ESTIMATE_EVM = 201525n; // p99 and max are same here: 201_525n
|
|
3915
|
+
const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237401n; // (99p: 163_963n + max: 310_839n) / 2 = 237_401n
|
|
3745
3916
|
/**
|
|
3746
3917
|
* The minimum finality threshold for CCTPv2 transfers.
|
|
3747
3918
|
*
|
|
@@ -3766,8 +3937,8 @@ const CCTPv2MinFinalityThreshold = {
|
|
|
3766
3937
|
*/
|
|
3767
3938
|
const DEFAULT_CONFIG = {
|
|
3768
3939
|
timeout: 2_000, // 2 seconds
|
|
3769
|
-
maxRetries: 30 * 20, // 2 seconds
|
|
3770
|
-
retryDelay:
|
|
3940
|
+
maxRetries: 30 * 20, // 30 * 20 * 2 seconds (from the retry delay) = 20 minutes (to account for slow transfers maximum time based on confirmations)
|
|
3941
|
+
retryDelay: 2_000, // 2 seconds
|
|
3771
3942
|
headers: {
|
|
3772
3943
|
'Content-Type': 'application/json',
|
|
3773
3944
|
},
|
|
@@ -4199,6 +4370,10 @@ class KitError extends Error {
|
|
|
4199
4370
|
}
|
|
4200
4371
|
}
|
|
4201
4372
|
|
|
4373
|
+
/**
|
|
4374
|
+
* Minimum error code for INPUT type errors.
|
|
4375
|
+
* INPUT errors represent validation failures and invalid parameters.
|
|
4376
|
+
*/
|
|
4202
4377
|
/**
|
|
4203
4378
|
* Standardized error definitions for INPUT type errors.
|
|
4204
4379
|
*
|
|
@@ -4552,7 +4727,9 @@ async function assertCCTPv2AttestationParams(attestation, params) {
|
|
|
4552
4727
|
const errors = [];
|
|
4553
4728
|
const message = attestation.decodedMessage;
|
|
4554
4729
|
const messageBody = message.decodedMessageBody;
|
|
4555
|
-
|
|
4730
|
+
// Use recipientAddress if provided, otherwise use destination.address
|
|
4731
|
+
const destinationAddressForMint = params.destination.recipientAddress ?? params.destination.address;
|
|
4732
|
+
const mintRecipient = await getMintRecipientAccount(params.destination.chain.type, destinationAddressForMint, params.destination.chain.usdcAddress);
|
|
4556
4733
|
let sender;
|
|
4557
4734
|
if (hasCustomContractSupport(params.source.chain, 'bridge')) {
|
|
4558
4735
|
if (params.source.chain.type === 'solana') {
|
|
@@ -4684,8 +4861,6 @@ async function bridgeApproval({ params, provider, }) {
|
|
|
4684
4861
|
adapter: params.source.adapter,
|
|
4685
4862
|
chain: params.source.chain,
|
|
4686
4863
|
request: await provider.approve(params.source, approvalAmount),
|
|
4687
|
-
// We need to wait for the approval to be confirmed on chain before the transfer can be executed
|
|
4688
|
-
confirmations: 2,
|
|
4689
4864
|
});
|
|
4690
4865
|
}
|
|
4691
4866
|
|
|
@@ -4699,8 +4874,8 @@ async function bridgeApproval({ params, provider, }) {
|
|
|
4699
4874
|
* @param params - The bridge parameters containing source, destination, amount and optional config
|
|
4700
4875
|
* @param sourceChain - The source chain definition where the burn will occur
|
|
4701
4876
|
* @returns Promise resolving to the bridge step with transaction details
|
|
4702
|
-
* @throws {ValidationError} If the parameters are invalid
|
|
4703
|
-
* @throws {BridgeError} If the burn transaction fails
|
|
4877
|
+
* @throws \{ValidationError\} If the parameters are invalid
|
|
4878
|
+
* @throws \{BridgeError\} If the burn transaction fails
|
|
4704
4879
|
*
|
|
4705
4880
|
* @example
|
|
4706
4881
|
* ```typescript
|
|
@@ -4709,16 +4884,11 @@ async function bridgeApproval({ params, provider, }) {
|
|
|
4709
4884
|
* ```
|
|
4710
4885
|
*/
|
|
4711
4886
|
async function bridgeBurn({ params, provider, }) {
|
|
4712
|
-
const transferSpeed = params.config?.transferSpeed ?? TransferSpeed.FAST;
|
|
4713
4887
|
return await executePreparedChainRequest({
|
|
4714
4888
|
name: 'burn',
|
|
4715
4889
|
adapter: params.source.adapter,
|
|
4716
4890
|
chain: params.source.chain,
|
|
4717
4891
|
request: await provider.burn(params),
|
|
4718
|
-
confirmations: transferSpeed === TransferSpeed.FAST
|
|
4719
|
-
? params.source.chain.cctp.contracts.v2.fastConfirmations
|
|
4720
|
-
: params.source.chain.cctp.contracts.v2.confirmations,
|
|
4721
|
-
timeout: transferSpeed === TransferSpeed.FAST ? undefined : 1_200_000, // 20 minutes (max timeout for cctpv2 slow transfers)
|
|
4722
4892
|
});
|
|
4723
4893
|
}
|
|
4724
4894
|
|
|
@@ -4730,16 +4900,18 @@ async function bridgeBurn({ params, provider, }) {
|
|
|
4730
4900
|
* and authorizes the minting of equivalent tokens.
|
|
4731
4901
|
*
|
|
4732
4902
|
* @param params - The bridge parameters containing source, destination, amount and optional config
|
|
4733
|
-
* @param
|
|
4734
|
-
* @
|
|
4735
|
-
* @
|
|
4736
|
-
* @throws {
|
|
4737
|
-
* @throws {BridgeError} If the attestation fetch fails
|
|
4903
|
+
* @param txHash - The hash of the burn transaction
|
|
4904
|
+
* @returns Promise resolving to the bridge step with attestation data
|
|
4905
|
+
* @throws \{ValidationError\} If the parameters are invalid
|
|
4906
|
+
* @throws \{BridgeError\} If the attestation fetch fails
|
|
4738
4907
|
*
|
|
4739
4908
|
* @example
|
|
4740
4909
|
* ```typescript
|
|
4741
|
-
* const attestationStep = await
|
|
4742
|
-
*
|
|
4910
|
+
* const attestationStep = await bridgeFetchAttestation(
|
|
4911
|
+
* { params, provider },
|
|
4912
|
+
* burnTxHash
|
|
4913
|
+
* )
|
|
4914
|
+
* console.log('Attestation data:', attestationStep.data)
|
|
4743
4915
|
* ```
|
|
4744
4916
|
*/
|
|
4745
4917
|
async function bridgeFetchAttestation({ params, provider, }, txHash) {
|
|
@@ -4748,7 +4920,17 @@ async function bridgeFetchAttestation({ params, provider, }, txHash) {
|
|
|
4748
4920
|
state: 'pending',
|
|
4749
4921
|
};
|
|
4750
4922
|
try {
|
|
4751
|
-
|
|
4923
|
+
/**
|
|
4924
|
+
* Give slow transfers a longer retry delay of 30 seconds to account for the longer time it takes to get the
|
|
4925
|
+
* attestation so that we do not hit the rate limit of the IRIS API or the retry limit of the API polling utility.
|
|
4926
|
+
* We only do this for slow transfers on source chains that have greater than 20 confirmations (5 minutes of blocktime
|
|
4927
|
+
* on the slowest blocktime chains) so that we do not slow down the attestation polling for lower confirmation chains.
|
|
4928
|
+
*/
|
|
4929
|
+
const apiPollingConfig = params.config?.transferSpeed === TransferSpeed.SLOW &&
|
|
4930
|
+
params.source.chain.cctp.contracts.v2.confirmations > 20
|
|
4931
|
+
? { retryDelay: 30_000 }
|
|
4932
|
+
: undefined;
|
|
4933
|
+
const attestation = await provider.fetchAttestation(params.source, txHash, apiPollingConfig);
|
|
4752
4934
|
step = {
|
|
4753
4935
|
...step,
|
|
4754
4936
|
state: 'success',
|
|
@@ -4756,10 +4938,18 @@ async function bridgeFetchAttestation({ params, provider, }, txHash) {
|
|
|
4756
4938
|
};
|
|
4757
4939
|
}
|
|
4758
4940
|
catch (err) {
|
|
4941
|
+
let errorMessage = 'Unknown attestation error';
|
|
4942
|
+
if (err instanceof Error) {
|
|
4943
|
+
errorMessage = err.message;
|
|
4944
|
+
}
|
|
4945
|
+
else if (typeof err === 'string') {
|
|
4946
|
+
errorMessage = err;
|
|
4947
|
+
}
|
|
4759
4948
|
step = {
|
|
4760
4949
|
...step,
|
|
4761
4950
|
state: 'error',
|
|
4762
4951
|
error: err,
|
|
4952
|
+
errorMessage,
|
|
4763
4953
|
data: undefined,
|
|
4764
4954
|
};
|
|
4765
4955
|
}
|
|
@@ -4795,10 +4985,37 @@ async function bridgeMint({ params, provider, }, attestation) {
|
|
|
4795
4985
|
adapter: params.destination.adapter,
|
|
4796
4986
|
chain: params.destination.chain,
|
|
4797
4987
|
request: await provider.mint(params.source, params.destination, attestation),
|
|
4798
|
-
// We will wait for the mint to be confirmed on chain before we will return is as success
|
|
4799
|
-
confirmations: 2,
|
|
4800
4988
|
});
|
|
4801
4989
|
}
|
|
4990
|
+
const mockAttestationMessage = {
|
|
4991
|
+
attestation: '0x8bd9b9e63eb05128eb2896b63e3e1df39bfd6bbfb893b69dc53c39252aeb85df1ded70263b07da17abf88e6e1b77f16ebcdb4eb1c6b4ea4c625215e5cb9dddb81bffe0b9d75e2094f05e48f18f70d0b254999bde90bab26f5c0da29b2d7e00feca1cfed119cba0d2a0e887648a40e803e0ca34aa8fd94ce068515eeaef72b520aa1b',
|
|
4992
|
+
message: '0x000000010000000000000006f446cb82eb486fef485c14c301eaab73aa35f87e3feda3547acf0f33cd4b40f30000000000000000000000008fe6b999dc680ccfdd5bf7eb0974218be2542daa0000000000000000000000008fe6b999dc680ccfdd5bf7eb0974218be2542daa0000000000000000000000000000000000000000000000000000000000000000000003e8000003e8000000010000000000000000000000001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000023f9a5bea7b92a0638520607407bc7f0310aeed400000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000c5567a5e3370d4dbfb0540025078e283e36a363d000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000001f6b158',
|
|
4993
|
+
eventNonce: '0xf446cb82eb486fef485c14c301eaab73aa35f87e3feda3547acf0f33cd4b40f3',
|
|
4994
|
+
cctpVersion: 2,
|
|
4995
|
+
status: 'complete',
|
|
4996
|
+
decodedMessage: {
|
|
4997
|
+
sourceDomain: '0',
|
|
4998
|
+
destinationDomain: '6',
|
|
4999
|
+
nonce: '0xf446cb82eb486fef485c14c301eaab73aa35f87e3feda3547acf0f33cd4b40f3',
|
|
5000
|
+
sender: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
|
|
5001
|
+
recipient: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
|
|
5002
|
+
destinationCaller: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
5003
|
+
minFinalityThreshold: '1000',
|
|
5004
|
+
finalityThresholdExecuted: '1000',
|
|
5005
|
+
messageBody: '0x000000010000000000000000000000001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000023f9a5bea7b92a0638520607407bc7f0310aeed400000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000c5567a5e3370d4dbfb0540025078e283e36a363d000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000001f6b158',
|
|
5006
|
+
decodedMessageBody: {
|
|
5007
|
+
burnToken: '0x1c7d4b196cb0c7b01d743fbc6116a902379c7238',
|
|
5008
|
+
mintRecipient: '0x23f9a5bea7b92a0638520607407bc7f0310aeed4',
|
|
5009
|
+
amount: '100000',
|
|
5010
|
+
messageSender: '0xc5567a5e3370d4dbfb0540025078e283e36a363d',
|
|
5011
|
+
maxFee: '11',
|
|
5012
|
+
feeExecuted: '10',
|
|
5013
|
+
expirationBlock: '32944472',
|
|
5014
|
+
hookData: null,
|
|
5015
|
+
},
|
|
5016
|
+
},
|
|
5017
|
+
delayReason: null,
|
|
5018
|
+
};
|
|
4802
5019
|
|
|
4803
5020
|
/**
|
|
4804
5021
|
* Ordered sequence of CCTP v2 bridge step executors.
|
|
@@ -4882,6 +5099,56 @@ function handleStepError(stepName, error, result) {
|
|
|
4882
5099
|
});
|
|
4883
5100
|
}
|
|
4884
5101
|
|
|
5102
|
+
/**
|
|
5103
|
+
* Dispatch a bridge step event through the provider's action dispatcher.
|
|
5104
|
+
*
|
|
5105
|
+
* Constructs the appropriate action payload and dispatches it to any registered
|
|
5106
|
+
* event listeners. Handles type-safe dispatching for different step types.
|
|
5107
|
+
*
|
|
5108
|
+
* @param name - The step name (approve, burn, fetchAttestation, or mint).
|
|
5109
|
+
* @param step - The completed bridge step containing transaction details and explorerUrl.
|
|
5110
|
+
* @param provider - The CCTP v2 provider with action dispatcher.
|
|
5111
|
+
*
|
|
5112
|
+
* @example
|
|
5113
|
+
* ```typescript
|
|
5114
|
+
* const step: BridgeStep = {
|
|
5115
|
+
* name: 'burn',
|
|
5116
|
+
* state: 'success',
|
|
5117
|
+
* txHash: '0xabc...',
|
|
5118
|
+
* explorerUrl: 'https://sepolia.etherscan.io/tx/0xabc...',
|
|
5119
|
+
* data: { ... }
|
|
5120
|
+
* }
|
|
5121
|
+
* dispatchStepEvent('burn', step, provider)
|
|
5122
|
+
* ```
|
|
5123
|
+
*/
|
|
5124
|
+
function dispatchStepEvent(name, step, provider) {
|
|
5125
|
+
if (!provider.actionDispatcher) {
|
|
5126
|
+
return;
|
|
5127
|
+
}
|
|
5128
|
+
const actionValues = {
|
|
5129
|
+
protocol: 'cctp',
|
|
5130
|
+
version: 'v2',
|
|
5131
|
+
values: step,
|
|
5132
|
+
};
|
|
5133
|
+
switch (name) {
|
|
5134
|
+
case 'approve':
|
|
5135
|
+
case 'burn':
|
|
5136
|
+
case 'mint':
|
|
5137
|
+
provider.actionDispatcher.dispatch(name, {
|
|
5138
|
+
...actionValues,
|
|
5139
|
+
method: name,
|
|
5140
|
+
});
|
|
5141
|
+
break;
|
|
5142
|
+
case 'fetchAttestation':
|
|
5143
|
+
provider.actionDispatcher.dispatch(name, {
|
|
5144
|
+
...actionValues,
|
|
5145
|
+
method: name,
|
|
5146
|
+
values: step,
|
|
5147
|
+
});
|
|
5148
|
+
break;
|
|
5149
|
+
}
|
|
5150
|
+
}
|
|
5151
|
+
|
|
4885
5152
|
/**
|
|
4886
5153
|
* Execute a cross-chain USDC bridge using the CCTP v2 protocol.
|
|
4887
5154
|
*
|
|
@@ -4938,34 +5205,19 @@ async function bridge(params, provider) {
|
|
|
4938
5205
|
try {
|
|
4939
5206
|
const step = await executor(params, provider, context);
|
|
4940
5207
|
if (step.state === 'error') {
|
|
4941
|
-
// Include error details from the step in the thrown error
|
|
4942
|
-
|
|
5208
|
+
// Include error details from the step in the thrown error. Fall back to step.error.
|
|
5209
|
+
let fallbackErrorMessage = 'Unknown error';
|
|
5210
|
+
if (step.error instanceof Error) {
|
|
5211
|
+
fallbackErrorMessage = step.error.message;
|
|
5212
|
+
}
|
|
5213
|
+
else if (typeof step.error === 'string') {
|
|
5214
|
+
fallbackErrorMessage = step.error;
|
|
5215
|
+
}
|
|
5216
|
+
const errorDetails = step.errorMessage ?? fallbackErrorMessage;
|
|
4943
5217
|
throw new Error(`${name} step failed: ${errorDetails}`);
|
|
4944
5218
|
}
|
|
4945
5219
|
context = updateContext?.(step);
|
|
4946
|
-
|
|
4947
|
-
protocol: 'cctp',
|
|
4948
|
-
version: 'v2',
|
|
4949
|
-
values: step,
|
|
4950
|
-
};
|
|
4951
|
-
// use a switch statement for type safety to separate the different step types
|
|
4952
|
-
switch (name) {
|
|
4953
|
-
case 'approve':
|
|
4954
|
-
case 'burn':
|
|
4955
|
-
case 'mint':
|
|
4956
|
-
provider.actionDispatcher?.dispatch(name, {
|
|
4957
|
-
...actionValues,
|
|
4958
|
-
method: name,
|
|
4959
|
-
});
|
|
4960
|
-
break;
|
|
4961
|
-
case 'fetchAttestation':
|
|
4962
|
-
provider.actionDispatcher?.dispatch(name, {
|
|
4963
|
-
...actionValues,
|
|
4964
|
-
method: name,
|
|
4965
|
-
values: step,
|
|
4966
|
-
});
|
|
4967
|
-
break;
|
|
4968
|
-
}
|
|
5220
|
+
dispatchStepEvent(name, step, provider);
|
|
4969
5221
|
result.steps.push(step);
|
|
4970
5222
|
}
|
|
4971
5223
|
catch (error) {
|
|
@@ -5261,10 +5513,12 @@ z.object({
|
|
|
5261
5513
|
*/
|
|
5262
5514
|
const validateBalanceForTransaction = async (params) => {
|
|
5263
5515
|
const { amount, adapter, token, tokenAddress, operationContext } = params;
|
|
5264
|
-
const balancePrepared = await adapter.prepareAction('usdc.balanceOf', {
|
|
5516
|
+
const balancePrepared = await adapter.prepareAction('usdc.balanceOf', {
|
|
5517
|
+
walletAddress: operationContext.address,
|
|
5518
|
+
}, operationContext);
|
|
5265
5519
|
const balance = await balancePrepared.execute();
|
|
5266
5520
|
if (BigInt(balance) < BigInt(amount)) {
|
|
5267
|
-
throw new InsufficientFundsError(balance.toString(), amount, token, tokenAddress,
|
|
5521
|
+
throw new InsufficientFundsError(balance.toString(), amount, token, tokenAddress, operationContext.chain);
|
|
5268
5522
|
}
|
|
5269
5523
|
};
|
|
5270
5524
|
|
|
@@ -5632,12 +5886,7 @@ async function retry(result, context, provider) {
|
|
|
5632
5886
|
throw new Error(errorMessage);
|
|
5633
5887
|
}
|
|
5634
5888
|
stepContext = updateContext?.(step);
|
|
5635
|
-
|
|
5636
|
-
protocol: 'cctp',
|
|
5637
|
-
version: 'v2',
|
|
5638
|
-
method: name,
|
|
5639
|
-
values: step.data,
|
|
5640
|
-
});
|
|
5889
|
+
dispatchStepEvent(name, step, provider);
|
|
5641
5890
|
result.steps.push(step);
|
|
5642
5891
|
}
|
|
5643
5892
|
catch (error) {
|
|
@@ -5833,22 +6082,22 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
5833
6082
|
async estimate(params) {
|
|
5834
6083
|
// CCTP-specific transfer params validation (includes base validation)
|
|
5835
6084
|
assertCCTPv2BridgeParams(params);
|
|
5836
|
-
const { source, destination, amount
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
}
|
|
6085
|
+
const { source, destination, amount } = params;
|
|
6086
|
+
const estimateBurn = async () => {
|
|
6087
|
+
const burn = await this.burn(params);
|
|
6088
|
+
return await burn.estimate(undefined, await source.adapter.calculateTransactionFee(hasCustomContractSupport(source.chain, 'bridge')
|
|
6089
|
+
? CUSTOM_BURN_GAS_ESTIMATE_EVM
|
|
6090
|
+
: DEPOSIT_FOR_BURN_GAS_ESTIMATE_EVM, undefined, source.chain));
|
|
6091
|
+
};
|
|
6092
|
+
const estimateMint = async () => {
|
|
6093
|
+
const mint = await this.mint(source, destination, mockAttestationMessage);
|
|
6094
|
+
return await mint.estimate(undefined, await destination.adapter.calculateTransactionFee(RECEIVE_MESSAGE_GAS_ESTIMATE_EVM, undefined, destination.chain));
|
|
6095
|
+
};
|
|
5847
6096
|
// Parallelize all independent async operations
|
|
5848
6097
|
const [approveEstimate, depositForBurnFee, receiveMessageFee, maxFee] = await Promise.allSettled([
|
|
5849
6098
|
this.approve(source, amount).then(async (approve) => approve.estimate()),
|
|
5850
|
-
|
|
5851
|
-
|
|
6099
|
+
estimateBurn(),
|
|
6100
|
+
estimateMint(),
|
|
5852
6101
|
params.config?.transferSpeed === undefined ||
|
|
5853
6102
|
params.config?.transferSpeed === TransferSpeed.FAST
|
|
5854
6103
|
? this.getMaxFee(params)
|
|
@@ -6015,14 +6264,7 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6015
6264
|
delegate: spenderAddress,
|
|
6016
6265
|
chain: resolvedContext?.chain ?? chain,
|
|
6017
6266
|
};
|
|
6018
|
-
|
|
6019
|
-
if (resolvedContext) {
|
|
6020
|
-
return await adapter.prepareAction('usdc.increaseAllowance', actionParams, resolvedContext);
|
|
6021
|
-
}
|
|
6022
|
-
else {
|
|
6023
|
-
// Legacy fallback for adapters without capabilities
|
|
6024
|
-
return await adapter.prepareAction('usdc.increaseAllowance', actionParams);
|
|
6025
|
-
}
|
|
6267
|
+
return await adapter.prepareAction('usdc.increaseAllowance', actionParams, resolvedContext);
|
|
6026
6268
|
}
|
|
6027
6269
|
/**
|
|
6028
6270
|
* Prepares a CCTP v2 token minting transaction on the destination chain.
|
|
@@ -6066,14 +6308,8 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6066
6308
|
assertCCTPv2WalletContext(destination);
|
|
6067
6309
|
// Extract operation context from destination wallet context
|
|
6068
6310
|
const operationContext = this.extractOperationContext(destination);
|
|
6069
|
-
//
|
|
6070
|
-
|
|
6071
|
-
try {
|
|
6072
|
-
resolvedContext = await resolveOperationContext(destination.adapter, operationContext);
|
|
6073
|
-
}
|
|
6074
|
-
catch (error) {
|
|
6075
|
-
throw new Error(`Failed to resolve operation context: ${error instanceof Error ? error.message : String(error)}`);
|
|
6076
|
-
}
|
|
6311
|
+
// Use recipientAddress if provided, otherwise use destination.address
|
|
6312
|
+
const destinationAddressForMint = destination.recipientAddress ?? destination.address;
|
|
6077
6313
|
// Prepare action parameters
|
|
6078
6314
|
const actionParams = {
|
|
6079
6315
|
fromChain: source.chain,
|
|
@@ -6081,16 +6317,9 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6081
6317
|
message: attestation.message,
|
|
6082
6318
|
attestation: attestation.attestation,
|
|
6083
6319
|
eventNonce: attestation.eventNonce,
|
|
6084
|
-
destinationAddress:
|
|
6320
|
+
destinationAddress: destinationAddressForMint,
|
|
6085
6321
|
};
|
|
6086
|
-
|
|
6087
|
-
if (resolvedContext) {
|
|
6088
|
-
return await destination.adapter.prepareAction('cctp.v2.receiveMessage', actionParams, resolvedContext);
|
|
6089
|
-
}
|
|
6090
|
-
else {
|
|
6091
|
-
// Legacy fallback for adapters without capabilities
|
|
6092
|
-
return await destination.adapter.prepareAction('cctp.v2.receiveMessage', actionParams);
|
|
6093
|
-
}
|
|
6322
|
+
return await destination.adapter.prepareAction('cctp.v2.receiveMessage', actionParams, operationContext);
|
|
6094
6323
|
}
|
|
6095
6324
|
/**
|
|
6096
6325
|
* Fetches attestation data for a burn transaction from the IRIS API.
|
|
@@ -6206,7 +6435,9 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6206
6435
|
* - `source`: The source wallet context with chain definition and adapter
|
|
6207
6436
|
* - `destination`: The destination wallet context with chain definition and adapter
|
|
6208
6437
|
* - `amount`: The bridge amount in minor units (e.g., "1000000" for 1 USDC)
|
|
6209
|
-
* - `config`: Optional bridge configuration including transferSpeed and maxFee settings
|
|
6438
|
+
* - `config`: Optional bridge configuration including transferSpeed and maxFee settings.
|
|
6439
|
+
* When used via BridgeKit, fee values are automatically converted from human-readable
|
|
6440
|
+
* format (e.g., "1") to smallest units (e.g., "1000000").
|
|
6210
6441
|
* @returns The maximum fee to be used for the bridge operation in minor units
|
|
6211
6442
|
*
|
|
6212
6443
|
* @example
|
|
@@ -6214,9 +6445,12 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6214
6445
|
* const maxFee = await provider.getMaxFee({
|
|
6215
6446
|
* source: { adapter: sourceAdapter, chain: Chains.Ethereum, address: '0x...' },
|
|
6216
6447
|
* destination: { adapter: destAdapter, chain: Chains.Base, address: '0x...' },
|
|
6217
|
-
* amount: '1000000', // 1 USDC
|
|
6448
|
+
* amount: '1000000', // 1 USDC in minor units
|
|
6218
6449
|
* token: 'USDC',
|
|
6219
|
-
* config: {
|
|
6450
|
+
* config: {
|
|
6451
|
+
* transferSpeed: TransferSpeed.FAST,
|
|
6452
|
+
* maxFee: '1000000' // Provider receives values in smallest units
|
|
6453
|
+
* }
|
|
6220
6454
|
* })
|
|
6221
6455
|
* console.log('Max fee:', maxFee)
|
|
6222
6456
|
* ```
|
|
@@ -6247,7 +6481,7 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6247
6481
|
maxFee = baseFee + baseFee / 10n;
|
|
6248
6482
|
}
|
|
6249
6483
|
else {
|
|
6250
|
-
// Use the max fee from the config
|
|
6484
|
+
// Use the max fee from the config (converted to minor units by BridgeKit)
|
|
6251
6485
|
maxFee = BigInt(config.maxFee);
|
|
6252
6486
|
}
|
|
6253
6487
|
return maxFee;
|
|
@@ -6284,10 +6518,11 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6284
6518
|
// Get the min finality threshold based on the transfer speed
|
|
6285
6519
|
const minFinalityThreshold = CCTPv2MinFinalityThreshold[transferSpeed];
|
|
6286
6520
|
// Calculate the max fee
|
|
6521
|
+
// Use recipientAddress if provided, otherwise use destination.address
|
|
6522
|
+
const destinationAddressForMint = destination.recipientAddress ?? destination.address;
|
|
6287
6523
|
const [maxFee, mintRecipient] = await Promise.all([
|
|
6288
6524
|
this.getMaxFee(params),
|
|
6289
|
-
getMintRecipientAccount(destination.chain.type, destination.
|
|
6290
|
-
(await destination.adapter.getAddress(destination.chain)), destination.chain.usdcAddress),
|
|
6525
|
+
getMintRecipientAccount(destination.chain.type, destinationAddressForMint, destination.chain.usdcAddress),
|
|
6291
6526
|
]);
|
|
6292
6527
|
const actionParams = {
|
|
6293
6528
|
fromChain: source.chain,
|
|
@@ -6317,14 +6552,7 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6317
6552
|
const finalParams = useCustomBurn
|
|
6318
6553
|
? { ...actionParams, feeRecipient: recipientAddress, protocolFee }
|
|
6319
6554
|
: actionParams;
|
|
6320
|
-
|
|
6321
|
-
if (resolvedContext) {
|
|
6322
|
-
return await source.adapter.prepareAction(actionType, finalParams, resolvedContext);
|
|
6323
|
-
}
|
|
6324
|
-
else {
|
|
6325
|
-
// Legacy fallback for adapters without capabilities
|
|
6326
|
-
return await source.adapter.prepareAction(actionType, finalParams);
|
|
6327
|
-
}
|
|
6555
|
+
return await source.adapter.prepareAction(actionType, finalParams, resolvedContext);
|
|
6328
6556
|
}
|
|
6329
6557
|
/**
|
|
6330
6558
|
* Waits for a transaction to be mined and confirmed on the blockchain.
|
|
@@ -6352,5 +6580,5 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6352
6580
|
}
|
|
6353
6581
|
}
|
|
6354
6582
|
|
|
6355
|
-
export { CCTPV2BridgingProvider };
|
|
6583
|
+
export { CCTPV2BridgingProvider, getMintRecipientAccount };
|
|
6356
6584
|
//# sourceMappingURL=index.mjs.map
|