@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
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
// Buffer polyfill setup - executes before any other code
|
|
22
22
|
// Ensures globalThis.Buffer is available for @solana/spl-token and other Solana libraries
|
|
23
|
-
|
|
23
|
+
const { Buffer } = require('buffer');
|
|
24
24
|
if (typeof globalThis !== 'undefined' && typeof globalThis.Buffer === 'undefined') {
|
|
25
25
|
globalThis.Buffer = Buffer;
|
|
26
26
|
}
|
|
@@ -30,10 +30,10 @@ if (typeof window !== 'undefined' && typeof window.Buffer === 'undefined') {
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
var zod = require('zod');
|
|
33
|
+
var units = require('@ethersproject/units');
|
|
33
34
|
var bytes = require('@ethersproject/bytes');
|
|
34
35
|
var address = require('@ethersproject/address');
|
|
35
36
|
var bs58 = require('bs58');
|
|
36
|
-
var units = require('@ethersproject/units');
|
|
37
37
|
|
|
38
38
|
function _interopDefault (e) { return e && e.__esModule ? e.default : e; }
|
|
39
39
|
|
|
@@ -54,6 +54,7 @@ var Blockchain;
|
|
|
54
54
|
Blockchain["Algorand_Testnet"] = "Algorand_Testnet";
|
|
55
55
|
Blockchain["Aptos"] = "Aptos";
|
|
56
56
|
Blockchain["Aptos_Testnet"] = "Aptos_Testnet";
|
|
57
|
+
Blockchain["Arc_Testnet"] = "Arc_Testnet";
|
|
57
58
|
Blockchain["Arbitrum"] = "Arbitrum";
|
|
58
59
|
Blockchain["Arbitrum_Sepolia"] = "Arbitrum_Sepolia";
|
|
59
60
|
Blockchain["Avalanche"] = "Avalanche";
|
|
@@ -274,6 +275,48 @@ const BRIDGE_CONTRACT_EVM_TESTNET = '0xC5567a5E3370d4DBfB0540025078e283e36A363d'
|
|
|
274
275
|
*/
|
|
275
276
|
const BRIDGE_CONTRACT_EVM_MAINNET = '0xB3FA262d0fB521cc93bE83d87b322b8A23DAf3F0';
|
|
276
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Arc Testnet chain definition
|
|
280
|
+
* @remarks
|
|
281
|
+
* This represents the test network for the Arc blockchain,
|
|
282
|
+
* Circle's EVM-compatible Layer-1 designed for stablecoin finance
|
|
283
|
+
* and asset tokenization. Arc uses USDC as the native gas token and
|
|
284
|
+
* features the Malachite Byzantine Fault Tolerant (BFT) consensus
|
|
285
|
+
* engine for sub-second finality.
|
|
286
|
+
*/
|
|
287
|
+
const ArcTestnet = defineChain({
|
|
288
|
+
type: 'evm',
|
|
289
|
+
chain: Blockchain.Arc_Testnet,
|
|
290
|
+
name: 'Arc Testnet',
|
|
291
|
+
title: 'ArcTestnet',
|
|
292
|
+
nativeCurrency: {
|
|
293
|
+
name: 'Arc',
|
|
294
|
+
symbol: 'Arc',
|
|
295
|
+
decimals: 18,
|
|
296
|
+
},
|
|
297
|
+
chainId: 5042002,
|
|
298
|
+
isTestnet: true,
|
|
299
|
+
explorerUrl: 'https://testnet.arcscan.app/tx/{hash}',
|
|
300
|
+
rpcEndpoints: ['https://rpc.testnet.arc.network/'],
|
|
301
|
+
eurcAddress: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
|
|
302
|
+
usdcAddress: '0x3600000000000000000000000000000000000000',
|
|
303
|
+
cctp: {
|
|
304
|
+
domain: 26,
|
|
305
|
+
contracts: {
|
|
306
|
+
v2: {
|
|
307
|
+
type: 'split',
|
|
308
|
+
tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
|
|
309
|
+
messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
|
|
310
|
+
confirmations: 1,
|
|
311
|
+
fastConfirmations: 1,
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
kitContracts: {
|
|
316
|
+
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
|
|
277
320
|
/**
|
|
278
321
|
* Arbitrum Mainnet chain definition
|
|
279
322
|
* @remarks
|
|
@@ -1565,26 +1608,26 @@ const Sonic = defineChain({
|
|
|
1565
1608
|
});
|
|
1566
1609
|
|
|
1567
1610
|
/**
|
|
1568
|
-
* Sonic
|
|
1611
|
+
* Sonic Testnet chain definition
|
|
1569
1612
|
* @remarks
|
|
1570
1613
|
* This represents the official test network for the Sonic blockchain.
|
|
1571
1614
|
*/
|
|
1572
1615
|
const SonicTestnet = defineChain({
|
|
1573
1616
|
type: 'evm',
|
|
1574
1617
|
chain: Blockchain.Sonic_Testnet,
|
|
1575
|
-
name: 'Sonic
|
|
1576
|
-
title: 'Sonic
|
|
1618
|
+
name: 'Sonic Testnet',
|
|
1619
|
+
title: 'Sonic Testnet',
|
|
1577
1620
|
nativeCurrency: {
|
|
1578
1621
|
name: 'Sonic',
|
|
1579
1622
|
symbol: 'S',
|
|
1580
1623
|
decimals: 18,
|
|
1581
1624
|
},
|
|
1582
|
-
chainId:
|
|
1625
|
+
chainId: 14601,
|
|
1583
1626
|
isTestnet: true,
|
|
1584
1627
|
explorerUrl: 'https://testnet.sonicscan.org/tx/{hash}',
|
|
1585
|
-
rpcEndpoints: ['https://rpc.
|
|
1628
|
+
rpcEndpoints: ['https://rpc.testnet.soniclabs.com'],
|
|
1586
1629
|
eurcAddress: null,
|
|
1587
|
-
usdcAddress: '
|
|
1630
|
+
usdcAddress: '0x0BA304580ee7c9a980CF72e55f5Ed2E9fd30Bc51',
|
|
1588
1631
|
cctp: {
|
|
1589
1632
|
domain: 13,
|
|
1590
1633
|
contracts: {
|
|
@@ -2101,6 +2144,7 @@ var Chains = {
|
|
|
2101
2144
|
AptosTestnet: AptosTestnet,
|
|
2102
2145
|
Arbitrum: Arbitrum,
|
|
2103
2146
|
ArbitrumSepolia: ArbitrumSepolia,
|
|
2147
|
+
ArcTestnet: ArcTestnet,
|
|
2104
2148
|
Avalanche: Avalanche,
|
|
2105
2149
|
AvalancheFuji: AvalancheFuji,
|
|
2106
2150
|
Base: Base,
|
|
@@ -2575,69 +2619,133 @@ class BridgingProvider {
|
|
|
2575
2619
|
}
|
|
2576
2620
|
|
|
2577
2621
|
/**
|
|
2578
|
-
*
|
|
2579
|
-
*
|
|
2580
|
-
* The user agent string is formatted as:
|
|
2581
|
-
* name/version (platform)
|
|
2582
|
-
*
|
|
2583
|
-
* - name: The SDK or kit name, set at runtime.
|
|
2584
|
-
* - version: The SDK version, set at runtime.
|
|
2585
|
-
* - platform: The runtime environment, e.g., "node/nodeVersion" or "browser/browserName".
|
|
2622
|
+
* Detect the runtime environment and return a shortened identifier.
|
|
2586
2623
|
*
|
|
2587
|
-
* @
|
|
2588
|
-
* The kit should call setKitIdentifier(name, version) once at startup.
|
|
2589
|
-
* A custom user agent will be automatically added to all HTTP requests made by the SDK.
|
|
2590
|
-
* This user agent includes the kit name, version, and platform information to help with
|
|
2591
|
-
* debugging and analytics.
|
|
2592
|
-
*
|
|
2593
|
-
* @example
|
|
2594
|
-
* ```typescript
|
|
2595
|
-
* setKitIdentifier('bridge-kit', '1.2.3')
|
|
2596
|
-
* getUserAgent() // "bridge-kit/1.2.3 (node/18.16.0)"
|
|
2597
|
-
* ```
|
|
2598
|
-
*
|
|
2599
|
-
* @returns The Stablecoin Kits SDK user agent string.
|
|
2624
|
+
* @returns Runtime string, e.g., "node/18", "browser/Chrome", or "unknown"
|
|
2600
2625
|
*/
|
|
2601
|
-
|
|
2602
|
-
|
|
2626
|
+
const getRuntime = () => {
|
|
2627
|
+
// Node.js environment
|
|
2603
2628
|
if (typeof process !== 'undefined' &&
|
|
2604
2629
|
typeof process.versions === 'object' &&
|
|
2605
2630
|
typeof process.versions.node === 'string') {
|
|
2606
|
-
|
|
2631
|
+
// Shorten to major version only
|
|
2632
|
+
const majorVersion = process.versions.node.split('.')[0] ?? 'unknown';
|
|
2633
|
+
return `node/${majorVersion}`;
|
|
2607
2634
|
}
|
|
2608
|
-
//
|
|
2635
|
+
// Browser environment
|
|
2609
2636
|
if (typeof window !== 'undefined' && typeof navigator !== 'undefined') {
|
|
2610
2637
|
const userAgent = navigator.userAgent;
|
|
2611
2638
|
const browserMatchers = {
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
'browser/Opera': (ua) => ua.includes('Opera') || ua.includes('OPR'),
|
|
2618
|
-
'browser/Brave': (ua) => ua.includes('Brave'),
|
|
2639
|
+
Edge: (ua) => ua.includes('Edg'),
|
|
2640
|
+
Chrome: (ua) => ua.includes('Chrome') && !ua.includes('Edg'),
|
|
2641
|
+
Firefox: (ua) => ua.includes('Firefox'),
|
|
2642
|
+
Safari: (ua) => ua.includes('Safari') && !ua.includes('Chrome'),
|
|
2643
|
+
Opera: (ua) => ua.includes('Opera') || ua.includes('OPR'),
|
|
2619
2644
|
};
|
|
2620
2645
|
for (const [browserName, matcher] of Object.entries(browserMatchers)) {
|
|
2621
2646
|
if (matcher(userAgent))
|
|
2622
|
-
return browserName
|
|
2647
|
+
return `browser/${browserName}`;
|
|
2623
2648
|
}
|
|
2624
2649
|
return 'browser/unknown';
|
|
2625
2650
|
}
|
|
2626
2651
|
return 'unknown';
|
|
2627
|
-
}
|
|
2652
|
+
};
|
|
2628
2653
|
/**
|
|
2629
|
-
*
|
|
2654
|
+
* Shorten package names by removing common prefixes.
|
|
2630
2655
|
*
|
|
2631
|
-
* @
|
|
2656
|
+
* @param fullName - Full package name, e.g., "\@circle-fin/bridge-kit"
|
|
2657
|
+
* @returns Shortened name, e.g., "bridge-kit"
|
|
2632
2658
|
*/
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2659
|
+
const shortenPackageName = (fullName) => {
|
|
2660
|
+
return fullName.replace('@circle-fin/', '');
|
|
2661
|
+
};
|
|
2662
|
+
/**
|
|
2663
|
+
* Format a component (name/version) with length optimizations.
|
|
2664
|
+
*
|
|
2665
|
+
* @param nameWithVersion - Component identifier, e.g., "\@circle-fin/bridge-kit/1.2.3"
|
|
2666
|
+
* @returns Formatted component, e.g., "bridge-kit/1.2"
|
|
2667
|
+
*/
|
|
2668
|
+
const formatComponent = (nameWithVersion) => {
|
|
2669
|
+
// Find the last / to split name and version
|
|
2670
|
+
// This handles scoped packages like @circle-fin/bridge-kit/1.0.0 correctly
|
|
2671
|
+
const lastSlashIndex = nameWithVersion.lastIndexOf('/');
|
|
2672
|
+
if (lastSlashIndex === -1) {
|
|
2673
|
+
// No version, just a name
|
|
2674
|
+
return shortenPackageName(nameWithVersion);
|
|
2675
|
+
}
|
|
2676
|
+
const name = nameWithVersion.substring(0, lastSlashIndex);
|
|
2677
|
+
const version = nameWithVersion.substring(lastSlashIndex + 1);
|
|
2678
|
+
if (name === '')
|
|
2679
|
+
return '';
|
|
2680
|
+
if (version === '')
|
|
2681
|
+
return shortenPackageName(name);
|
|
2682
|
+
return `${shortenPackageName(name)}/${version}`;
|
|
2683
|
+
};
|
|
2684
|
+
/**
|
|
2685
|
+
* Create a new request context from global state.
|
|
2686
|
+
*
|
|
2687
|
+
* Reads the current external prefix and kit identifier from globalThis.
|
|
2688
|
+
* This is used internally by HTTP utilities.
|
|
2689
|
+
*
|
|
2690
|
+
* @returns A new request context
|
|
2691
|
+
* @internal
|
|
2692
|
+
*/
|
|
2693
|
+
const createRequestContext = () => {
|
|
2694
|
+
const context = {};
|
|
2695
|
+
if (typeof globalThis !== 'undefined') {
|
|
2696
|
+
if (globalThis.__STABLECOIN_KITS_EXTERNAL_PREFIX__ !== undefined) {
|
|
2697
|
+
context.externalPrefix = globalThis.__STABLECOIN_KITS_EXTERNAL_PREFIX__;
|
|
2698
|
+
}
|
|
2699
|
+
if (globalThis.__STABLECOIN_KITS_CURRENT_KIT__ !== undefined) {
|
|
2700
|
+
context.kit = globalThis.__STABLECOIN_KITS_CURRENT_KIT__;
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
return context;
|
|
2704
|
+
};
|
|
2705
|
+
/**
|
|
2706
|
+
* Build a user agent string from a request context.
|
|
2707
|
+
*
|
|
2708
|
+
* Formats the context into a user agent string with automatic optimizations
|
|
2709
|
+
* (shortened package names and truncated versions). The current implementation
|
|
2710
|
+
* tracks a single kit globally.
|
|
2711
|
+
*
|
|
2712
|
+
* Format: `[external-prefix] kit (runtime)`
|
|
2713
|
+
*
|
|
2714
|
+
* @param context - Request context or undefined to use the global context
|
|
2715
|
+
* @returns Formatted user agent string
|
|
2716
|
+
*
|
|
2717
|
+
* @example
|
|
2718
|
+
* ```typescript
|
|
2719
|
+
* // With application prefix and kit
|
|
2720
|
+
* const ua = getUserAgent(context)
|
|
2721
|
+
* // → "my-app/1.0 bridge-kit/1.2 (node/18)"
|
|
2722
|
+
*
|
|
2723
|
+
* // Kit only (no application prefix set)
|
|
2724
|
+
* const ua = getUserAgent()
|
|
2725
|
+
* // → "bridge-kit/1.2 (node/18)"
|
|
2726
|
+
* ```
|
|
2727
|
+
*/
|
|
2728
|
+
const getUserAgent = (context) => {
|
|
2729
|
+
const ctx = createRequestContext();
|
|
2730
|
+
const runtime = getRuntime();
|
|
2731
|
+
// Build user agent string
|
|
2732
|
+
const parts = [];
|
|
2733
|
+
// Add external prefix if set
|
|
2734
|
+
if (ctx.externalPrefix !== undefined) {
|
|
2735
|
+
const formatted = formatComponent(ctx.externalPrefix);
|
|
2736
|
+
if (formatted)
|
|
2737
|
+
parts.push(formatted);
|
|
2738
|
+
}
|
|
2739
|
+
// Add kit if set
|
|
2740
|
+
if (ctx.kit !== undefined) {
|
|
2741
|
+
const formatted = formatComponent(ctx.kit);
|
|
2742
|
+
if (formatted)
|
|
2743
|
+
parts.push(formatted);
|
|
2744
|
+
}
|
|
2745
|
+
// Add runtime
|
|
2746
|
+
parts.push(`(${runtime})`);
|
|
2747
|
+
return parts.join(' ');
|
|
2748
|
+
};
|
|
2641
2749
|
|
|
2642
2750
|
/**
|
|
2643
2751
|
* Default configuration values for the API polling utility.
|
|
@@ -2889,6 +2997,84 @@ const pollApiGet = async (url, isValidType, config) => {
|
|
|
2889
2997
|
return pollApiWithValidation(url, 'GET', isValidType, config);
|
|
2890
2998
|
};
|
|
2891
2999
|
|
|
3000
|
+
/**
|
|
3001
|
+
* Convert a value from its smallest unit representation to a human-readable decimal string.
|
|
3002
|
+
*
|
|
3003
|
+
* This function normalizes token values from their blockchain representation (where
|
|
3004
|
+
* everything is stored as integers in the smallest denomination) to human-readable
|
|
3005
|
+
* decimal format. Uses the battle-tested implementation from @ethersproject/units.
|
|
3006
|
+
*
|
|
3007
|
+
* @param value - The value in smallest units (e.g., "1000000" for 1 USDC with 6 decimals)
|
|
3008
|
+
* @param decimals - The number of decimal places for the unit conversion
|
|
3009
|
+
* @returns A human-readable decimal string (e.g., "1.0")
|
|
3010
|
+
* @throws Error if the value is not a valid numeric string
|
|
3011
|
+
*
|
|
3012
|
+
* @example
|
|
3013
|
+
* ```typescript
|
|
3014
|
+
* import { formatUnits } from '@core/utils'
|
|
3015
|
+
*
|
|
3016
|
+
* // Format USDC (6 decimals)
|
|
3017
|
+
* const usdcFormatted = formatUnits('1000000', 6)
|
|
3018
|
+
* console.log(usdcFormatted) // "1.0"
|
|
3019
|
+
*
|
|
3020
|
+
* // Format ETH (18 decimals)
|
|
3021
|
+
* const ethFormatted = formatUnits('1000000000000000000', 18)
|
|
3022
|
+
* console.log(ethFormatted) // "1.0"
|
|
3023
|
+
*
|
|
3024
|
+
* // Format with fractional part
|
|
3025
|
+
* const fractionalFormatted = formatUnits('1500000', 6)
|
|
3026
|
+
* console.log(fractionalFormatted) // "1.5"
|
|
3027
|
+
* ```
|
|
3028
|
+
*/
|
|
3029
|
+
const formatUnits = (value, decimals) => {
|
|
3030
|
+
return units.formatUnits(value, decimals);
|
|
3031
|
+
};
|
|
3032
|
+
|
|
3033
|
+
/**
|
|
3034
|
+
* Format a token amount into a human-readable decimal string.
|
|
3035
|
+
*
|
|
3036
|
+
* Accepts a smallest-unit string and either assumes USDC's 6 decimals or derives the
|
|
3037
|
+
* native decimals from the provided chain definition. Delegates to {@link formatUnits}
|
|
3038
|
+
* to preserve consistent rounding and formatting behaviour across the SDK.
|
|
3039
|
+
*
|
|
3040
|
+
* @remarks
|
|
3041
|
+
* When `token` is `'native'`, supply a chain identifier that {@link resolveChainIdentifier}
|
|
3042
|
+
* can resolve so the native currency decimals can be determined.
|
|
3043
|
+
*
|
|
3044
|
+
* @param params - The formatting input including the raw value and token selector.
|
|
3045
|
+
* @returns The decimal string representation of the amount.
|
|
3046
|
+
* @throws Error if the value cannot be parsed or if the chain identifier is unknown.
|
|
3047
|
+
*
|
|
3048
|
+
* @example
|
|
3049
|
+
* ```typescript
|
|
3050
|
+
* import { formatAmount } from '@core/utils'
|
|
3051
|
+
* import { Ethereum } from '@core/chains'
|
|
3052
|
+
*
|
|
3053
|
+
* const usdcAmount = formatAmount({ value: '1000000', token: 'USDC' })
|
|
3054
|
+
* console.log(usdcAmount) // "1"
|
|
3055
|
+
*
|
|
3056
|
+
* const ethAmount = formatAmount({
|
|
3057
|
+
* value: '3141592000000000000',
|
|
3058
|
+
* token: 'native',
|
|
3059
|
+
* chain: Ethereum,
|
|
3060
|
+
* })
|
|
3061
|
+
* console.log(ethAmount) // "3.141592"
|
|
3062
|
+
* ```
|
|
3063
|
+
*/
|
|
3064
|
+
const formatAmount = (params) => {
|
|
3065
|
+
const { value, token } = params;
|
|
3066
|
+
switch (token) {
|
|
3067
|
+
case 'USDC':
|
|
3068
|
+
return formatUnits(value, 6);
|
|
3069
|
+
case 'native':
|
|
3070
|
+
return formatUnits(value, resolveChainIdentifier(params.chain).nativeCurrency.decimals);
|
|
3071
|
+
default:
|
|
3072
|
+
// This will cause a compile-time error if a new token type is added to
|
|
3073
|
+
// `FormatAmountParams` but not handled in this switch statement, ensuring exhaustiveness.
|
|
3074
|
+
throw new Error(`formatAmount: Unhandled token type: ${token}`);
|
|
3075
|
+
}
|
|
3076
|
+
};
|
|
3077
|
+
|
|
2892
3078
|
/**
|
|
2893
3079
|
* Custom error class for insufficient funds errors.
|
|
2894
3080
|
* Provides structured error information while hiding implementation details.
|
|
@@ -2896,7 +3082,7 @@ const pollApiGet = async (url, isValidType, config) => {
|
|
|
2896
3082
|
class InsufficientFundsError extends Error {
|
|
2897
3083
|
balance;
|
|
2898
3084
|
amount;
|
|
2899
|
-
|
|
3085
|
+
token;
|
|
2900
3086
|
tokenAddress;
|
|
2901
3087
|
chain;
|
|
2902
3088
|
/**
|
|
@@ -2904,16 +3090,27 @@ class InsufficientFundsError extends Error {
|
|
|
2904
3090
|
*
|
|
2905
3091
|
* @param balance - The current token balance in the wallet (smallest unit).
|
|
2906
3092
|
* @param amount - The required transaction amount (smallest unit).
|
|
2907
|
-
* @param
|
|
3093
|
+
* @param token - The token ('USDC' or 'native').
|
|
2908
3094
|
* @param tokenAddress - The contract address of the token.
|
|
2909
3095
|
* @param chain - The blockchain network name where the transaction failed.
|
|
2910
3096
|
*/
|
|
2911
|
-
constructor(balance, amount,
|
|
2912
|
-
const
|
|
3097
|
+
constructor(balance, amount, token, tokenAddress, chain) {
|
|
3098
|
+
const formattedBalance = formatAmount({
|
|
3099
|
+
value: balance,
|
|
3100
|
+
token,
|
|
3101
|
+
chain,
|
|
3102
|
+
});
|
|
3103
|
+
const formattedAmount = formatAmount({
|
|
3104
|
+
value: amount,
|
|
3105
|
+
token,
|
|
3106
|
+
chain,
|
|
3107
|
+
});
|
|
3108
|
+
const chainName = typeof chain === 'object' && 'name' in chain ? chain.name : chain;
|
|
3109
|
+
const message = `Insufficient funds for ${token} (${tokenAddress}) transaction on ${chainName}. Balance: ${formattedBalance} ${token}, is less than transaction amount: ${formattedAmount} ${token}.`;
|
|
2913
3110
|
super(message);
|
|
2914
3111
|
this.balance = balance;
|
|
2915
3112
|
this.amount = amount;
|
|
2916
|
-
this.
|
|
3113
|
+
this.token = token;
|
|
2917
3114
|
this.tokenAddress = tokenAddress;
|
|
2918
3115
|
this.chain = chain;
|
|
2919
3116
|
this.name = 'InsufficientFundsError';
|
|
@@ -3292,39 +3489,6 @@ const convertAddress = (address, targetFormat) => {
|
|
|
3292
3489
|
throw new Error(`Unsupported address format: ${address}`);
|
|
3293
3490
|
};
|
|
3294
3491
|
|
|
3295
|
-
/**
|
|
3296
|
-
* Convert a value from its smallest unit representation to a human-readable decimal string.
|
|
3297
|
-
*
|
|
3298
|
-
* This function normalizes token values from their blockchain representation (where
|
|
3299
|
-
* everything is stored as integers in the smallest denomination) to human-readable
|
|
3300
|
-
* decimal format. Uses the battle-tested implementation from @ethersproject/units.
|
|
3301
|
-
*
|
|
3302
|
-
* @param value - The value in smallest units (e.g., "1000000" for 1 USDC with 6 decimals)
|
|
3303
|
-
* @param decimals - The number of decimal places for the unit conversion
|
|
3304
|
-
* @returns A human-readable decimal string (e.g., "1.0")
|
|
3305
|
-
* @throws Error if the value is not a valid numeric string
|
|
3306
|
-
*
|
|
3307
|
-
* @example
|
|
3308
|
-
* ```typescript
|
|
3309
|
-
* import { formatUnits } from '@core/utils'
|
|
3310
|
-
*
|
|
3311
|
-
* // Format USDC (6 decimals)
|
|
3312
|
-
* const usdcFormatted = formatUnits('1000000', 6)
|
|
3313
|
-
* console.log(usdcFormatted) // "1.0"
|
|
3314
|
-
*
|
|
3315
|
-
* // Format ETH (18 decimals)
|
|
3316
|
-
* const ethFormatted = formatUnits('1000000000000000000', 18)
|
|
3317
|
-
* console.log(ethFormatted) // "1.0"
|
|
3318
|
-
*
|
|
3319
|
-
* // Format with fractional part
|
|
3320
|
-
* const fractionalFormatted = formatUnits('1500000', 6)
|
|
3321
|
-
* console.log(fractionalFormatted) // "1.5"
|
|
3322
|
-
* ```
|
|
3323
|
-
*/
|
|
3324
|
-
const formatUnits = (value, decimals) => {
|
|
3325
|
-
return units.formatUnits(value, decimals);
|
|
3326
|
-
};
|
|
3327
|
-
|
|
3328
3492
|
/**
|
|
3329
3493
|
* Build a complete explorer URL from a chain definition and transaction hash.
|
|
3330
3494
|
*
|
|
@@ -3586,9 +3750,9 @@ const customFeeSchema = zod.z
|
|
|
3586
3750
|
* token: 'USDC',
|
|
3587
3751
|
* config: {
|
|
3588
3752
|
* transferSpeed: 'FAST',
|
|
3589
|
-
* maxFee: '
|
|
3753
|
+
* maxFee: '1.5', // Decimal format
|
|
3590
3754
|
* customFee: {
|
|
3591
|
-
* value: '
|
|
3755
|
+
* value: '0.5', // Decimal format
|
|
3592
3756
|
* recipientAddress: '0x1234567890123456789012345678901234567890'
|
|
3593
3757
|
* }
|
|
3594
3758
|
* }
|
|
@@ -3615,7 +3779,12 @@ const bridgeParamsSchema = zod.z.object({
|
|
|
3615
3779
|
transferSpeed: zod.z.nativeEnum(TransferSpeed).optional(),
|
|
3616
3780
|
maxFee: zod.z
|
|
3617
3781
|
.string()
|
|
3618
|
-
.
|
|
3782
|
+
.pipe(createDecimalStringValidator({
|
|
3783
|
+
allowZero: true,
|
|
3784
|
+
regexMessage: 'maxFee must be a numeric string with optional decimal places (e.g., "1", "0.5", "1.5")',
|
|
3785
|
+
attributeName: 'maxFee',
|
|
3786
|
+
maxDecimals: 6,
|
|
3787
|
+
})(zod.z.string()))
|
|
3619
3788
|
.optional(),
|
|
3620
3789
|
customFee: customFeeSchema.optional(),
|
|
3621
3790
|
}),
|
|
@@ -3745,9 +3914,11 @@ async function fetchUsdcFastBurnFee(sourceDomain, destinationDomain, isTestnet)
|
|
|
3745
3914
|
* amount and invalid atttestation messages.
|
|
3746
3915
|
*
|
|
3747
3916
|
* These values were obtained by averaging gas for the function call from the last 3 months.
|
|
3917
|
+
* see: https://dune.com/queries/6022210/9697710/
|
|
3748
3918
|
*/
|
|
3749
|
-
const DEPOSIT_FOR_BURN_GAS_ESTIMATE_EVM =
|
|
3750
|
-
const
|
|
3919
|
+
const DEPOSIT_FOR_BURN_GAS_ESTIMATE_EVM = 169914n; // (99p: 111_521n + max: 226_506n) / 2 = 169_914n
|
|
3920
|
+
const CUSTOM_BURN_GAS_ESTIMATE_EVM = 201525n; // p99 and max are same here: 201_525n
|
|
3921
|
+
const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237401n; // (99p: 163_963n + max: 310_839n) / 2 = 237_401n
|
|
3751
3922
|
/**
|
|
3752
3923
|
* The minimum finality threshold for CCTPv2 transfers.
|
|
3753
3924
|
*
|
|
@@ -3772,8 +3943,8 @@ const CCTPv2MinFinalityThreshold = {
|
|
|
3772
3943
|
*/
|
|
3773
3944
|
const DEFAULT_CONFIG = {
|
|
3774
3945
|
timeout: 2_000, // 2 seconds
|
|
3775
|
-
maxRetries: 30 * 20, // 2 seconds
|
|
3776
|
-
retryDelay:
|
|
3946
|
+
maxRetries: 30 * 20, // 30 * 20 * 2 seconds (from the retry delay) = 20 minutes (to account for slow transfers maximum time based on confirmations)
|
|
3947
|
+
retryDelay: 2_000, // 2 seconds
|
|
3777
3948
|
headers: {
|
|
3778
3949
|
'Content-Type': 'application/json',
|
|
3779
3950
|
},
|
|
@@ -4205,6 +4376,10 @@ class KitError extends Error {
|
|
|
4205
4376
|
}
|
|
4206
4377
|
}
|
|
4207
4378
|
|
|
4379
|
+
/**
|
|
4380
|
+
* Minimum error code for INPUT type errors.
|
|
4381
|
+
* INPUT errors represent validation failures and invalid parameters.
|
|
4382
|
+
*/
|
|
4208
4383
|
/**
|
|
4209
4384
|
* Standardized error definitions for INPUT type errors.
|
|
4210
4385
|
*
|
|
@@ -4558,7 +4733,9 @@ async function assertCCTPv2AttestationParams(attestation, params) {
|
|
|
4558
4733
|
const errors = [];
|
|
4559
4734
|
const message = attestation.decodedMessage;
|
|
4560
4735
|
const messageBody = message.decodedMessageBody;
|
|
4561
|
-
|
|
4736
|
+
// Use recipientAddress if provided, otherwise use destination.address
|
|
4737
|
+
const destinationAddressForMint = params.destination.recipientAddress ?? params.destination.address;
|
|
4738
|
+
const mintRecipient = await getMintRecipientAccount(params.destination.chain.type, destinationAddressForMint, params.destination.chain.usdcAddress);
|
|
4562
4739
|
let sender;
|
|
4563
4740
|
if (hasCustomContractSupport(params.source.chain, 'bridge')) {
|
|
4564
4741
|
if (params.source.chain.type === 'solana') {
|
|
@@ -4690,8 +4867,6 @@ async function bridgeApproval({ params, provider, }) {
|
|
|
4690
4867
|
adapter: params.source.adapter,
|
|
4691
4868
|
chain: params.source.chain,
|
|
4692
4869
|
request: await provider.approve(params.source, approvalAmount),
|
|
4693
|
-
// We need to wait for the approval to be confirmed on chain before the transfer can be executed
|
|
4694
|
-
confirmations: 2,
|
|
4695
4870
|
});
|
|
4696
4871
|
}
|
|
4697
4872
|
|
|
@@ -4705,8 +4880,8 @@ async function bridgeApproval({ params, provider, }) {
|
|
|
4705
4880
|
* @param params - The bridge parameters containing source, destination, amount and optional config
|
|
4706
4881
|
* @param sourceChain - The source chain definition where the burn will occur
|
|
4707
4882
|
* @returns Promise resolving to the bridge step with transaction details
|
|
4708
|
-
* @throws {ValidationError} If the parameters are invalid
|
|
4709
|
-
* @throws {BridgeError} If the burn transaction fails
|
|
4883
|
+
* @throws \{ValidationError\} If the parameters are invalid
|
|
4884
|
+
* @throws \{BridgeError\} If the burn transaction fails
|
|
4710
4885
|
*
|
|
4711
4886
|
* @example
|
|
4712
4887
|
* ```typescript
|
|
@@ -4715,16 +4890,11 @@ async function bridgeApproval({ params, provider, }) {
|
|
|
4715
4890
|
* ```
|
|
4716
4891
|
*/
|
|
4717
4892
|
async function bridgeBurn({ params, provider, }) {
|
|
4718
|
-
const transferSpeed = params.config?.transferSpeed ?? TransferSpeed.FAST;
|
|
4719
4893
|
return await executePreparedChainRequest({
|
|
4720
4894
|
name: 'burn',
|
|
4721
4895
|
adapter: params.source.adapter,
|
|
4722
4896
|
chain: params.source.chain,
|
|
4723
4897
|
request: await provider.burn(params),
|
|
4724
|
-
confirmations: transferSpeed === TransferSpeed.FAST
|
|
4725
|
-
? params.source.chain.cctp.contracts.v2.fastConfirmations
|
|
4726
|
-
: params.source.chain.cctp.contracts.v2.confirmations,
|
|
4727
|
-
timeout: transferSpeed === TransferSpeed.FAST ? undefined : 1_200_000, // 20 minutes (max timeout for cctpv2 slow transfers)
|
|
4728
4898
|
});
|
|
4729
4899
|
}
|
|
4730
4900
|
|
|
@@ -4736,16 +4906,18 @@ async function bridgeBurn({ params, provider, }) {
|
|
|
4736
4906
|
* and authorizes the minting of equivalent tokens.
|
|
4737
4907
|
*
|
|
4738
4908
|
* @param params - The bridge parameters containing source, destination, amount and optional config
|
|
4739
|
-
* @param
|
|
4740
|
-
* @
|
|
4741
|
-
* @
|
|
4742
|
-
* @throws {
|
|
4743
|
-
* @throws {BridgeError} If the attestation fetch fails
|
|
4909
|
+
* @param txHash - The hash of the burn transaction
|
|
4910
|
+
* @returns Promise resolving to the bridge step with attestation data
|
|
4911
|
+
* @throws \{ValidationError\} If the parameters are invalid
|
|
4912
|
+
* @throws \{BridgeError\} If the attestation fetch fails
|
|
4744
4913
|
*
|
|
4745
4914
|
* @example
|
|
4746
4915
|
* ```typescript
|
|
4747
|
-
* const attestationStep = await
|
|
4748
|
-
*
|
|
4916
|
+
* const attestationStep = await bridgeFetchAttestation(
|
|
4917
|
+
* { params, provider },
|
|
4918
|
+
* burnTxHash
|
|
4919
|
+
* )
|
|
4920
|
+
* console.log('Attestation data:', attestationStep.data)
|
|
4749
4921
|
* ```
|
|
4750
4922
|
*/
|
|
4751
4923
|
async function bridgeFetchAttestation({ params, provider, }, txHash) {
|
|
@@ -4754,7 +4926,17 @@ async function bridgeFetchAttestation({ params, provider, }, txHash) {
|
|
|
4754
4926
|
state: 'pending',
|
|
4755
4927
|
};
|
|
4756
4928
|
try {
|
|
4757
|
-
|
|
4929
|
+
/**
|
|
4930
|
+
* Give slow transfers a longer retry delay of 30 seconds to account for the longer time it takes to get the
|
|
4931
|
+
* attestation so that we do not hit the rate limit of the IRIS API or the retry limit of the API polling utility.
|
|
4932
|
+
* We only do this for slow transfers on source chains that have greater than 20 confirmations (5 minutes of blocktime
|
|
4933
|
+
* on the slowest blocktime chains) so that we do not slow down the attestation polling for lower confirmation chains.
|
|
4934
|
+
*/
|
|
4935
|
+
const apiPollingConfig = params.config?.transferSpeed === TransferSpeed.SLOW &&
|
|
4936
|
+
params.source.chain.cctp.contracts.v2.confirmations > 20
|
|
4937
|
+
? { retryDelay: 30_000 }
|
|
4938
|
+
: undefined;
|
|
4939
|
+
const attestation = await provider.fetchAttestation(params.source, txHash, apiPollingConfig);
|
|
4758
4940
|
step = {
|
|
4759
4941
|
...step,
|
|
4760
4942
|
state: 'success',
|
|
@@ -4762,10 +4944,18 @@ async function bridgeFetchAttestation({ params, provider, }, txHash) {
|
|
|
4762
4944
|
};
|
|
4763
4945
|
}
|
|
4764
4946
|
catch (err) {
|
|
4947
|
+
let errorMessage = 'Unknown attestation error';
|
|
4948
|
+
if (err instanceof Error) {
|
|
4949
|
+
errorMessage = err.message;
|
|
4950
|
+
}
|
|
4951
|
+
else if (typeof err === 'string') {
|
|
4952
|
+
errorMessage = err;
|
|
4953
|
+
}
|
|
4765
4954
|
step = {
|
|
4766
4955
|
...step,
|
|
4767
4956
|
state: 'error',
|
|
4768
4957
|
error: err,
|
|
4958
|
+
errorMessage,
|
|
4769
4959
|
data: undefined,
|
|
4770
4960
|
};
|
|
4771
4961
|
}
|
|
@@ -4801,10 +4991,37 @@ async function bridgeMint({ params, provider, }, attestation) {
|
|
|
4801
4991
|
adapter: params.destination.adapter,
|
|
4802
4992
|
chain: params.destination.chain,
|
|
4803
4993
|
request: await provider.mint(params.source, params.destination, attestation),
|
|
4804
|
-
// We will wait for the mint to be confirmed on chain before we will return is as success
|
|
4805
|
-
confirmations: 2,
|
|
4806
4994
|
});
|
|
4807
4995
|
}
|
|
4996
|
+
const mockAttestationMessage = {
|
|
4997
|
+
attestation: '0x8bd9b9e63eb05128eb2896b63e3e1df39bfd6bbfb893b69dc53c39252aeb85df1ded70263b07da17abf88e6e1b77f16ebcdb4eb1c6b4ea4c625215e5cb9dddb81bffe0b9d75e2094f05e48f18f70d0b254999bde90bab26f5c0da29b2d7e00feca1cfed119cba0d2a0e887648a40e803e0ca34aa8fd94ce068515eeaef72b520aa1b',
|
|
4998
|
+
message: '0x000000010000000000000006f446cb82eb486fef485c14c301eaab73aa35f87e3feda3547acf0f33cd4b40f30000000000000000000000008fe6b999dc680ccfdd5bf7eb0974218be2542daa0000000000000000000000008fe6b999dc680ccfdd5bf7eb0974218be2542daa0000000000000000000000000000000000000000000000000000000000000000000003e8000003e8000000010000000000000000000000001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000023f9a5bea7b92a0638520607407bc7f0310aeed400000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000c5567a5e3370d4dbfb0540025078e283e36a363d000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000001f6b158',
|
|
4999
|
+
eventNonce: '0xf446cb82eb486fef485c14c301eaab73aa35f87e3feda3547acf0f33cd4b40f3',
|
|
5000
|
+
cctpVersion: 2,
|
|
5001
|
+
status: 'complete',
|
|
5002
|
+
decodedMessage: {
|
|
5003
|
+
sourceDomain: '0',
|
|
5004
|
+
destinationDomain: '6',
|
|
5005
|
+
nonce: '0xf446cb82eb486fef485c14c301eaab73aa35f87e3feda3547acf0f33cd4b40f3',
|
|
5006
|
+
sender: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
|
|
5007
|
+
recipient: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
|
|
5008
|
+
destinationCaller: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
5009
|
+
minFinalityThreshold: '1000',
|
|
5010
|
+
finalityThresholdExecuted: '1000',
|
|
5011
|
+
messageBody: '0x000000010000000000000000000000001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000023f9a5bea7b92a0638520607407bc7f0310aeed400000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000c5567a5e3370d4dbfb0540025078e283e36a363d000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000001f6b158',
|
|
5012
|
+
decodedMessageBody: {
|
|
5013
|
+
burnToken: '0x1c7d4b196cb0c7b01d743fbc6116a902379c7238',
|
|
5014
|
+
mintRecipient: '0x23f9a5bea7b92a0638520607407bc7f0310aeed4',
|
|
5015
|
+
amount: '100000',
|
|
5016
|
+
messageSender: '0xc5567a5e3370d4dbfb0540025078e283e36a363d',
|
|
5017
|
+
maxFee: '11',
|
|
5018
|
+
feeExecuted: '10',
|
|
5019
|
+
expirationBlock: '32944472',
|
|
5020
|
+
hookData: null,
|
|
5021
|
+
},
|
|
5022
|
+
},
|
|
5023
|
+
delayReason: null,
|
|
5024
|
+
};
|
|
4808
5025
|
|
|
4809
5026
|
/**
|
|
4810
5027
|
* Ordered sequence of CCTP v2 bridge step executors.
|
|
@@ -4888,6 +5105,56 @@ function handleStepError(stepName, error, result) {
|
|
|
4888
5105
|
});
|
|
4889
5106
|
}
|
|
4890
5107
|
|
|
5108
|
+
/**
|
|
5109
|
+
* Dispatch a bridge step event through the provider's action dispatcher.
|
|
5110
|
+
*
|
|
5111
|
+
* Constructs the appropriate action payload and dispatches it to any registered
|
|
5112
|
+
* event listeners. Handles type-safe dispatching for different step types.
|
|
5113
|
+
*
|
|
5114
|
+
* @param name - The step name (approve, burn, fetchAttestation, or mint).
|
|
5115
|
+
* @param step - The completed bridge step containing transaction details and explorerUrl.
|
|
5116
|
+
* @param provider - The CCTP v2 provider with action dispatcher.
|
|
5117
|
+
*
|
|
5118
|
+
* @example
|
|
5119
|
+
* ```typescript
|
|
5120
|
+
* const step: BridgeStep = {
|
|
5121
|
+
* name: 'burn',
|
|
5122
|
+
* state: 'success',
|
|
5123
|
+
* txHash: '0xabc...',
|
|
5124
|
+
* explorerUrl: 'https://sepolia.etherscan.io/tx/0xabc...',
|
|
5125
|
+
* data: { ... }
|
|
5126
|
+
* }
|
|
5127
|
+
* dispatchStepEvent('burn', step, provider)
|
|
5128
|
+
* ```
|
|
5129
|
+
*/
|
|
5130
|
+
function dispatchStepEvent(name, step, provider) {
|
|
5131
|
+
if (!provider.actionDispatcher) {
|
|
5132
|
+
return;
|
|
5133
|
+
}
|
|
5134
|
+
const actionValues = {
|
|
5135
|
+
protocol: 'cctp',
|
|
5136
|
+
version: 'v2',
|
|
5137
|
+
values: step,
|
|
5138
|
+
};
|
|
5139
|
+
switch (name) {
|
|
5140
|
+
case 'approve':
|
|
5141
|
+
case 'burn':
|
|
5142
|
+
case 'mint':
|
|
5143
|
+
provider.actionDispatcher.dispatch(name, {
|
|
5144
|
+
...actionValues,
|
|
5145
|
+
method: name,
|
|
5146
|
+
});
|
|
5147
|
+
break;
|
|
5148
|
+
case 'fetchAttestation':
|
|
5149
|
+
provider.actionDispatcher.dispatch(name, {
|
|
5150
|
+
...actionValues,
|
|
5151
|
+
method: name,
|
|
5152
|
+
values: step,
|
|
5153
|
+
});
|
|
5154
|
+
break;
|
|
5155
|
+
}
|
|
5156
|
+
}
|
|
5157
|
+
|
|
4891
5158
|
/**
|
|
4892
5159
|
* Execute a cross-chain USDC bridge using the CCTP v2 protocol.
|
|
4893
5160
|
*
|
|
@@ -4944,34 +5211,19 @@ async function bridge(params, provider) {
|
|
|
4944
5211
|
try {
|
|
4945
5212
|
const step = await executor(params, provider, context);
|
|
4946
5213
|
if (step.state === 'error') {
|
|
4947
|
-
// Include error details from the step in the thrown error
|
|
4948
|
-
|
|
5214
|
+
// Include error details from the step in the thrown error. Fall back to step.error.
|
|
5215
|
+
let fallbackErrorMessage = 'Unknown error';
|
|
5216
|
+
if (step.error instanceof Error) {
|
|
5217
|
+
fallbackErrorMessage = step.error.message;
|
|
5218
|
+
}
|
|
5219
|
+
else if (typeof step.error === 'string') {
|
|
5220
|
+
fallbackErrorMessage = step.error;
|
|
5221
|
+
}
|
|
5222
|
+
const errorDetails = step.errorMessage ?? fallbackErrorMessage;
|
|
4949
5223
|
throw new Error(`${name} step failed: ${errorDetails}`);
|
|
4950
5224
|
}
|
|
4951
5225
|
context = updateContext?.(step);
|
|
4952
|
-
|
|
4953
|
-
protocol: 'cctp',
|
|
4954
|
-
version: 'v2',
|
|
4955
|
-
values: step,
|
|
4956
|
-
};
|
|
4957
|
-
// use a switch statement for type safety to separate the different step types
|
|
4958
|
-
switch (name) {
|
|
4959
|
-
case 'approve':
|
|
4960
|
-
case 'burn':
|
|
4961
|
-
case 'mint':
|
|
4962
|
-
provider.actionDispatcher?.dispatch(name, {
|
|
4963
|
-
...actionValues,
|
|
4964
|
-
method: name,
|
|
4965
|
-
});
|
|
4966
|
-
break;
|
|
4967
|
-
case 'fetchAttestation':
|
|
4968
|
-
provider.actionDispatcher?.dispatch(name, {
|
|
4969
|
-
...actionValues,
|
|
4970
|
-
method: name,
|
|
4971
|
-
values: step,
|
|
4972
|
-
});
|
|
4973
|
-
break;
|
|
4974
|
-
}
|
|
5226
|
+
dispatchStepEvent(name, step, provider);
|
|
4975
5227
|
result.steps.push(step);
|
|
4976
5228
|
}
|
|
4977
5229
|
catch (error) {
|
|
@@ -5267,10 +5519,12 @@ zod.z.object({
|
|
|
5267
5519
|
*/
|
|
5268
5520
|
const validateBalanceForTransaction = async (params) => {
|
|
5269
5521
|
const { amount, adapter, token, tokenAddress, operationContext } = params;
|
|
5270
|
-
const balancePrepared = await adapter.prepareAction('usdc.balanceOf', {
|
|
5522
|
+
const balancePrepared = await adapter.prepareAction('usdc.balanceOf', {
|
|
5523
|
+
walletAddress: operationContext.address,
|
|
5524
|
+
}, operationContext);
|
|
5271
5525
|
const balance = await balancePrepared.execute();
|
|
5272
5526
|
if (BigInt(balance) < BigInt(amount)) {
|
|
5273
|
-
throw new InsufficientFundsError(balance.toString(), amount, token, tokenAddress,
|
|
5527
|
+
throw new InsufficientFundsError(balance.toString(), amount, token, tokenAddress, operationContext.chain);
|
|
5274
5528
|
}
|
|
5275
5529
|
};
|
|
5276
5530
|
|
|
@@ -5638,12 +5892,7 @@ async function retry(result, context, provider) {
|
|
|
5638
5892
|
throw new Error(errorMessage);
|
|
5639
5893
|
}
|
|
5640
5894
|
stepContext = updateContext?.(step);
|
|
5641
|
-
|
|
5642
|
-
protocol: 'cctp',
|
|
5643
|
-
version: 'v2',
|
|
5644
|
-
method: name,
|
|
5645
|
-
values: step.data,
|
|
5646
|
-
});
|
|
5895
|
+
dispatchStepEvent(name, step, provider);
|
|
5647
5896
|
result.steps.push(step);
|
|
5648
5897
|
}
|
|
5649
5898
|
catch (error) {
|
|
@@ -5839,22 +6088,22 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
5839
6088
|
async estimate(params) {
|
|
5840
6089
|
// CCTP-specific transfer params validation (includes base validation)
|
|
5841
6090
|
assertCCTPv2BridgeParams(params);
|
|
5842
|
-
const { source, destination, amount
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
}
|
|
6091
|
+
const { source, destination, amount } = params;
|
|
6092
|
+
const estimateBurn = async () => {
|
|
6093
|
+
const burn = await this.burn(params);
|
|
6094
|
+
return await burn.estimate(undefined, await source.adapter.calculateTransactionFee(hasCustomContractSupport(source.chain, 'bridge')
|
|
6095
|
+
? CUSTOM_BURN_GAS_ESTIMATE_EVM
|
|
6096
|
+
: DEPOSIT_FOR_BURN_GAS_ESTIMATE_EVM, undefined, source.chain));
|
|
6097
|
+
};
|
|
6098
|
+
const estimateMint = async () => {
|
|
6099
|
+
const mint = await this.mint(source, destination, mockAttestationMessage);
|
|
6100
|
+
return await mint.estimate(undefined, await destination.adapter.calculateTransactionFee(RECEIVE_MESSAGE_GAS_ESTIMATE_EVM, undefined, destination.chain));
|
|
6101
|
+
};
|
|
5853
6102
|
// Parallelize all independent async operations
|
|
5854
6103
|
const [approveEstimate, depositForBurnFee, receiveMessageFee, maxFee] = await Promise.allSettled([
|
|
5855
6104
|
this.approve(source, amount).then(async (approve) => approve.estimate()),
|
|
5856
|
-
|
|
5857
|
-
|
|
6105
|
+
estimateBurn(),
|
|
6106
|
+
estimateMint(),
|
|
5858
6107
|
params.config?.transferSpeed === undefined ||
|
|
5859
6108
|
params.config?.transferSpeed === TransferSpeed.FAST
|
|
5860
6109
|
? this.getMaxFee(params)
|
|
@@ -6021,14 +6270,7 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6021
6270
|
delegate: spenderAddress,
|
|
6022
6271
|
chain: resolvedContext?.chain ?? chain,
|
|
6023
6272
|
};
|
|
6024
|
-
|
|
6025
|
-
if (resolvedContext) {
|
|
6026
|
-
return await adapter.prepareAction('usdc.increaseAllowance', actionParams, resolvedContext);
|
|
6027
|
-
}
|
|
6028
|
-
else {
|
|
6029
|
-
// Legacy fallback for adapters without capabilities
|
|
6030
|
-
return await adapter.prepareAction('usdc.increaseAllowance', actionParams);
|
|
6031
|
-
}
|
|
6273
|
+
return await adapter.prepareAction('usdc.increaseAllowance', actionParams, resolvedContext);
|
|
6032
6274
|
}
|
|
6033
6275
|
/**
|
|
6034
6276
|
* Prepares a CCTP v2 token minting transaction on the destination chain.
|
|
@@ -6072,14 +6314,8 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6072
6314
|
assertCCTPv2WalletContext(destination);
|
|
6073
6315
|
// Extract operation context from destination wallet context
|
|
6074
6316
|
const operationContext = this.extractOperationContext(destination);
|
|
6075
|
-
//
|
|
6076
|
-
|
|
6077
|
-
try {
|
|
6078
|
-
resolvedContext = await resolveOperationContext(destination.adapter, operationContext);
|
|
6079
|
-
}
|
|
6080
|
-
catch (error) {
|
|
6081
|
-
throw new Error(`Failed to resolve operation context: ${error instanceof Error ? error.message : String(error)}`);
|
|
6082
|
-
}
|
|
6317
|
+
// Use recipientAddress if provided, otherwise use destination.address
|
|
6318
|
+
const destinationAddressForMint = destination.recipientAddress ?? destination.address;
|
|
6083
6319
|
// Prepare action parameters
|
|
6084
6320
|
const actionParams = {
|
|
6085
6321
|
fromChain: source.chain,
|
|
@@ -6087,16 +6323,9 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6087
6323
|
message: attestation.message,
|
|
6088
6324
|
attestation: attestation.attestation,
|
|
6089
6325
|
eventNonce: attestation.eventNonce,
|
|
6090
|
-
destinationAddress:
|
|
6326
|
+
destinationAddress: destinationAddressForMint,
|
|
6091
6327
|
};
|
|
6092
|
-
|
|
6093
|
-
if (resolvedContext) {
|
|
6094
|
-
return await destination.adapter.prepareAction('cctp.v2.receiveMessage', actionParams, resolvedContext);
|
|
6095
|
-
}
|
|
6096
|
-
else {
|
|
6097
|
-
// Legacy fallback for adapters without capabilities
|
|
6098
|
-
return await destination.adapter.prepareAction('cctp.v2.receiveMessage', actionParams);
|
|
6099
|
-
}
|
|
6328
|
+
return await destination.adapter.prepareAction('cctp.v2.receiveMessage', actionParams, operationContext);
|
|
6100
6329
|
}
|
|
6101
6330
|
/**
|
|
6102
6331
|
* Fetches attestation data for a burn transaction from the IRIS API.
|
|
@@ -6212,7 +6441,9 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6212
6441
|
* - `source`: The source wallet context with chain definition and adapter
|
|
6213
6442
|
* - `destination`: The destination wallet context with chain definition and adapter
|
|
6214
6443
|
* - `amount`: The bridge amount in minor units (e.g., "1000000" for 1 USDC)
|
|
6215
|
-
* - `config`: Optional bridge configuration including transferSpeed and maxFee settings
|
|
6444
|
+
* - `config`: Optional bridge configuration including transferSpeed and maxFee settings.
|
|
6445
|
+
* When used via BridgeKit, fee values are automatically converted from human-readable
|
|
6446
|
+
* format (e.g., "1") to smallest units (e.g., "1000000").
|
|
6216
6447
|
* @returns The maximum fee to be used for the bridge operation in minor units
|
|
6217
6448
|
*
|
|
6218
6449
|
* @example
|
|
@@ -6220,9 +6451,12 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6220
6451
|
* const maxFee = await provider.getMaxFee({
|
|
6221
6452
|
* source: { adapter: sourceAdapter, chain: Chains.Ethereum, address: '0x...' },
|
|
6222
6453
|
* destination: { adapter: destAdapter, chain: Chains.Base, address: '0x...' },
|
|
6223
|
-
* amount: '1000000', // 1 USDC
|
|
6454
|
+
* amount: '1000000', // 1 USDC in minor units
|
|
6224
6455
|
* token: 'USDC',
|
|
6225
|
-
* config: {
|
|
6456
|
+
* config: {
|
|
6457
|
+
* transferSpeed: TransferSpeed.FAST,
|
|
6458
|
+
* maxFee: '1000000' // Provider receives values in smallest units
|
|
6459
|
+
* }
|
|
6226
6460
|
* })
|
|
6227
6461
|
* console.log('Max fee:', maxFee)
|
|
6228
6462
|
* ```
|
|
@@ -6253,7 +6487,7 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6253
6487
|
maxFee = baseFee + baseFee / 10n;
|
|
6254
6488
|
}
|
|
6255
6489
|
else {
|
|
6256
|
-
// Use the max fee from the config
|
|
6490
|
+
// Use the max fee from the config (converted to minor units by BridgeKit)
|
|
6257
6491
|
maxFee = BigInt(config.maxFee);
|
|
6258
6492
|
}
|
|
6259
6493
|
return maxFee;
|
|
@@ -6290,10 +6524,11 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6290
6524
|
// Get the min finality threshold based on the transfer speed
|
|
6291
6525
|
const minFinalityThreshold = CCTPv2MinFinalityThreshold[transferSpeed];
|
|
6292
6526
|
// Calculate the max fee
|
|
6527
|
+
// Use recipientAddress if provided, otherwise use destination.address
|
|
6528
|
+
const destinationAddressForMint = destination.recipientAddress ?? destination.address;
|
|
6293
6529
|
const [maxFee, mintRecipient] = await Promise.all([
|
|
6294
6530
|
this.getMaxFee(params),
|
|
6295
|
-
getMintRecipientAccount(destination.chain.type, destination.
|
|
6296
|
-
(await destination.adapter.getAddress(destination.chain)), destination.chain.usdcAddress),
|
|
6531
|
+
getMintRecipientAccount(destination.chain.type, destinationAddressForMint, destination.chain.usdcAddress),
|
|
6297
6532
|
]);
|
|
6298
6533
|
const actionParams = {
|
|
6299
6534
|
fromChain: source.chain,
|
|
@@ -6323,14 +6558,7 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6323
6558
|
const finalParams = useCustomBurn
|
|
6324
6559
|
? { ...actionParams, feeRecipient: recipientAddress, protocolFee }
|
|
6325
6560
|
: actionParams;
|
|
6326
|
-
|
|
6327
|
-
if (resolvedContext) {
|
|
6328
|
-
return await source.adapter.prepareAction(actionType, finalParams, resolvedContext);
|
|
6329
|
-
}
|
|
6330
|
-
else {
|
|
6331
|
-
// Legacy fallback for adapters without capabilities
|
|
6332
|
-
return await source.adapter.prepareAction(actionType, finalParams);
|
|
6333
|
-
}
|
|
6561
|
+
return await source.adapter.prepareAction(actionType, finalParams, resolvedContext);
|
|
6334
6562
|
}
|
|
6335
6563
|
/**
|
|
6336
6564
|
* Waits for a transaction to be mined and confirmed on the blockchain.
|
|
@@ -6359,4 +6587,5 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
6359
6587
|
}
|
|
6360
6588
|
|
|
6361
6589
|
exports.CCTPV2BridgingProvider = CCTPV2BridgingProvider;
|
|
6362
|
-
|
|
6590
|
+
exports.getMintRecipientAccount = getMintRecipientAccount;
|
|
6591
|
+
//# sourceMappingURL=index.cjs.map
|