@circle-fin/app-kit 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/README.md +3 -3
- package/bridge.cjs +469 -231
- package/bridge.d.cts +16 -2
- package/bridge.d.mts +16 -2
- package/bridge.d.ts +16 -2
- package/bridge.mjs +469 -231
- package/chains.cjs +94 -0
- package/chains.d.cts +2 -0
- package/chains.d.mts +2 -0
- package/chains.d.ts +2 -0
- package/chains.mjs +94 -0
- package/context.d.cts +16 -2
- package/context.d.mts +16 -2
- package/context.d.ts +16 -2
- package/earn.cjs +438 -208
- package/earn.d.cts +33 -13
- package/earn.d.mts +33 -13
- package/earn.d.ts +33 -13
- package/earn.mjs +438 -208
- package/estimateBridge.cjs +469 -231
- package/estimateBridge.d.cts +16 -2
- package/estimateBridge.d.mts +16 -2
- package/estimateBridge.d.ts +16 -2
- package/estimateBridge.mjs +469 -231
- package/estimateSwap.cjs +210 -87
- package/estimateSwap.d.cts +16 -2
- package/estimateSwap.d.mts +16 -2
- package/estimateSwap.d.ts +16 -2
- package/estimateSwap.mjs +210 -87
- package/index.cjs +884 -413
- package/index.d.cts +284 -27
- package/index.d.mts +284 -27
- package/index.d.ts +284 -27
- package/index.mjs +884 -413
- package/package.json +12 -6
- package/swap.cjs +210 -87
- package/swap.d.cts +16 -2
- package/swap.d.mts +16 -2
- package/swap.d.ts +16 -2
- package/swap.mjs +210 -87
- package/unifiedBalance.cjs +119 -5
- package/unifiedBalance.d.cts +2 -0
- package/unifiedBalance.d.mts +2 -0
- package/unifiedBalance.d.ts +2 -0
- package/unifiedBalance.mjs +119 -5
package/index.mjs
CHANGED
|
@@ -22,6 +22,7 @@ import { parseUnits as parseUnits$1, formatUnits as formatUnits$1 } from '@ether
|
|
|
22
22
|
import { hexlify, hexZeroPad } from '@ethersproject/bytes';
|
|
23
23
|
import { getAddress } from '@ethersproject/address';
|
|
24
24
|
import bs58 from 'bs58';
|
|
25
|
+
import '@ethersproject/abi';
|
|
25
26
|
import { PublicKey } from '@solana/web3.js';
|
|
26
27
|
import 'bn.js';
|
|
27
28
|
import '@coral-xyz/anchor';
|
|
@@ -3551,7 +3552,7 @@ class KitError extends Error {
|
|
|
3551
3552
|
*
|
|
3552
3553
|
* Error code ranges:
|
|
3553
3554
|
* - 1100-1105: INPUT errors — invalid inputs, unsupported configurations
|
|
3554
|
-
* - 8100-
|
|
3555
|
+
* - 8100-8105: SERVICE errors — retryable backend/provider failures
|
|
3555
3556
|
*
|
|
3556
3557
|
* @example
|
|
3557
3558
|
* ```typescript
|
|
@@ -3627,6 +3628,11 @@ class KitError extends Error {
|
|
|
3627
3628
|
code: 8104,
|
|
3628
3629
|
name: 'EARN_PAUSED',
|
|
3629
3630
|
type: 'SERVICE'
|
|
3631
|
+
},
|
|
3632
|
+
/** Position PnL is still reconciling and can be retried. */ POSITION_PNL_PENDING: {
|
|
3633
|
+
code: 8105,
|
|
3634
|
+
name: 'EARN_POSITION_PNL_PENDING',
|
|
3635
|
+
type: 'SERVICE'
|
|
3630
3636
|
}
|
|
3631
3637
|
};
|
|
3632
3638
|
|
|
@@ -3650,11 +3656,11 @@ function getOptionalString(value) {
|
|
|
3650
3656
|
* - vault-not-found, unsupported-chain, unsupported-vault,
|
|
3651
3657
|
* signature-rejected, invalid-id, invalid-batch-size, position-not-registered,
|
|
3652
3658
|
* withdrawal-max-exceeded, insufficient-balance, bridge prepare idempotency
|
|
3653
|
-
* conflicts
|
|
3659
|
+
* conflicts, invalid vault-asset amount precision
|
|
3654
3660
|
*
|
|
3655
3661
|
* SERVICE errors (RETRYABLE) — try again later:
|
|
3656
3662
|
* - signing-failed, provider-error, rewards-fetch-failed,
|
|
3657
|
-
* internal-error, vault-refresh-busy, off-chain-paused,
|
|
3663
|
+
* internal-error, vault-refresh-busy, off-chain-paused, position-PnL-pending,
|
|
3658
3664
|
* bridge failures/status lookup failures
|
|
3659
3665
|
*
|
|
3660
3666
|
* Unrecognized codes fall through to `parseApiError` for HTTP-status-based
|
|
@@ -3834,6 +3840,20 @@ function getOptionalString(value) {
|
|
|
3834
3840
|
recoverability: 'RETRYABLE'
|
|
3835
3841
|
}
|
|
3836
3842
|
],
|
|
3843
|
+
[
|
|
3844
|
+
380415,
|
|
3845
|
+
{
|
|
3846
|
+
errorDef: EarnError.INVALID_INPUT,
|
|
3847
|
+
recoverability: 'FATAL'
|
|
3848
|
+
}
|
|
3849
|
+
],
|
|
3850
|
+
[
|
|
3851
|
+
380416,
|
|
3852
|
+
{
|
|
3853
|
+
errorDef: EarnError.POSITION_PNL_PENDING,
|
|
3854
|
+
recoverability: 'RETRYABLE'
|
|
3855
|
+
}
|
|
3856
|
+
],
|
|
3837
3857
|
// Bridge (380_5XX)
|
|
3838
3858
|
[
|
|
3839
3859
|
380500,
|
|
@@ -3869,6 +3889,13 @@ function getOptionalString(value) {
|
|
|
3869
3889
|
errorDef: EarnError.PROVIDER_ERROR,
|
|
3870
3890
|
recoverability: 'RETRYABLE'
|
|
3871
3891
|
}
|
|
3892
|
+
],
|
|
3893
|
+
[
|
|
3894
|
+
380505,
|
|
3895
|
+
{
|
|
3896
|
+
errorDef: EarnError.PROVIDER_ERROR,
|
|
3897
|
+
recoverability: 'FATAL'
|
|
3898
|
+
}
|
|
3872
3899
|
]
|
|
3873
3900
|
]);
|
|
3874
3901
|
/**
|
|
@@ -3971,6 +3998,8 @@ function getOptionalString(value) {
|
|
|
3971
3998
|
Blockchain["Celo_Alfajores_Testnet"] = "Celo_Alfajores_Testnet";
|
|
3972
3999
|
Blockchain["Codex"] = "Codex";
|
|
3973
4000
|
Blockchain["Codex_Testnet"] = "Codex_Testnet";
|
|
4001
|
+
Blockchain["Cronos"] = "Cronos";
|
|
4002
|
+
Blockchain["Cronos_Testnet"] = "Cronos_Testnet";
|
|
3974
4003
|
Blockchain["Edge"] = "Edge";
|
|
3975
4004
|
Blockchain["Edge_Testnet"] = "Edge_Testnet";
|
|
3976
4005
|
Blockchain["Ethereum"] = "Ethereum";
|
|
@@ -4053,6 +4082,7 @@ var BridgeChain;
|
|
|
4053
4082
|
BridgeChain["Avalanche"] = "Avalanche";
|
|
4054
4083
|
BridgeChain["Base"] = "Base";
|
|
4055
4084
|
BridgeChain["Codex"] = "Codex";
|
|
4085
|
+
BridgeChain["Cronos"] = "Cronos";
|
|
4056
4086
|
BridgeChain["Edge"] = "Edge";
|
|
4057
4087
|
BridgeChain["Ethereum"] = "Ethereum";
|
|
4058
4088
|
BridgeChain["HyperEVM"] = "HyperEVM";
|
|
@@ -4077,6 +4107,7 @@ var BridgeChain;
|
|
|
4077
4107
|
BridgeChain["Avalanche_Fuji"] = "Avalanche_Fuji";
|
|
4078
4108
|
BridgeChain["Base_Sepolia"] = "Base_Sepolia";
|
|
4079
4109
|
BridgeChain["Codex_Testnet"] = "Codex_Testnet";
|
|
4110
|
+
BridgeChain["Cronos_Testnet"] = "Cronos_Testnet";
|
|
4080
4111
|
BridgeChain["Edge_Testnet"] = "Edge_Testnet";
|
|
4081
4112
|
BridgeChain["Ethereum_Sepolia"] = "Ethereum_Sepolia";
|
|
4082
4113
|
BridgeChain["HyperEVM_Testnet"] = "HyperEVM_Testnet";
|
|
@@ -4328,23 +4359,6 @@ var EarnChain;
|
|
|
4328
4359
|
}
|
|
4329
4360
|
});
|
|
4330
4361
|
|
|
4331
|
-
/**
|
|
4332
|
-
* Standard decimal places for tokens.
|
|
4333
|
-
*
|
|
4334
|
-
* These constants define the decimal precision used by different token types
|
|
4335
|
-
* across the SDK. They are used for amount conversions between human-readable
|
|
4336
|
-
* format and smallest units (base units).
|
|
4337
|
-
*
|
|
4338
|
-
* @remarks
|
|
4339
|
-
* - Most stablecoins (USDC, EURC, USDT, PYUSD) use 6 decimal places
|
|
4340
|
-
* - Some stablecoins (DAI, USDE) and most native tokens (ETH, POL, PLUME) use 18 decimals
|
|
4341
|
-
* - The NATIVE alias uses chain-specific decimals which may vary (e.g., SOL uses 9)
|
|
4342
|
-
*/ /**
|
|
4343
|
-
* Standard decimal places for 6-decimal tokens.
|
|
4344
|
-
*
|
|
4345
|
-
* Used by most stablecoins: USDC, EURC, USDT, PYUSD
|
|
4346
|
-
*/ const TOKEN_DECIMALS_6 = 6;
|
|
4347
|
-
|
|
4348
4362
|
/**
|
|
4349
4363
|
* @packageDocumentation
|
|
4350
4364
|
* @module SwapTokenRegistry
|
|
@@ -5160,6 +5174,96 @@ var EarnChain;
|
|
|
5160
5174
|
}
|
|
5161
5175
|
});
|
|
5162
5176
|
|
|
5177
|
+
/**
|
|
5178
|
+
* Cronos Mainnet chain definition
|
|
5179
|
+
* @remarks
|
|
5180
|
+
* This represents the official production network for the Cronos blockchain.
|
|
5181
|
+
* Cronos is an EVM-compatible blockchain.
|
|
5182
|
+
*/ const Cronos = defineChain({
|
|
5183
|
+
type: 'evm',
|
|
5184
|
+
chain: Blockchain.Cronos,
|
|
5185
|
+
name: 'Cronos',
|
|
5186
|
+
title: 'Cronos Mainnet',
|
|
5187
|
+
nativeCurrency: {
|
|
5188
|
+
name: 'Cronos',
|
|
5189
|
+
symbol: 'CRO',
|
|
5190
|
+
decimals: 18
|
|
5191
|
+
},
|
|
5192
|
+
chainId: 25,
|
|
5193
|
+
isTestnet: false,
|
|
5194
|
+
explorerUrl: 'https://cronoscan.com/tx/{hash}',
|
|
5195
|
+
rpcEndpoints: [
|
|
5196
|
+
'https://evm.cronos.org'
|
|
5197
|
+
],
|
|
5198
|
+
eurcAddress: '0xA6dE01a2d62C6B5f3525d768f34d276652C554c8',
|
|
5199
|
+
usdcAddress: '0x3D7F2C478aAfdB65542BCB44bCeeC05849999d2D',
|
|
5200
|
+
usdtAddress: null,
|
|
5201
|
+
cctp: {
|
|
5202
|
+
domain: 32,
|
|
5203
|
+
contracts: {
|
|
5204
|
+
v2: {
|
|
5205
|
+
type: 'split',
|
|
5206
|
+
tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
|
|
5207
|
+
messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
|
|
5208
|
+
confirmations: 1,
|
|
5209
|
+
fastConfirmations: 1
|
|
5210
|
+
}
|
|
5211
|
+
},
|
|
5212
|
+
forwarderSupported: {
|
|
5213
|
+
source: false,
|
|
5214
|
+
destination: false
|
|
5215
|
+
}
|
|
5216
|
+
},
|
|
5217
|
+
kitContracts: {
|
|
5218
|
+
bridge: BRIDGE_CONTRACT_EVM_MAINNET
|
|
5219
|
+
}
|
|
5220
|
+
});
|
|
5221
|
+
|
|
5222
|
+
/**
|
|
5223
|
+
* Cronos Testnet chain definition
|
|
5224
|
+
* @remarks
|
|
5225
|
+
* This represents the official test network for the Cronos blockchain.
|
|
5226
|
+
* Cronos is an EVM-compatible blockchain.
|
|
5227
|
+
*/ const CronosTestnet = defineChain({
|
|
5228
|
+
type: 'evm',
|
|
5229
|
+
chain: Blockchain.Cronos_Testnet,
|
|
5230
|
+
name: 'Cronos Testnet',
|
|
5231
|
+
title: 'Cronos Testnet',
|
|
5232
|
+
nativeCurrency: {
|
|
5233
|
+
name: 'CRO',
|
|
5234
|
+
symbol: 'tCRO',
|
|
5235
|
+
decimals: 18
|
|
5236
|
+
},
|
|
5237
|
+
chainId: 338,
|
|
5238
|
+
isTestnet: true,
|
|
5239
|
+
explorerUrl: 'https://explorer.cronos.org/testnet/tx/{hash}',
|
|
5240
|
+
rpcEndpoints: [
|
|
5241
|
+
'https://evm-t3.cronos.org'
|
|
5242
|
+
],
|
|
5243
|
+
eurcAddress: '0x31f7538adb53cF16350e6B0c89d03D91b7D12c46',
|
|
5244
|
+
usdcAddress: '0xEb33dc5fac03833e132593659e1dE7256aB59794',
|
|
5245
|
+
usdtAddress: null,
|
|
5246
|
+
cctp: {
|
|
5247
|
+
domain: 32,
|
|
5248
|
+
contracts: {
|
|
5249
|
+
v2: {
|
|
5250
|
+
type: 'split',
|
|
5251
|
+
tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
|
|
5252
|
+
messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
|
|
5253
|
+
confirmations: 1,
|
|
5254
|
+
fastConfirmations: 1
|
|
5255
|
+
}
|
|
5256
|
+
},
|
|
5257
|
+
forwarderSupported: {
|
|
5258
|
+
source: false,
|
|
5259
|
+
destination: false
|
|
5260
|
+
}
|
|
5261
|
+
},
|
|
5262
|
+
kitContracts: {
|
|
5263
|
+
bridge: BRIDGE_CONTRACT_EVM_TESTNET
|
|
5264
|
+
}
|
|
5265
|
+
});
|
|
5266
|
+
|
|
5163
5267
|
/**
|
|
5164
5268
|
* Edge Mainnet chain definition
|
|
5165
5269
|
* @remarks
|
|
@@ -7511,6 +7615,8 @@ var Chains = /*#__PURE__*/Object.freeze({
|
|
|
7511
7615
|
CeloAlfajoresTestnet: CeloAlfajoresTestnet,
|
|
7512
7616
|
Codex: Codex,
|
|
7513
7617
|
CodexTestnet: CodexTestnet,
|
|
7618
|
+
Cronos: Cronos,
|
|
7619
|
+
CronosTestnet: CronosTestnet,
|
|
7514
7620
|
Edge: Edge,
|
|
7515
7621
|
EdgeTestnet: EdgeTestnet,
|
|
7516
7622
|
Ethereum: Ethereum,
|
|
@@ -9885,6 +9991,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
9885
9991
|
[Blockchain.Base]: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
|
|
9886
9992
|
[Blockchain.Celo]: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C',
|
|
9887
9993
|
[Blockchain.Codex]: '0xd996633a415985DBd7D6D12f4A4343E31f5037cf',
|
|
9994
|
+
[Blockchain.Cronos]: '0x3D7F2C478aAfdB65542BCB44bCeeC05849999d2D',
|
|
9888
9995
|
[Blockchain.Edge]: '0x98d2919b9A214E6Fa5384AC81E6864bA686Ad74c',
|
|
9889
9996
|
[Blockchain.Ethereum]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
|
9890
9997
|
[Blockchain.Hedera]: '0.0.456858',
|
|
@@ -9918,6 +10025,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
9918
10025
|
[Blockchain.Avalanche_Fuji]: '0x5425890298aed601595a70AB815c96711a31Bc65',
|
|
9919
10026
|
[Blockchain.Base_Sepolia]: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
|
|
9920
10027
|
[Blockchain.Codex_Testnet]: '0x6d7f141b6819C2c9CC2f818e6ad549E7Ca090F8f',
|
|
10028
|
+
[Blockchain.Cronos_Testnet]: '0xEb33dc5fac03833e132593659e1dE7256aB59794',
|
|
9921
10029
|
[Blockchain.Edge_Testnet]: '0x2d9F7CAD728051AA35Ecdc472a14cf8cDF5CFD6B',
|
|
9922
10030
|
[Blockchain.Ethereum_Sepolia]: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',
|
|
9923
10031
|
[Blockchain.Hedera_Testnet]: '0.0.429274',
|
|
@@ -9985,13 +10093,22 @@ function parseOrThrow(value, schema, context) {
|
|
|
9985
10093
|
symbol: 'EURC',
|
|
9986
10094
|
decimals: 6,
|
|
9987
10095
|
locators: {
|
|
10096
|
+
// =========================================================================
|
|
10097
|
+
// Mainnets
|
|
10098
|
+
// =========================================================================
|
|
9988
10099
|
[Blockchain.Avalanche]: '0xc891EB4cbdEFf6e073e859e987815Ed1505c2ACD',
|
|
9989
10100
|
[Blockchain.Base]: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42',
|
|
10101
|
+
[Blockchain.Cronos]: '0xA6dE01a2d62C6B5f3525d768f34d276652C554c8',
|
|
9990
10102
|
[Blockchain.Ethereum]: '0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c',
|
|
9991
10103
|
[Blockchain.Solana]: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr',
|
|
9992
10104
|
[Blockchain.World_Chain]: '0x1C60ba0A0eD1019e8Eb035E6daF4155A5cE2380B',
|
|
10105
|
+
// =========================================================================
|
|
9993
10106
|
// Testnets
|
|
9994
|
-
|
|
10107
|
+
// =========================================================================
|
|
10108
|
+
[Blockchain.Arc_Testnet]: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
|
|
10109
|
+
[Blockchain.Base_Sepolia]: '0x808456652fdb597867f38412077A9182bf77359F',
|
|
10110
|
+
[Blockchain.Cronos_Testnet]: '0x31f7538adb53cF16350e6B0c89d03D91b7D12c46',
|
|
10111
|
+
[Blockchain.Ethereum_Sepolia]: '0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4'
|
|
9995
10112
|
}
|
|
9996
10113
|
};
|
|
9997
10114
|
|
|
@@ -10201,8 +10318,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
10201
10318
|
* cirBTC (Circle Bitcoin) token definition with addresses and metadata.
|
|
10202
10319
|
*
|
|
10203
10320
|
* @remarks
|
|
10204
|
-
* Built-in cirBTC definition for the TokenRegistry.
|
|
10205
|
-
* on Arc Testnet.
|
|
10321
|
+
* Built-in cirBTC definition for the TokenRegistry.
|
|
10206
10322
|
*
|
|
10207
10323
|
* @example
|
|
10208
10324
|
* ```typescript
|
|
@@ -10218,7 +10334,15 @@ function parseOrThrow(value, schema, context) {
|
|
|
10218
10334
|
symbol: 'cirBTC',
|
|
10219
10335
|
decimals: 8,
|
|
10220
10336
|
locators: {
|
|
10221
|
-
|
|
10337
|
+
// =========================================================================
|
|
10338
|
+
// Mainnets
|
|
10339
|
+
// =========================================================================
|
|
10340
|
+
[Blockchain.Ethereum]: '0x72DFB2E44f59C5AD2bAFE84314E5b99a7cd5075E',
|
|
10341
|
+
// =========================================================================
|
|
10342
|
+
// Testnets
|
|
10343
|
+
// =========================================================================
|
|
10344
|
+
[Blockchain.Arc_Testnet]: '0xf0C4a4CE82A5746AbAAd9425360Ab04fbBA432BF',
|
|
10345
|
+
[Blockchain.Ethereum_Sepolia]: '0x3a3fe695F684Bf9b9e43CF43C2b895Ea5e392bB3'
|
|
10222
10346
|
}
|
|
10223
10347
|
};
|
|
10224
10348
|
|
|
@@ -11498,11 +11622,11 @@ function resolveOptions(options) {
|
|
|
11498
11622
|
void emitAnalyticsLog(buildPayload$1(config, stepEntry?.[1] ?? fallbackEventType, errorDetails, context));
|
|
11499
11623
|
}
|
|
11500
11624
|
|
|
11501
|
-
var name$
|
|
11502
|
-
var version$
|
|
11503
|
-
var pkg$
|
|
11504
|
-
name: name$
|
|
11505
|
-
version: version$
|
|
11625
|
+
var name$4 = "@circle-fin/bridge-kit";
|
|
11626
|
+
var version$5 = "1.12.0";
|
|
11627
|
+
var pkg$5 = {
|
|
11628
|
+
name: name$4,
|
|
11629
|
+
version: version$5};
|
|
11506
11630
|
|
|
11507
11631
|
const assertCustomFeePolicySymbol$2 = Symbol('assertCustomFeePolicy');
|
|
11508
11632
|
/**
|
|
@@ -13528,7 +13652,7 @@ var TransferSpeed;
|
|
|
13528
13652
|
const bridgeKitCaller = {
|
|
13529
13653
|
type: 'kit',
|
|
13530
13654
|
name: 'BridgeKit',
|
|
13531
|
-
version: pkg$
|
|
13655
|
+
version: pkg$5.version
|
|
13532
13656
|
};
|
|
13533
13657
|
// Create default runtime and tokens for invocation context resolution
|
|
13534
13658
|
const defaults = {
|
|
@@ -14198,6 +14322,15 @@ const CUSTOM_BURN_GAS_ESTIMATE_EVM = 201_525n // p99 and max are same here: 201_
|
|
|
14198
14322
|
;
|
|
14199
14323
|
const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_839n) / 2 = 237_401n
|
|
14200
14324
|
;
|
|
14325
|
+
// Hard execution caps: observed max + ~30% buffer, used as gasLimit overrides on
|
|
14326
|
+
// chains whose eth_estimateGas under-reports (e.g. Cronos EIP-7623 calldata floor).
|
|
14327
|
+
// Kept separate from the fee-estimate averages above.
|
|
14328
|
+
const APPROVE_GAS_LIMIT_EVM = 100_000n // ERC-20 approve observed max ~46k
|
|
14329
|
+
;
|
|
14330
|
+
const DEPOSIT_FOR_BURN_GAS_LIMIT_EVM = 300_000n // observed max 226_506 + ~30%
|
|
14331
|
+
;
|
|
14332
|
+
const RECEIVE_MESSAGE_GAS_LIMIT_EVM = 400_000n // observed max 310_839 + ~30%
|
|
14333
|
+
;
|
|
14201
14334
|
/**
|
|
14202
14335
|
* The minimum finality threshold for CCTPv2 transfers.
|
|
14203
14336
|
*
|
|
@@ -14226,6 +14359,27 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
14226
14359
|
'Content-Type': 'application/json'
|
|
14227
14360
|
}
|
|
14228
14361
|
};
|
|
14362
|
+
/**
|
|
14363
|
+
* Merges caller-provided polling overrides on top of {@link DEFAULT_CONFIG}.
|
|
14364
|
+
*
|
|
14365
|
+
* Headers are merged independently so caller-supplied headers augment the
|
|
14366
|
+
* defaults (such as `Content-Type`) rather than replacing them wholesale.
|
|
14367
|
+
*
|
|
14368
|
+
* @param config - Caller-provided polling configuration overrides
|
|
14369
|
+
* @param internalDefaults - Internal defaults applied before `config` (for example a
|
|
14370
|
+
* reduced `maxRetries` for one-shot requests); `config` still wins on conflict
|
|
14371
|
+
* @returns The effective polling configuration
|
|
14372
|
+
* @internal
|
|
14373
|
+
*/ const mergeAttestationConfig = (config, internalDefaults = {})=>({
|
|
14374
|
+
...DEFAULT_CONFIG$2,
|
|
14375
|
+
...internalDefaults,
|
|
14376
|
+
...config,
|
|
14377
|
+
headers: {
|
|
14378
|
+
...DEFAULT_CONFIG$2.headers,
|
|
14379
|
+
...internalDefaults.headers,
|
|
14380
|
+
...config.headers
|
|
14381
|
+
}
|
|
14382
|
+
});
|
|
14229
14383
|
/**
|
|
14230
14384
|
* Type guard that verifies if an unknown value matches the AttestationMessage shape
|
|
14231
14385
|
* and has all required properties.
|
|
@@ -14372,10 +14526,7 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
14372
14526
|
* ```
|
|
14373
14527
|
*/ const fetchAttestation = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
14374
14528
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
14375
|
-
const effectiveConfig =
|
|
14376
|
-
...DEFAULT_CONFIG$2,
|
|
14377
|
-
...config
|
|
14378
|
-
};
|
|
14529
|
+
const effectiveConfig = mergeAttestationConfig(config);
|
|
14379
14530
|
return await pollApiGet(url, isAttestationResponse, effectiveConfig);
|
|
14380
14531
|
};
|
|
14381
14532
|
/**
|
|
@@ -14418,11 +14569,9 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
14418
14569
|
*/ const fetchAttestationWithoutStatusCheck = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
14419
14570
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
14420
14571
|
// Use minimal retries since we're just fetching existing data
|
|
14421
|
-
const effectiveConfig = {
|
|
14422
|
-
|
|
14423
|
-
|
|
14424
|
-
...config
|
|
14425
|
-
};
|
|
14572
|
+
const effectiveConfig = mergeAttestationConfig(config, {
|
|
14573
|
+
maxRetries: 3
|
|
14574
|
+
});
|
|
14426
14575
|
return await pollApiGet(url, isAttestationResponseWithoutStatusCheck, effectiveConfig);
|
|
14427
14576
|
};
|
|
14428
14577
|
/**
|
|
@@ -14482,10 +14631,7 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
14482
14631
|
* ```
|
|
14483
14632
|
*/ const fetchReAttestedAttestation = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
14484
14633
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
14485
|
-
const effectiveConfig =
|
|
14486
|
-
...DEFAULT_CONFIG$2,
|
|
14487
|
-
...config
|
|
14488
|
-
};
|
|
14634
|
+
const effectiveConfig = mergeAttestationConfig(config);
|
|
14489
14635
|
return await pollApiGet(url, isReAttestedAttestationResponse, effectiveConfig);
|
|
14490
14636
|
};
|
|
14491
14637
|
/**
|
|
@@ -14551,14 +14697,139 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
|
|
|
14551
14697
|
*/ const requestReAttestation = async (nonce, isTestnet, config = {})=>{
|
|
14552
14698
|
const url = buildReAttestUrl(nonce, isTestnet);
|
|
14553
14699
|
// Use minimal retries since we're just submitting a request, not polling for state
|
|
14554
|
-
const effectiveConfig = {
|
|
14555
|
-
|
|
14556
|
-
|
|
14557
|
-
...config
|
|
14558
|
-
};
|
|
14700
|
+
const effectiveConfig = mergeAttestationConfig(config, {
|
|
14701
|
+
maxRetries: 3
|
|
14702
|
+
});
|
|
14559
14703
|
return await pollApiPost(url, {}, isReAttestationResponse, effectiveConfig);
|
|
14560
14704
|
};
|
|
14561
14705
|
|
|
14706
|
+
/**
|
|
14707
|
+
* Type guard that checks if the relayer has confirmed the mint transaction.
|
|
14708
|
+
*
|
|
14709
|
+
* This function validates that:
|
|
14710
|
+
* 1. The response has valid AttestationResponse structure
|
|
14711
|
+
* 2. At least one message has forwardState === 'CONFIRMED' (or 'COMPLETE') and a valid forwardTxHash
|
|
14712
|
+
*
|
|
14713
|
+
* If forwardState is 'FAILED', throws a non-retryable KitError.
|
|
14714
|
+
* If forwardState is 'PENDING' or not present, throws a RETRYABLE KitError to continue polling.
|
|
14715
|
+
*
|
|
14716
|
+
* @param obj - The value to check, typically a parsed JSON response
|
|
14717
|
+
* @returns True if the relayer has confirmed the mint
|
|
14718
|
+
* @throws {KitError} With FATAL recoverability if structure is invalid
|
|
14719
|
+
* @throws {KitError} With RESUMABLE recoverability if forwardState is 'FAILED'
|
|
14720
|
+
* @throws {KitError} With RETRYABLE recoverability if still pending
|
|
14721
|
+
* @internal
|
|
14722
|
+
*/ const isRelayerMintConfirmed = (obj)=>{
|
|
14723
|
+
// First check if the structure is valid
|
|
14724
|
+
if (!hasValidAttestationStructure(obj)) {
|
|
14725
|
+
throw new KitError({
|
|
14726
|
+
...InputError.VALIDATION_FAILED,
|
|
14727
|
+
recoverability: 'FATAL',
|
|
14728
|
+
message: 'Invalid attestation response structure from IRIS API.'
|
|
14729
|
+
});
|
|
14730
|
+
}
|
|
14731
|
+
// Find the first message (typically there's only one)
|
|
14732
|
+
const message = obj.messages[0];
|
|
14733
|
+
if (!message) {
|
|
14734
|
+
throw new KitError({
|
|
14735
|
+
...InputError.VALIDATION_FAILED,
|
|
14736
|
+
recoverability: 'FATAL',
|
|
14737
|
+
message: 'No attestation messages found in IRIS API response.'
|
|
14738
|
+
});
|
|
14739
|
+
}
|
|
14740
|
+
// Check for FAILED state - this is a permanent failure
|
|
14741
|
+
if (message.forwardState === 'FAILED') {
|
|
14742
|
+
throw new KitError({
|
|
14743
|
+
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
14744
|
+
recoverability: 'RESUMABLE',
|
|
14745
|
+
message: 'Circle relayer failed to forward the mint transaction. The mint may still have succeeded if another party submitted it. Check the recipient wallet balance before retrying. If the mint did not occur, you can manually submit it using the attestation data in the error cause.',
|
|
14746
|
+
cause: {
|
|
14747
|
+
trace: {
|
|
14748
|
+
eventNonce: message.eventNonce,
|
|
14749
|
+
attestation: message.attestation,
|
|
14750
|
+
message: message.message
|
|
14751
|
+
}
|
|
14752
|
+
}
|
|
14753
|
+
});
|
|
14754
|
+
}
|
|
14755
|
+
// Check if mint is confirmed (or complete) with a valid transaction hash
|
|
14756
|
+
// We accept both CONFIRMED and COMPLETE since COMPLETE implies CONFIRMED
|
|
14757
|
+
if ((message.forwardState === 'CONFIRMED' || message.forwardState === 'COMPLETE') && typeof message.forwardTxHash === 'string' && message.forwardTxHash.trim().length > 0) {
|
|
14758
|
+
return true;
|
|
14759
|
+
}
|
|
14760
|
+
// Still pending or not yet processed - throw RETRYABLE error to continue polling
|
|
14761
|
+
throw new KitError({
|
|
14762
|
+
...NetworkError.RELAYER_PENDING,
|
|
14763
|
+
recoverability: 'RETRYABLE',
|
|
14764
|
+
message: 'Relayer mint not ready. Waiting for confirmation.'
|
|
14765
|
+
});
|
|
14766
|
+
};
|
|
14767
|
+
/**
|
|
14768
|
+
* Polls the attestation API until the relayer's mint transaction is confirmed.
|
|
14769
|
+
*
|
|
14770
|
+
* This function is used when `useForwarder` is enabled. Instead of the user
|
|
14771
|
+
* submitting the mint transaction, Circle's Orbit relayer handles it automatically.
|
|
14772
|
+
* This function polls until the relayer has submitted and confirmed the mint transaction.
|
|
14773
|
+
*
|
|
14774
|
+
* @remarks
|
|
14775
|
+
* - Uses a 20-minute timeout by default (600 retries × 2 seconds)
|
|
14776
|
+
* - Throws immediately if `forwardState` is 'FAILED'
|
|
14777
|
+
* - Waits for `forwardState` to be 'CONFIRMED' or 'COMPLETE' (COMPLETE implies CONFIRMED)
|
|
14778
|
+
* - Returns the attestation message with `forwardTxHash` populated
|
|
14779
|
+
*
|
|
14780
|
+
* @param sourceDomainId - The CCTP domain ID of the source chain
|
|
14781
|
+
* @param transactionHash - The transaction hash of the burn operation
|
|
14782
|
+
* @param isTestnet - Whether this is for a testnet chain (true) or mainnet (false)
|
|
14783
|
+
* @param config - Optional configuration overrides for polling behavior
|
|
14784
|
+
* @returns The attestation message with confirmed forwardTxHash
|
|
14785
|
+
* @throws {KitError} With code 'NETWORK_RELAYER_FORWARD_FAILED' if relayer failed
|
|
14786
|
+
* @throws {KitError} If timeout is reached while still pending
|
|
14787
|
+
*
|
|
14788
|
+
* @example
|
|
14789
|
+
* ```typescript
|
|
14790
|
+
* const attestation = await fetchRelayerMint(0, '0xabc...', false)
|
|
14791
|
+
* console.log('Relayer mint tx:', attestation.forwardTxHash)
|
|
14792
|
+
* ```
|
|
14793
|
+
*/ const fetchRelayerMint = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
14794
|
+
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
14795
|
+
const effectiveConfig = mergeAttestationConfig(config);
|
|
14796
|
+
let response;
|
|
14797
|
+
try {
|
|
14798
|
+
response = await pollApiGet(url, isRelayerMintConfirmed, effectiveConfig);
|
|
14799
|
+
} catch (error) {
|
|
14800
|
+
// Enrich RELAYER_FORWARD_FAILED errors with the burn transaction hash
|
|
14801
|
+
if (error instanceof KitError && error.name === 'NETWORK_RELAYER_FORWARD_FAILED') {
|
|
14802
|
+
throw new KitError({
|
|
14803
|
+
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
14804
|
+
recoverability: error.recoverability,
|
|
14805
|
+
message: error.message,
|
|
14806
|
+
cause: {
|
|
14807
|
+
...error.cause,
|
|
14808
|
+
trace: {
|
|
14809
|
+
...error.cause?.trace,
|
|
14810
|
+
burnTxHash: transactionHash
|
|
14811
|
+
}
|
|
14812
|
+
}
|
|
14813
|
+
});
|
|
14814
|
+
}
|
|
14815
|
+
throw error;
|
|
14816
|
+
}
|
|
14817
|
+
// Return the first message (which should have forwardTxHash)
|
|
14818
|
+
// Note: This check is needed for TypeScript type safety even though
|
|
14819
|
+
// isRelayerMintConfirmed validates messages[0] exists. The type guard
|
|
14820
|
+
// narrows the type at the call site, but TypeScript can't infer that
|
|
14821
|
+
// the array still has elements after pollApiGet returns.
|
|
14822
|
+
const message = response.messages[0];
|
|
14823
|
+
if (!message) {
|
|
14824
|
+
throw new KitError({
|
|
14825
|
+
...InputError.VALIDATION_FAILED,
|
|
14826
|
+
recoverability: 'FATAL',
|
|
14827
|
+
message: 'No attestation messages found in response after polling.'
|
|
14828
|
+
});
|
|
14829
|
+
}
|
|
14830
|
+
return message;
|
|
14831
|
+
};
|
|
14832
|
+
|
|
14562
14833
|
const assertCCTPv2WalletContextSymbol = Symbol('assertCCTPv2WalletContext');
|
|
14563
14834
|
/**
|
|
14564
14835
|
* Asserts that the provided parameters match the CCTPv2 wallet context interface.
|
|
@@ -15433,6 +15704,8 @@ function hasPendingState(analysis, result) {
|
|
|
15433
15704
|
* - `adapter`: The adapter that will execute the transaction
|
|
15434
15705
|
* - `confirmations`: The number of confirmations to wait for (defaults to 1)
|
|
15435
15706
|
* - `timeout`: The timeout for the request in milliseconds
|
|
15707
|
+
* - `gasLimit`: Optional explicit gas limit (number) forwarded to EVM execute,
|
|
15708
|
+
* bypassing `eth_estimateGas`; ignored for non-EVM requests
|
|
15436
15709
|
* @returns The bridge step with the transaction details and explorer URL
|
|
15437
15710
|
* @throws If the transaction execution fails
|
|
15438
15711
|
*
|
|
@@ -15447,7 +15720,7 @@ function hasPendingState(analysis, result) {
|
|
|
15447
15720
|
* })
|
|
15448
15721
|
* console.log('Transaction hash:', step.txHash)
|
|
15449
15722
|
* ```
|
|
15450
|
-
*/ async function executePreparedChainRequest({ name, request, adapter, chain, confirmations = 1, timeout }) {
|
|
15723
|
+
*/ async function executePreparedChainRequest({ name, request, adapter, chain, confirmations = 1, timeout, gasLimit }) {
|
|
15451
15724
|
const step = {
|
|
15452
15725
|
name,
|
|
15453
15726
|
state: 'pending'
|
|
@@ -15460,7 +15733,9 @@ function hasPendingState(analysis, result) {
|
|
|
15460
15733
|
step.state = 'noop';
|
|
15461
15734
|
return step;
|
|
15462
15735
|
}
|
|
15463
|
-
const txHash = await request.execute(
|
|
15736
|
+
const txHash = request.type === 'evm' && gasLimit !== undefined ? await request.execute({
|
|
15737
|
+
gasLimit
|
|
15738
|
+
}) : await request.execute();
|
|
15464
15739
|
step.txHash = txHash;
|
|
15465
15740
|
const retryOptions = {
|
|
15466
15741
|
isRetryable: (err)=>isRetryableError$1(parseBlockchainError(err, {
|
|
@@ -15532,7 +15807,8 @@ function hasPendingState(analysis, result) {
|
|
|
15532
15807
|
name: 'approve',
|
|
15533
15808
|
adapter: params.source.adapter,
|
|
15534
15809
|
chain: params.source.chain,
|
|
15535
|
-
request: await provider.approve(params.source, approvalAmount)
|
|
15810
|
+
request: await provider.approve(params.source, approvalAmount),
|
|
15811
|
+
gasLimit: Number(APPROVE_GAS_LIMIT_EVM)
|
|
15536
15812
|
});
|
|
15537
15813
|
}
|
|
15538
15814
|
|
|
@@ -15559,7 +15835,8 @@ function hasPendingState(analysis, result) {
|
|
|
15559
15835
|
name: 'burn',
|
|
15560
15836
|
adapter: params.source.adapter,
|
|
15561
15837
|
chain: params.source.chain,
|
|
15562
|
-
request: await provider.burn(params)
|
|
15838
|
+
request: await provider.burn(params),
|
|
15839
|
+
gasLimit: Number(DEPOSIT_FOR_BURN_GAS_LIMIT_EVM)
|
|
15563
15840
|
});
|
|
15564
15841
|
}
|
|
15565
15842
|
|
|
@@ -15645,11 +15922,18 @@ function hasPendingState(analysis, result) {
|
|
|
15645
15922
|
*/ async function bridgeMint({ params, provider }, attestation) {
|
|
15646
15923
|
// Validate attestation message matches transfer params
|
|
15647
15924
|
await assertCCTPv2AttestationParams(attestation, params);
|
|
15925
|
+
const mintRequest = await provider.mint(params.source, params.destination, attestation);
|
|
15648
15926
|
const step = await executePreparedChainRequest({
|
|
15649
15927
|
name: 'mint',
|
|
15650
15928
|
adapter: params.destination.adapter,
|
|
15651
15929
|
chain: params.destination.chain,
|
|
15652
|
-
request:
|
|
15930
|
+
request: mintRequest,
|
|
15931
|
+
// Some chains (e.g. Cronos) enforce an EIP-7623 calldata gas floor that
|
|
15932
|
+
// eth_estimateGas does not account for, returning a below-floor value
|
|
15933
|
+
// without reverting. Pinning to a value above the observed execution max
|
|
15934
|
+
// (310_839) bypasses re-estimation and guarantees we clear both the floor
|
|
15935
|
+
// and the actual execution cost.
|
|
15936
|
+
gasLimit: Number(RECEIVE_MESSAGE_GAS_LIMIT_EVM)
|
|
15653
15937
|
});
|
|
15654
15938
|
// Add forwarded: false for non-relayer mints
|
|
15655
15939
|
return {
|
|
@@ -15687,136 +15971,6 @@ const mockAttestationMessage = {
|
|
|
15687
15971
|
delayReason: null
|
|
15688
15972
|
};
|
|
15689
15973
|
|
|
15690
|
-
/**
|
|
15691
|
-
* Type guard that checks if the relayer has confirmed the mint transaction.
|
|
15692
|
-
*
|
|
15693
|
-
* This function validates that:
|
|
15694
|
-
* 1. The response has valid AttestationResponse structure
|
|
15695
|
-
* 2. At least one message has forwardState === 'CONFIRMED' (or 'COMPLETE') and a valid forwardTxHash
|
|
15696
|
-
*
|
|
15697
|
-
* If forwardState is 'FAILED', throws a non-retryable KitError.
|
|
15698
|
-
* If forwardState is 'PENDING' or not present, throws a RETRYABLE KitError to continue polling.
|
|
15699
|
-
*
|
|
15700
|
-
* @param obj - The value to check, typically a parsed JSON response
|
|
15701
|
-
* @returns True if the relayer has confirmed the mint
|
|
15702
|
-
* @throws {KitError} With FATAL recoverability if structure is invalid
|
|
15703
|
-
* @throws {KitError} With RESUMABLE recoverability if forwardState is 'FAILED'
|
|
15704
|
-
* @throws {KitError} With RETRYABLE recoverability if still pending
|
|
15705
|
-
* @internal
|
|
15706
|
-
*/ const isRelayerMintConfirmed = (obj)=>{
|
|
15707
|
-
// First check if the structure is valid
|
|
15708
|
-
if (!hasValidAttestationStructure(obj)) {
|
|
15709
|
-
throw new KitError({
|
|
15710
|
-
...InputError.VALIDATION_FAILED,
|
|
15711
|
-
recoverability: 'FATAL',
|
|
15712
|
-
message: 'Invalid attestation response structure from IRIS API.'
|
|
15713
|
-
});
|
|
15714
|
-
}
|
|
15715
|
-
// Find the first message (typically there's only one)
|
|
15716
|
-
const message = obj.messages[0];
|
|
15717
|
-
if (!message) {
|
|
15718
|
-
throw new KitError({
|
|
15719
|
-
...InputError.VALIDATION_FAILED,
|
|
15720
|
-
recoverability: 'FATAL',
|
|
15721
|
-
message: 'No attestation messages found in IRIS API response.'
|
|
15722
|
-
});
|
|
15723
|
-
}
|
|
15724
|
-
// Check for FAILED state - this is a permanent failure
|
|
15725
|
-
if (message.forwardState === 'FAILED') {
|
|
15726
|
-
throw new KitError({
|
|
15727
|
-
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
15728
|
-
recoverability: 'RESUMABLE',
|
|
15729
|
-
message: 'Circle relayer failed to forward the mint transaction. The mint may still have succeeded if another party submitted it. Check the recipient wallet balance before retrying. If the mint did not occur, you can manually submit it using the attestation data in the error cause.',
|
|
15730
|
-
cause: {
|
|
15731
|
-
trace: {
|
|
15732
|
-
eventNonce: message.eventNonce,
|
|
15733
|
-
attestation: message.attestation,
|
|
15734
|
-
message: message.message
|
|
15735
|
-
}
|
|
15736
|
-
}
|
|
15737
|
-
});
|
|
15738
|
-
}
|
|
15739
|
-
// Check if mint is confirmed (or complete) with a valid transaction hash
|
|
15740
|
-
// We accept both CONFIRMED and COMPLETE since COMPLETE implies CONFIRMED
|
|
15741
|
-
if ((message.forwardState === 'CONFIRMED' || message.forwardState === 'COMPLETE') && typeof message.forwardTxHash === 'string' && message.forwardTxHash.trim().length > 0) {
|
|
15742
|
-
return true;
|
|
15743
|
-
}
|
|
15744
|
-
// Still pending or not yet processed - throw RETRYABLE error to continue polling
|
|
15745
|
-
throw new KitError({
|
|
15746
|
-
...NetworkError.RELAYER_PENDING,
|
|
15747
|
-
recoverability: 'RETRYABLE',
|
|
15748
|
-
message: 'Relayer mint not ready. Waiting for confirmation.'
|
|
15749
|
-
});
|
|
15750
|
-
};
|
|
15751
|
-
/**
|
|
15752
|
-
* Polls the attestation API until the relayer's mint transaction is confirmed.
|
|
15753
|
-
*
|
|
15754
|
-
* This function is used when `useForwarder` is enabled. Instead of the user
|
|
15755
|
-
* submitting the mint transaction, Circle's Orbit relayer handles it automatically.
|
|
15756
|
-
* This function polls until the relayer has submitted and confirmed the mint transaction.
|
|
15757
|
-
*
|
|
15758
|
-
* @remarks
|
|
15759
|
-
* - Uses a 20-minute timeout by default (600 retries × 2 seconds)
|
|
15760
|
-
* - Throws immediately if `forwardState` is 'FAILED'
|
|
15761
|
-
* - Waits for `forwardState` to be 'CONFIRMED' or 'COMPLETE' (COMPLETE implies CONFIRMED)
|
|
15762
|
-
* - Returns the attestation message with `forwardTxHash` populated
|
|
15763
|
-
*
|
|
15764
|
-
* @param sourceDomainId - The CCTP domain ID of the source chain
|
|
15765
|
-
* @param transactionHash - The transaction hash of the burn operation
|
|
15766
|
-
* @param isTestnet - Whether this is for a testnet chain (true) or mainnet (false)
|
|
15767
|
-
* @param config - Optional configuration overrides for polling behavior
|
|
15768
|
-
* @returns The attestation message with confirmed forwardTxHash
|
|
15769
|
-
* @throws {KitError} With code 'NETWORK_RELAYER_FORWARD_FAILED' if relayer failed
|
|
15770
|
-
* @throws {KitError} If timeout is reached while still pending
|
|
15771
|
-
*
|
|
15772
|
-
* @example
|
|
15773
|
-
* ```typescript
|
|
15774
|
-
* const attestation = await fetchRelayerMint(0, '0xabc...', false)
|
|
15775
|
-
* console.log('Relayer mint tx:', attestation.forwardTxHash)
|
|
15776
|
-
* ```
|
|
15777
|
-
*/ const fetchRelayerMint = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
|
|
15778
|
-
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
15779
|
-
const effectiveConfig = {
|
|
15780
|
-
...DEFAULT_CONFIG$2,
|
|
15781
|
-
...config
|
|
15782
|
-
};
|
|
15783
|
-
let response;
|
|
15784
|
-
try {
|
|
15785
|
-
response = await pollApiGet(url, isRelayerMintConfirmed, effectiveConfig);
|
|
15786
|
-
} catch (error) {
|
|
15787
|
-
// Enrich RELAYER_FORWARD_FAILED errors with the burn transaction hash
|
|
15788
|
-
if (error instanceof KitError && error.name === 'NETWORK_RELAYER_FORWARD_FAILED') {
|
|
15789
|
-
throw new KitError({
|
|
15790
|
-
...NetworkError.RELAYER_FORWARD_FAILED,
|
|
15791
|
-
recoverability: error.recoverability,
|
|
15792
|
-
message: error.message,
|
|
15793
|
-
cause: {
|
|
15794
|
-
...error.cause,
|
|
15795
|
-
trace: {
|
|
15796
|
-
...error.cause?.trace,
|
|
15797
|
-
burnTxHash: transactionHash
|
|
15798
|
-
}
|
|
15799
|
-
}
|
|
15800
|
-
});
|
|
15801
|
-
}
|
|
15802
|
-
throw error;
|
|
15803
|
-
}
|
|
15804
|
-
// Return the first message (which should have forwardTxHash)
|
|
15805
|
-
// Note: This check is needed for TypeScript type safety even though
|
|
15806
|
-
// isRelayerMintConfirmed validates messages[0] exists. The type guard
|
|
15807
|
-
// narrows the type at the call site, but TypeScript can't infer that
|
|
15808
|
-
// the array still has elements after pollApiGet returns.
|
|
15809
|
-
const message = response.messages[0];
|
|
15810
|
-
if (!message) {
|
|
15811
|
-
throw new KitError({
|
|
15812
|
-
...InputError.VALIDATION_FAILED,
|
|
15813
|
-
recoverability: 'FATAL',
|
|
15814
|
-
message: 'No attestation messages found in response after polling.'
|
|
15815
|
-
});
|
|
15816
|
-
}
|
|
15817
|
-
return message;
|
|
15818
|
-
};
|
|
15819
|
-
|
|
15820
15974
|
/**
|
|
15821
15975
|
* Executes the mint step for forwarding mode where Circle's relayer handles the mint.
|
|
15822
15976
|
*
|
|
@@ -15830,6 +15984,7 @@ const mockAttestationMessage = {
|
|
|
15830
15984
|
* immediately after the API confirms the mint.
|
|
15831
15985
|
*
|
|
15832
15986
|
* @param params - The bridge parameters containing source, destination, amount and config
|
|
15987
|
+
* @param provider - The CCTP v2 bridging provider
|
|
15833
15988
|
* @param context - The step context containing burnTxHash from the burn step
|
|
15834
15989
|
* @returns Promise resolving to the bridge step with transaction details
|
|
15835
15990
|
* @throws {KitError} If burnTxHash is not available in context
|
|
@@ -15838,10 +15993,13 @@ const mockAttestationMessage = {
|
|
|
15838
15993
|
*
|
|
15839
15994
|
* @example
|
|
15840
15995
|
* ```typescript
|
|
15841
|
-
* const step = await bridgeRelayerMint(
|
|
15996
|
+
* const step = await bridgeRelayerMint(
|
|
15997
|
+
* { params, provider },
|
|
15998
|
+
* { burnTxHash: '0x...' }
|
|
15999
|
+
* )
|
|
15842
16000
|
* console.log('Relayer mint tx:', step.txHash)
|
|
15843
16001
|
* ```
|
|
15844
|
-
*/ async function bridgeRelayerMint(params, context) {
|
|
16002
|
+
*/ async function bridgeRelayerMint({ params, provider }, context) {
|
|
15845
16003
|
const step = {
|
|
15846
16004
|
name: 'mint',
|
|
15847
16005
|
state: 'pending',
|
|
@@ -15857,7 +16015,7 @@ const mockAttestationMessage = {
|
|
|
15857
16015
|
}
|
|
15858
16016
|
try {
|
|
15859
16017
|
// Poll attestation API until relayer confirms the mint
|
|
15860
|
-
const attestation = await fetchRelayerMint(params.source
|
|
16018
|
+
const attestation = await provider.fetchRelayerMint(params.source, context.burnTxHash);
|
|
15861
16019
|
// Extract the relayer's mint transaction hash
|
|
15862
16020
|
const forwardTxHash = attestation.forwardTxHash;
|
|
15863
16021
|
if (!forwardTxHash) {
|
|
@@ -15951,7 +16109,7 @@ const mockAttestationMessage = {
|
|
|
15951
16109
|
name: 'mint',
|
|
15952
16110
|
// bridgeRelayerMint validates context.burnTxHash internally with a KitError
|
|
15953
16111
|
// bridgeMint requires attestationData which is validated here
|
|
15954
|
-
executor: useForwarder ? async (params,
|
|
16112
|
+
executor: useForwarder ? async (params, provider, context)=>{
|
|
15955
16113
|
if (!context) {
|
|
15956
16114
|
throw new KitError({
|
|
15957
16115
|
...InputError.VALIDATION_FAILED,
|
|
@@ -15959,7 +16117,10 @@ const mockAttestationMessage = {
|
|
|
15959
16117
|
message: 'Step context is required for relayer mint'
|
|
15960
16118
|
});
|
|
15961
16119
|
}
|
|
15962
|
-
return bridgeRelayerMint(
|
|
16120
|
+
return bridgeRelayerMint({
|
|
16121
|
+
params,
|
|
16122
|
+
provider
|
|
16123
|
+
}, context);
|
|
15963
16124
|
} : async (params, provider, context)=>{
|
|
15964
16125
|
if (!context?.attestationData) {
|
|
15965
16126
|
throw new KitError({
|
|
@@ -16290,16 +16451,16 @@ const mockAttestationMessage = {
|
|
|
16290
16451
|
return step;
|
|
16291
16452
|
}
|
|
16292
16453
|
|
|
16293
|
-
var version$
|
|
16294
|
-
var pkg$
|
|
16295
|
-
version: version$
|
|
16454
|
+
var version$4 = "1.9.0";
|
|
16455
|
+
var pkg$4 = {
|
|
16456
|
+
version: version$4};
|
|
16296
16457
|
|
|
16297
16458
|
/**
|
|
16298
16459
|
* Provider caller component for bridge operations.
|
|
16299
16460
|
*/ const BRIDGE_CALLER = {
|
|
16300
16461
|
type: 'provider',
|
|
16301
16462
|
name: 'CCTPV2BridgingProvider.bridge',
|
|
16302
|
-
version: pkg$
|
|
16463
|
+
version: pkg$4.version
|
|
16303
16464
|
};
|
|
16304
16465
|
/**
|
|
16305
16466
|
* Resolve invocation context for bridge operations.
|
|
@@ -16672,7 +16833,7 @@ var pkg$3 = {
|
|
|
16672
16833
|
*/ const RETRY_CALLER = {
|
|
16673
16834
|
type: 'provider',
|
|
16674
16835
|
name: 'CCTPV2BridgingProvider.retry',
|
|
16675
|
-
version: pkg$
|
|
16836
|
+
version: pkg$4.version
|
|
16676
16837
|
};
|
|
16677
16838
|
/**
|
|
16678
16839
|
* Resolve invocation context for retry operations.
|
|
@@ -16996,6 +17157,39 @@ var pkg$3 = {
|
|
|
16996
17157
|
}
|
|
16997
17158
|
}
|
|
16998
17159
|
|
|
17160
|
+
function isPlainObject(value) {
|
|
17161
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
17162
|
+
return false;
|
|
17163
|
+
}
|
|
17164
|
+
const prototype = Object.getPrototypeOf(value);
|
|
17165
|
+
return prototype === Object.prototype || prototype === null;
|
|
17166
|
+
}
|
|
17167
|
+
function assertHeadersConfig(headers, field) {
|
|
17168
|
+
if (headers === undefined) {
|
|
17169
|
+
return;
|
|
17170
|
+
}
|
|
17171
|
+
if (!isPlainObject(headers)) {
|
|
17172
|
+
throw createValidationFailedError$1(field, headers, `${field} must be a plain object with string header values when provided`);
|
|
17173
|
+
}
|
|
17174
|
+
for (const [name, value] of Object.entries(headers)){
|
|
17175
|
+
if (typeof value !== 'string') {
|
|
17176
|
+
throw createValidationFailedError$1(`${field}.${name}`, value, 'header values must be strings');
|
|
17177
|
+
}
|
|
17178
|
+
}
|
|
17179
|
+
}
|
|
17180
|
+
function assertCCTPV2Config(config) {
|
|
17181
|
+
if (!isPlainObject(config)) {
|
|
17182
|
+
throw createValidationFailedError$1('config', config, 'config must be a plain object when provided');
|
|
17183
|
+
}
|
|
17184
|
+
assertHeadersConfig(config['headers'], 'config.headers');
|
|
17185
|
+
const attestation = config['attestation'];
|
|
17186
|
+
if (attestation !== undefined) {
|
|
17187
|
+
if (!isPlainObject(attestation)) {
|
|
17188
|
+
throw createValidationFailedError$1('config.attestation', attestation, 'config.attestation must be a plain object when provided');
|
|
17189
|
+
}
|
|
17190
|
+
assertHeadersConfig(attestation['headers'], 'config.attestation.headers');
|
|
17191
|
+
}
|
|
17192
|
+
}
|
|
16999
17193
|
/**
|
|
17000
17194
|
* All chains that are supported by the CCTP v2 provider.
|
|
17001
17195
|
*
|
|
@@ -17064,9 +17258,38 @@ var pkg$3 = {
|
|
|
17064
17258
|
* @param config - Optional configuration overrides for the provider
|
|
17065
17259
|
*/ constructor(config = {}){
|
|
17066
17260
|
super();
|
|
17261
|
+
assertCCTPV2Config(config);
|
|
17067
17262
|
this.config = config;
|
|
17068
17263
|
}
|
|
17069
17264
|
/**
|
|
17265
|
+
* Resolves the effective polling configuration for an attestation request.
|
|
17266
|
+
*
|
|
17267
|
+
* Precedence (lowest to highest): provider `config.attestation`, then the
|
|
17268
|
+
* per-call `config`. Headers merge independently across
|
|
17269
|
+
* `config.attestation.headers`, the provider-level `config.headers`, and any
|
|
17270
|
+
* per-call `config.headers`, so a more specific header augments rather than
|
|
17271
|
+
* replaces the broader ones. The `headers` key is omitted entirely when no
|
|
17272
|
+
* headers are configured, leaving the attestation fetchers' defaults intact.
|
|
17273
|
+
*
|
|
17274
|
+
* @param config - Optional per-call polling configuration overrides
|
|
17275
|
+
* @returns The merged polling configuration passed to the attestation fetchers
|
|
17276
|
+
*/ resolveAttestationConfig(config) {
|
|
17277
|
+
const headers = {
|
|
17278
|
+
...this.config?.attestation?.headers,
|
|
17279
|
+
...this.config?.headers,
|
|
17280
|
+
...config?.headers
|
|
17281
|
+
};
|
|
17282
|
+
// Polling fields follow normal precedence; headers are merged separately
|
|
17283
|
+
// below so narrower config layers augment rather than replace broader ones.
|
|
17284
|
+
return {
|
|
17285
|
+
...this.config?.attestation,
|
|
17286
|
+
...config,
|
|
17287
|
+
...Object.keys(headers).length > 0 ? {
|
|
17288
|
+
headers
|
|
17289
|
+
} : {}
|
|
17290
|
+
};
|
|
17291
|
+
}
|
|
17292
|
+
/**
|
|
17070
17293
|
* Execute a cross-chain USDC bridge operation using the CCTP v2 protocol.
|
|
17071
17294
|
*
|
|
17072
17295
|
* This method orchestrates the complete CCTP v2 bridge flow including validation,
|
|
@@ -17492,11 +17715,7 @@ var pkg$3 = {
|
|
|
17492
17715
|
*/ async fetchAttestation(source, transactionHash, config) {
|
|
17493
17716
|
assertCCTPv2WalletContext(source);
|
|
17494
17717
|
try {
|
|
17495
|
-
|
|
17496
|
-
const effectiveConfig = {
|
|
17497
|
-
...this.config?.attestation,
|
|
17498
|
-
...config
|
|
17499
|
-
};
|
|
17718
|
+
const effectiveConfig = this.resolveAttestationConfig(config);
|
|
17500
17719
|
const response = await fetchAttestation(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
|
|
17501
17720
|
const message = response.messages[0];
|
|
17502
17721
|
if (!message) {
|
|
@@ -17513,6 +17732,49 @@ var pkg$3 = {
|
|
|
17513
17732
|
}
|
|
17514
17733
|
}
|
|
17515
17734
|
/**
|
|
17735
|
+
* Polls attestation data until Circle's relayer mint transaction is confirmed.
|
|
17736
|
+
*
|
|
17737
|
+
* This method is used by forwarded transfers. It polls the same Iris
|
|
17738
|
+
* attestation endpoint as {@link CCTPV2BridgingProvider.fetchAttestation},
|
|
17739
|
+
* but waits for a completed relayer forward state and returns the attestation
|
|
17740
|
+
* message containing `forwardTxHash`.
|
|
17741
|
+
*
|
|
17742
|
+
* @typeParam TFromAdapterCapabilities - The type representing the capabilities of the source adapter
|
|
17743
|
+
* @param source - The source wallet context containing the chain definition and wallet address
|
|
17744
|
+
* @param transactionHash - The transaction hash of the burn operation
|
|
17745
|
+
* @param config - Optional polling configuration overrides for timeout, retries, delay, and headers
|
|
17746
|
+
* @returns A promise that resolves to the attestation message with `forwardTxHash`
|
|
17747
|
+
* @throws KitError If the relayer forward fails, the response is invalid, or polling times out
|
|
17748
|
+
*
|
|
17749
|
+
* @example
|
|
17750
|
+
* ```typescript
|
|
17751
|
+
* import { CCTPV2BridgingProvider } from '@circle-fin/provider-cctp-v2'
|
|
17752
|
+
* import { Chains } from '@core/chains'
|
|
17753
|
+
*
|
|
17754
|
+
* const provider = new CCTPV2BridgingProvider({
|
|
17755
|
+
* headers: { 'X-Partner-UUID': '00000000-0000-0000-0000-000000000000' },
|
|
17756
|
+
* })
|
|
17757
|
+
*
|
|
17758
|
+
* const attestation = await provider.fetchRelayerMint(
|
|
17759
|
+
* {
|
|
17760
|
+
* adapter,
|
|
17761
|
+
* chain: Chains.EthereumSepolia,
|
|
17762
|
+
* address: '0x1234...',
|
|
17763
|
+
* },
|
|
17764
|
+
* '0xabc123...',
|
|
17765
|
+
* )
|
|
17766
|
+
*
|
|
17767
|
+
* console.log('Relayer mint tx:', attestation.forwardTxHash)
|
|
17768
|
+
* ```
|
|
17769
|
+
*/ async fetchRelayerMint(source, transactionHash, config) {
|
|
17770
|
+
assertCCTPv2WalletContext(source);
|
|
17771
|
+
if (typeof transactionHash !== 'string' || transactionHash.trim() === '') {
|
|
17772
|
+
throw createValidationFailedError$1('transactionHash', transactionHash, 'transactionHash must be a non-empty string');
|
|
17773
|
+
}
|
|
17774
|
+
const effectiveConfig = this.resolveAttestationConfig(config);
|
|
17775
|
+
return await fetchRelayerMint(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
|
|
17776
|
+
}
|
|
17777
|
+
/**
|
|
17516
17778
|
* Requests a fresh attestation for an expired attestation.
|
|
17517
17779
|
*
|
|
17518
17780
|
* This method is used when the original attestation has expired before the mint
|
|
@@ -17566,11 +17828,7 @@ var pkg$3 = {
|
|
|
17566
17828
|
throw new Error('Failed to re-attest: Invalid transaction hash');
|
|
17567
17829
|
}
|
|
17568
17830
|
try {
|
|
17569
|
-
|
|
17570
|
-
const effectiveConfig = {
|
|
17571
|
-
...this.config?.attestation,
|
|
17572
|
-
...config
|
|
17573
|
-
};
|
|
17831
|
+
const effectiveConfig = this.resolveAttestationConfig(config);
|
|
17574
17832
|
// Step 1: Get existing attestation data to extract nonce
|
|
17575
17833
|
const existingAttestation = await fetchAttestationWithoutStatusCheck(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
|
|
17576
17834
|
const nonce = existingAttestation.messages[0]?.eventNonce;
|
|
@@ -17855,8 +18113,13 @@ var pkg$3 = {
|
|
|
17855
18113
|
/**
|
|
17856
18114
|
* The default providers that will be used in addition to the providers provided
|
|
17857
18115
|
* to the BridgeKit constructor.
|
|
17858
|
-
|
|
17859
|
-
|
|
18116
|
+
*
|
|
18117
|
+
* @param config - Optional configuration forwarded to the default providers
|
|
18118
|
+
* @returns The default bridging providers
|
|
18119
|
+
*/ const getDefaultProviders$3 = (config = {})=>[
|
|
18120
|
+
new CCTPV2BridgingProvider(config.headers ? {
|
|
18121
|
+
headers: config.headers
|
|
18122
|
+
} : {})
|
|
17860
18123
|
];
|
|
17861
18124
|
|
|
17862
18125
|
/**
|
|
@@ -17967,7 +18230,7 @@ var pkg$3 = {
|
|
|
17967
18230
|
]
|
|
17968
18231
|
];
|
|
17969
18232
|
|
|
17970
|
-
/** SDK name used in telemetry payloads. */ const SDK_NAME$2 = resolveKitSdkName(pkg$
|
|
18233
|
+
/** SDK name used in telemetry payloads. */ const SDK_NAME$2 = resolveKitSdkName(pkg$5.name);
|
|
17971
18234
|
/**
|
|
17972
18235
|
* Pick the most-relevant `txHash` to attach to an error telemetry payload.
|
|
17973
18236
|
*
|
|
@@ -18008,7 +18271,7 @@ var pkg$3 = {
|
|
|
18008
18271
|
*/ const BRIDGE_KIT_CALLER = {
|
|
18009
18272
|
type: 'kit',
|
|
18010
18273
|
name: 'BridgeKit',
|
|
18011
|
-
version: pkg$
|
|
18274
|
+
version: pkg$5.version
|
|
18012
18275
|
};
|
|
18013
18276
|
/**
|
|
18014
18277
|
* Merge BridgeKit's caller into the invocation metadata for retry operations.
|
|
@@ -18099,7 +18362,9 @@ var pkg$3 = {
|
|
|
18099
18362
|
* ```
|
|
18100
18363
|
*/ constructor(config = {}){
|
|
18101
18364
|
// Handle provider configuration
|
|
18102
|
-
const defaultProviders = getDefaultProviders$3(
|
|
18365
|
+
const defaultProviders = getDefaultProviders$3(config.headers ? {
|
|
18366
|
+
headers: config.headers
|
|
18367
|
+
} : {});
|
|
18103
18368
|
this.providers = [
|
|
18104
18369
|
...defaultProviders,
|
|
18105
18370
|
...config.providers ?? []
|
|
@@ -18108,7 +18373,7 @@ var pkg$3 = {
|
|
|
18108
18373
|
this.disableErrorReporting = config.disableErrorReporting === true;
|
|
18109
18374
|
this.telemetryConfig = {
|
|
18110
18375
|
sdkName: SDK_NAME$2,
|
|
18111
|
-
sdkVersion: pkg$
|
|
18376
|
+
sdkVersion: pkg$5.version,
|
|
18112
18377
|
disabled: this.disableErrorReporting
|
|
18113
18378
|
};
|
|
18114
18379
|
for (const provider of this.providers){
|
|
@@ -18630,7 +18895,7 @@ var pkg$3 = {
|
|
|
18630
18895
|
}
|
|
18631
18896
|
|
|
18632
18897
|
// Auto-register this kit for user agent tracking
|
|
18633
|
-
registerKit(`${pkg$
|
|
18898
|
+
registerKit(`${pkg$5.name}/${pkg$5.version}`);
|
|
18634
18899
|
|
|
18635
18900
|
/**
|
|
18636
18901
|
* Create a BridgeKit instance with optional developer fee configuration.
|
|
@@ -18679,6 +18944,9 @@ registerKit(`${pkg$4.name}/${pkg$4.version}`);
|
|
|
18679
18944
|
const kit = new BridgeKit({
|
|
18680
18945
|
...context.disableErrorReporting != null && {
|
|
18681
18946
|
disableErrorReporting: context.disableErrorReporting
|
|
18947
|
+
},
|
|
18948
|
+
...context.headers != null && {
|
|
18949
|
+
headers: context.headers
|
|
18682
18950
|
}
|
|
18683
18951
|
});
|
|
18684
18952
|
if (hasBoth) {
|
|
@@ -18696,11 +18964,11 @@ registerKit(`${pkg$4.name}/${pkg$4.version}`);
|
|
|
18696
18964
|
return kit;
|
|
18697
18965
|
};
|
|
18698
18966
|
|
|
18699
|
-
var name$
|
|
18700
|
-
var version$
|
|
18701
|
-
var pkg$
|
|
18702
|
-
name: name$
|
|
18703
|
-
version: version$
|
|
18967
|
+
var name$3 = "@circle-fin/swap-kit";
|
|
18968
|
+
var version$3 = "1.3.2";
|
|
18969
|
+
var pkg$3 = {
|
|
18970
|
+
name: name$3,
|
|
18971
|
+
version: version$3};
|
|
18704
18972
|
|
|
18705
18973
|
const chainIdentifierField = z.custom((value)=>chainIdentifierSchema.safeParse(value).success, {
|
|
18706
18974
|
message: 'chain must be a valid chain identifier'
|
|
@@ -23632,7 +23900,7 @@ function writeBytes32(buffer, hex, offset) {
|
|
|
23632
23900
|
}
|
|
23633
23901
|
}
|
|
23634
23902
|
|
|
23635
|
-
const TOKEN_REGISTRY$
|
|
23903
|
+
const TOKEN_REGISTRY$4 = createTokenRegistry();
|
|
23636
23904
|
/**
|
|
23637
23905
|
* Resolve token alias to contract address for the given chain.
|
|
23638
23906
|
*
|
|
@@ -23721,7 +23989,7 @@ const TOKEN_REGISTRY$3 = createTokenRegistry();
|
|
|
23721
23989
|
}
|
|
23722
23990
|
});
|
|
23723
23991
|
}
|
|
23724
|
-
const tokenDefinition = TOKEN_REGISTRY$
|
|
23992
|
+
const tokenDefinition = TOKEN_REGISTRY$4.get(upperToken);
|
|
23725
23993
|
if (tokenDefinition !== undefined) {
|
|
23726
23994
|
const resolvedAddress = tokenDefinition.locators[chain.chain];
|
|
23727
23995
|
if (!resolvedAddress) {
|
|
@@ -23738,7 +24006,7 @@ const TOKEN_REGISTRY$3 = createTokenRegistry();
|
|
|
23738
24006
|
}
|
|
23739
24007
|
});
|
|
23740
24008
|
}
|
|
23741
|
-
return TOKEN_REGISTRY$
|
|
24009
|
+
return TOKEN_REGISTRY$4.resolve(upperToken, chain.chain).locator;
|
|
23742
24010
|
}
|
|
23743
24011
|
// Not an alias - return as-is (assumed to be address)
|
|
23744
24012
|
return token;
|
|
@@ -23835,11 +24103,11 @@ function resolveFeePayoutChain(tokenIn, tokenOut, sourceChain, destinationChain)
|
|
|
23835
24103
|
if (destinationChain.chain !== sourceChain.chain) {
|
|
23836
24104
|
return sourceChain;
|
|
23837
24105
|
}
|
|
23838
|
-
const inputIsOk = isOkToken(tokenIn, sourceChain, TOKEN_REGISTRY$
|
|
24106
|
+
const inputIsOk = isOkToken(tokenIn, sourceChain, TOKEN_REGISTRY$4, OK_TOKEN_SYMBOLS$1);
|
|
23839
24107
|
if (inputIsOk) {
|
|
23840
24108
|
return sourceChain;
|
|
23841
24109
|
}
|
|
23842
|
-
const outputIsOk = isOkToken(tokenOut, destinationChain, TOKEN_REGISTRY$
|
|
24110
|
+
const outputIsOk = isOkToken(tokenOut, destinationChain, TOKEN_REGISTRY$4, OK_TOKEN_SYMBOLS$1);
|
|
23843
24111
|
return outputIsOk ? destinationChain : sourceChain;
|
|
23844
24112
|
}
|
|
23845
24113
|
/**
|
|
@@ -25130,7 +25398,7 @@ function resolveFeePayoutChain(tokenIn, tokenOut, sourceChain, destinationChain)
|
|
|
25130
25398
|
}
|
|
25131
25399
|
}
|
|
25132
25400
|
|
|
25133
|
-
const TOKEN_REGISTRY$
|
|
25401
|
+
const TOKEN_REGISTRY$3 = createTokenRegistry();
|
|
25134
25402
|
/**
|
|
25135
25403
|
* Resolve a token identifier to its canonical symbol for the Stablecoin Service Swap Provider.
|
|
25136
25404
|
*
|
|
@@ -25174,7 +25442,7 @@ const TOKEN_REGISTRY$2 = createTokenRegistry();
|
|
|
25174
25442
|
return NATIVE_TOKEN;
|
|
25175
25443
|
}
|
|
25176
25444
|
// For known registry aliases, return canonical symbol directly.
|
|
25177
|
-
if (TOKEN_REGISTRY$
|
|
25445
|
+
if (TOKEN_REGISTRY$3.get(upperToken) !== undefined) {
|
|
25178
25446
|
return upperToken;
|
|
25179
25447
|
}
|
|
25180
25448
|
// For EVM chains, normalize addresses to lowercase for case-insensitive comparison.
|
|
@@ -25184,7 +25452,7 @@ const TOKEN_REGISTRY$2 = createTokenRegistry();
|
|
|
25184
25452
|
// Native address is given
|
|
25185
25453
|
if (isNativeEvmAddress(token) || isNativeSolanaAddress(token)) return NATIVE_TOKEN;
|
|
25186
25454
|
try {
|
|
25187
|
-
const { symbol } = TOKEN_REGISTRY$
|
|
25455
|
+
const { symbol } = TOKEN_REGISTRY$3.resolveByAddress(normalizedToken, chain.chain);
|
|
25188
25456
|
if (symbol === undefined) {
|
|
25189
25457
|
return null;
|
|
25190
25458
|
}
|
|
@@ -25315,7 +25583,7 @@ const TOKEN_REGISTRY$2 = createTokenRegistry();
|
|
|
25315
25583
|
return `Insufficient ${displaySymbol} balance for swap operation.\n\n` + `Wallet: ${walletAddress}\n` + `Current balance: ${currentDisplay}\n` + `Required: ${requiredDisplay}\n` + `Shortfall: ${shortfallDisplay}\n\n` + `This swap requires ${requiredSummary} to complete the transaction.\n\n` + `Action: Add at least ${actionAmount} to your wallet to complete this swap.`;
|
|
25316
25584
|
}
|
|
25317
25585
|
|
|
25318
|
-
const TOKEN_REGISTRY$
|
|
25586
|
+
const TOKEN_REGISTRY$2 = createTokenRegistry();
|
|
25319
25587
|
/**
|
|
25320
25588
|
* Format a raw base-unit amount into a human-readable decimal string.
|
|
25321
25589
|
*
|
|
@@ -25375,12 +25643,12 @@ const TOKEN_REGISTRY$1 = createTokenRegistry();
|
|
|
25375
25643
|
value,
|
|
25376
25644
|
token: resolvedSymbol,
|
|
25377
25645
|
chain,
|
|
25378
|
-
tokens: TOKEN_REGISTRY$
|
|
25646
|
+
tokens: TOKEN_REGISTRY$2
|
|
25379
25647
|
}),
|
|
25380
25648
|
token: resolvedSymbol
|
|
25381
25649
|
};
|
|
25382
25650
|
}
|
|
25383
|
-
const tokenDef = TOKEN_REGISTRY$
|
|
25651
|
+
const tokenDef = TOKEN_REGISTRY$2.get(resolvedSymbol);
|
|
25384
25652
|
if (tokenDef) {
|
|
25385
25653
|
const decimals = resolveTokenDecimals$1(tokenDef, chain.chain);
|
|
25386
25654
|
return {
|
|
@@ -25411,7 +25679,7 @@ const TOKEN_REGISTRY$1 = createTokenRegistry();
|
|
|
25411
25679
|
* i.e. 1 + 0.2 + 0.1 = 1.3. Use the greater of (local estimate × this multiplier)
|
|
25412
25680
|
* or the proxy's gasLimit.
|
|
25413
25681
|
*/ const GAS_SAFETY_MULTIPLIER = 1.3;
|
|
25414
|
-
const TOKEN_REGISTRY = createTokenRegistry();
|
|
25682
|
+
const TOKEN_REGISTRY$1 = createTokenRegistry();
|
|
25415
25683
|
/**
|
|
25416
25684
|
* Enhances a KitError with transaction details (txHash and explorerUrl).
|
|
25417
25685
|
*
|
|
@@ -25487,6 +25755,19 @@ const TOKEN_REGISTRY = createTokenRegistry();
|
|
|
25487
25755
|
*
|
|
25488
25756
|
* @internal
|
|
25489
25757
|
*/ const SUPPORTED_CHAINS$1 = getSwapSupportedChains(Chains);
|
|
25758
|
+
/**
|
|
25759
|
+
* Inter-poll delays (ms) for the same-chain `amountOut` enrichment loop.
|
|
25760
|
+
*
|
|
25761
|
+
* A same-chain swap is already terminal `DONE` once its source transaction
|
|
25762
|
+
* confirms; this short, escalating budget (~5s total across up to four polls)
|
|
25763
|
+
* gives the service a brief window to resolve the output amount before
|
|
25764
|
+
* `swap()` returns. The loop exits the instant `amountOut` is available.
|
|
25765
|
+
*/ const SAME_CHAIN_AMOUNT_OUT_POLL_DELAYS_MS = [
|
|
25766
|
+
1_000,
|
|
25767
|
+
2_000,
|
|
25768
|
+
2_000
|
|
25769
|
+
];
|
|
25770
|
+
const sleep$3 = async (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
|
|
25490
25771
|
function buildSwapProgress(statusResult) {
|
|
25491
25772
|
return {
|
|
25492
25773
|
status: statusResult.status,
|
|
@@ -25506,29 +25787,46 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
25506
25787
|
}
|
|
25507
25788
|
};
|
|
25508
25789
|
}
|
|
25509
|
-
|
|
25510
|
-
|
|
25511
|
-
|
|
25512
|
-
|
|
25513
|
-
|
|
25514
|
-
|
|
25515
|
-
|
|
25516
|
-
|
|
25790
|
+
// A same-chain swap completes atomically in the source transaction, which
|
|
25791
|
+
// has already been confirmed (and receipt-checked for revert) by the time we
|
|
25792
|
+
// get here. Its terminal status is therefore `DONE`. We briefly poll the
|
|
25793
|
+
// status endpoint to enrich `amountOut`, which the service usually resolves
|
|
25794
|
+
// within a few seconds. A non-DONE/erroneous peek never downgrades the
|
|
25795
|
+
// status — once the short budget is exhausted we return `DONE` without
|
|
25796
|
+
// `amountOut`, and the caller can fetch it later via `getSwapStatus`.
|
|
25797
|
+
for(let attempt = 0;; attempt += 1){
|
|
25798
|
+
try {
|
|
25799
|
+
const statusResult = await getSwapStatus$2({
|
|
25800
|
+
txHash,
|
|
25801
|
+
chain: chain.chain,
|
|
25802
|
+
apiKey
|
|
25803
|
+
});
|
|
25804
|
+
if (statusResult.status === 'DONE' && statusResult.amountOut !== undefined) {
|
|
25805
|
+
return {
|
|
25806
|
+
progress: buildSwapProgress(statusResult),
|
|
25807
|
+
amountOut: statusResult.amountOut
|
|
25808
|
+
};
|
|
25809
|
+
}
|
|
25810
|
+
} catch {
|
|
25811
|
+
// Reachability/parse failure during enrichment — non-fatal; the swap
|
|
25812
|
+
// already succeeded on-chain. `getSwapStatus` already retried transient
|
|
25813
|
+
// network errors internally, so further polling is unlikely to help.
|
|
25517
25814
|
return {
|
|
25518
|
-
progress
|
|
25519
|
-
|
|
25815
|
+
progress: {
|
|
25816
|
+
status: 'DONE'
|
|
25817
|
+
}
|
|
25520
25818
|
};
|
|
25521
25819
|
}
|
|
25522
|
-
|
|
25523
|
-
|
|
25524
|
-
|
|
25525
|
-
|
|
25526
|
-
|
|
25527
|
-
|
|
25528
|
-
|
|
25529
|
-
|
|
25530
|
-
|
|
25531
|
-
|
|
25820
|
+
const delayMs = SAME_CHAIN_AMOUNT_OUT_POLL_DELAYS_MS[attempt];
|
|
25821
|
+
if (delayMs === undefined) {
|
|
25822
|
+
// Budget exhausted — terminal `DONE`, `amountOut` not yet available.
|
|
25823
|
+
return {
|
|
25824
|
+
progress: {
|
|
25825
|
+
status: 'DONE'
|
|
25826
|
+
}
|
|
25827
|
+
};
|
|
25828
|
+
}
|
|
25829
|
+
await sleep$3(delayMs);
|
|
25532
25830
|
}
|
|
25533
25831
|
}
|
|
25534
25832
|
/**
|
|
@@ -25809,7 +26107,7 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
25809
26107
|
return true;
|
|
25810
26108
|
}
|
|
25811
26109
|
// At least one token must be supported for fee collection
|
|
25812
|
-
return getSwapOkTokenStatus(tokenInAddress, tokenOutAddress, chain, TOKEN_REGISTRY, OK_TOKEN_SYMBOLS$1).hasOkToken;
|
|
26110
|
+
return getSwapOkTokenStatus(tokenInAddress, tokenOutAddress, chain, TOKEN_REGISTRY$1, OK_TOKEN_SYMBOLS$1).hasOkToken;
|
|
25813
26111
|
}
|
|
25814
26112
|
/**
|
|
25815
26113
|
* Estimate swap costs and output amount by delegating to the Stablecoin Service quote API.
|
|
@@ -26406,14 +26704,14 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
26406
26704
|
}
|
|
26407
26705
|
// Build swap fees with adapter-backed formatting for unregistered tokens
|
|
26408
26706
|
const swapResultFees = serviceResponse.fees ? await this.buildFormattedFees(serviceResponse.fees, chain, destinationChain, adapter, config?.customFee?.recipientAddress) : undefined;
|
|
26409
|
-
// Best-effort enrichment:
|
|
26410
|
-
//
|
|
26411
|
-
//
|
|
26412
|
-
//
|
|
26413
|
-
// destination mint takes minutes (CCTPv2 attestation),
|
|
26414
|
-
//
|
|
26415
|
-
//
|
|
26416
|
-
//
|
|
26707
|
+
// Best-effort enrichment: briefly poll for amountOut on same-chain swaps.
|
|
26708
|
+
// They complete atomically in the (already-confirmed) source tx, so the
|
|
26709
|
+
// result is terminal `DONE` regardless; the short poll only adds amountOut
|
|
26710
|
+
// once the service has resolved it. Cross-chain swaps are skipped because
|
|
26711
|
+
// the destination mint takes minutes (CCTPv2 attestation), so polling would
|
|
26712
|
+
// always return PENDING/WAIT_DESTINATION_TRANSACTION while burning the
|
|
26713
|
+
// budget. Callers should use `kit.getSwapStatus()` to poll cross-chain
|
|
26714
|
+
// swaps to DONE.
|
|
26417
26715
|
const statusSnapshot = await fetchSameChainStatusSnapshot({
|
|
26418
26716
|
isCrossChainSwap,
|
|
26419
26717
|
txHash,
|
|
@@ -30040,7 +30338,7 @@ function resolveTokenEntry(entry, index, chain, chainDef, context) {
|
|
|
30040
30338
|
ESTIMATE: 'swap_estimate'
|
|
30041
30339
|
};
|
|
30042
30340
|
|
|
30043
|
-
/** SDK name used in telemetry payloads. */ const SDK_NAME$1 = resolveKitSdkName(pkg$
|
|
30341
|
+
/** SDK name used in telemetry payloads. */ const SDK_NAME$1 = resolveKitSdkName(pkg$3.name);
|
|
30044
30342
|
/**
|
|
30045
30343
|
* A high-level class-based interface for same-chain and cross-chain token swap operations.
|
|
30046
30344
|
*
|
|
@@ -30163,7 +30461,7 @@ function resolveTokenEntry(entry, index, chain, chainDef, context) {
|
|
|
30163
30461
|
this.disableErrorReporting = config.disableErrorReporting === true;
|
|
30164
30462
|
this.telemetryConfig = {
|
|
30165
30463
|
sdkName: SDK_NAME$1,
|
|
30166
|
-
sdkVersion: pkg$
|
|
30464
|
+
sdkVersion: pkg$3.version,
|
|
30167
30465
|
disabled: this.disableErrorReporting
|
|
30168
30466
|
};
|
|
30169
30467
|
}
|
|
@@ -30619,7 +30917,7 @@ function resolveTokenEntry(entry, index, chain, chainDef, context) {
|
|
|
30619
30917
|
}
|
|
30620
30918
|
|
|
30621
30919
|
// Auto-register this kit for user agent tracking
|
|
30622
|
-
registerKit(`${pkg$
|
|
30920
|
+
registerKit(`${pkg$3.name}/${pkg$3.version}`);
|
|
30623
30921
|
|
|
30624
30922
|
/**
|
|
30625
30923
|
* Create a SwapKit instance with optional developer fee configuration.
|
|
@@ -30714,12 +31012,15 @@ registerKit(`${pkg$2.name}/${pkg$2.version}`);
|
|
|
30714
31012
|
return kit;
|
|
30715
31013
|
};
|
|
30716
31014
|
|
|
30717
|
-
var name$
|
|
30718
|
-
var version$
|
|
30719
|
-
var pkg$
|
|
30720
|
-
name: name$
|
|
30721
|
-
version: version$
|
|
31015
|
+
var name$2 = "@circle-fin/earn-kit";
|
|
31016
|
+
var version$2 = "1.2.2";
|
|
31017
|
+
var pkg$2 = {
|
|
31018
|
+
name: name$2,
|
|
31019
|
+
version: version$2};
|
|
30722
31020
|
|
|
31021
|
+
const EARN_BRIDGE_ERC3009_TOKEN_SYMBOLS = [
|
|
31022
|
+
'USDC'
|
|
31023
|
+
];
|
|
30723
31024
|
/**
|
|
30724
31025
|
* Default base URL for the Earn Service API.
|
|
30725
31026
|
*
|
|
@@ -30764,20 +31065,7 @@ var pkg$1 = {
|
|
|
30764
31065
|
*/ const EARN_BRIDGE_DESTINATION_CHAIN_TO_API = {
|
|
30765
31066
|
[Blockchain.Arc_Testnet]: CHAIN_TO_API[Blockchain.Arc_Testnet]
|
|
30766
31067
|
};
|
|
30767
|
-
|
|
30768
|
-
* Expected EIP-712 domain of the ERC-3009 authorization token per source
|
|
30769
|
-
* chain.
|
|
30770
|
-
*
|
|
30771
|
-
* Cross-chain Earn deposits are USDC-only, but USDC deployments differ on the
|
|
30772
|
-
* EIP-712 domain name: newer deployments use `USDC` while older ones (for
|
|
30773
|
-
* example Arbitrum Sepolia) kept `USD Coin` from the FiatToken V2 upgrade.
|
|
30774
|
-
* These values mirror the bridge service's per-chain domain table and the
|
|
30775
|
-
* on-chain `DOMAIN_SEPARATOR` inputs. Asserted before signing so a tampered
|
|
30776
|
-
* prepare response cannot point the signature at another token. The
|
|
30777
|
-
* `satisfies` check forces a domain entry whenever a source chain is added.
|
|
30778
|
-
*
|
|
30779
|
-
* @internal
|
|
30780
|
-
*/ const EARN_BRIDGE_ERC3009_DOMAINS = {
|
|
31068
|
+
const EARN_BRIDGE_USDC_ERC3009_DOMAINS = {
|
|
30781
31069
|
[Blockchain.Arbitrum_Sepolia]: {
|
|
30782
31070
|
name: 'USD Coin',
|
|
30783
31071
|
version: '2'
|
|
@@ -30791,6 +31079,21 @@ var pkg$1 = {
|
|
|
30791
31079
|
version: '2'
|
|
30792
31080
|
}
|
|
30793
31081
|
};
|
|
31082
|
+
/**
|
|
31083
|
+
* Expected EIP-712 domain of the ERC-3009 authorization token per
|
|
31084
|
+
* cross-chain-supported source token and source chain.
|
|
31085
|
+
*
|
|
31086
|
+
* The bridge service prepares an ERC-3009 payload for the deposited token. We
|
|
31087
|
+
* keep this table as an explicit SDK allowlist because token deployments can
|
|
31088
|
+
* differ by source chain and by domain name/version. These values mirror the
|
|
31089
|
+
* bridge service's per-chain domain table and the on-chain DOMAIN_SEPARATOR
|
|
31090
|
+
* inputs. Asserted before signing so a tampered prepare response cannot point
|
|
31091
|
+
* the signature at another token or unexpected token domain.
|
|
31092
|
+
*
|
|
31093
|
+
* @internal
|
|
31094
|
+
*/ const EARN_BRIDGE_ERC3009_DOMAINS = {
|
|
31095
|
+
USDC: EARN_BRIDGE_USDC_ERC3009_DOMAINS
|
|
31096
|
+
};
|
|
30794
31097
|
/**
|
|
30795
31098
|
* Maximum tolerated remaining lifetime of a prepared ERC-3009 authorization.
|
|
30796
31099
|
*
|
|
@@ -31250,6 +31553,16 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
31250
31553
|
return approvalTxHash;
|
|
31251
31554
|
}
|
|
31252
31555
|
|
|
31556
|
+
/** @internal */ function isSameAddress(actual, expected) {
|
|
31557
|
+
return actual.toLowerCase() === expected.toLowerCase();
|
|
31558
|
+
}
|
|
31559
|
+
/** @internal */ function assertDecimalPlaces(field, value, tokenDecimals, reason) {
|
|
31560
|
+
const decimals = value.split('.')[1]?.length ?? 0;
|
|
31561
|
+
if (decimals > tokenDecimals) {
|
|
31562
|
+
throw createValidationFailedError$1(field, value, reason);
|
|
31563
|
+
}
|
|
31564
|
+
}
|
|
31565
|
+
|
|
31253
31566
|
/**
|
|
31254
31567
|
* Build the `tokenInputs` array forwarded to the adapter contract's
|
|
31255
31568
|
* `execute(executeParams, tokenInputs, signature)` call.
|
|
@@ -31273,15 +31586,17 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
31273
31586
|
*
|
|
31274
31587
|
* @example
|
|
31275
31588
|
* ```typescript
|
|
31276
|
-
* const
|
|
31277
|
-
* 'chain.usdcAddress',
|
|
31278
|
-
* chain.usdcAddress,
|
|
31279
|
-
* )
|
|
31280
|
-
*
|
|
31281
|
-
* const tokenInputs = buildEarnTokenInputs(
|
|
31589
|
+
* const approvalToken = resolveEarnApprovalToken(
|
|
31282
31590
|
* executionPayload.executionParams,
|
|
31283
|
-
* approvedToken,
|
|
31284
31591
|
* )
|
|
31592
|
+
*
|
|
31593
|
+
* const tokenInputs =
|
|
31594
|
+
* approvalToken === undefined
|
|
31595
|
+
* ? []
|
|
31596
|
+
* : buildEarnTokenInputs(
|
|
31597
|
+
* executionPayload.executionParams,
|
|
31598
|
+
* approvalToken,
|
|
31599
|
+
* )
|
|
31285
31600
|
* ```
|
|
31286
31601
|
*
|
|
31287
31602
|
* @internal
|
|
@@ -31293,7 +31608,7 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
31293
31608
|
return;
|
|
31294
31609
|
}
|
|
31295
31610
|
const { tokenIn } = instruction;
|
|
31296
|
-
if (tokenIn
|
|
31611
|
+
if (!isSameAddress(tokenIn, approvedToken)) {
|
|
31297
31612
|
throw createValidationFailedError$1(`executionParams.instructions[${index.toString()}].tokenIn`, tokenIn, 'tokenIn must match the token approved for adapter spending');
|
|
31298
31613
|
}
|
|
31299
31614
|
tokenInputs.push({
|
|
@@ -31305,6 +31620,33 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
31305
31620
|
});
|
|
31306
31621
|
return tokenInputs;
|
|
31307
31622
|
}
|
|
31623
|
+
/**
|
|
31624
|
+
* Resolve the single token that needs adapter allowance from signed Earn
|
|
31625
|
+
* instructions.
|
|
31626
|
+
*
|
|
31627
|
+
* @param executionParams - Service-signed `ExecutionParams` forwarded to the adapter.
|
|
31628
|
+
* @returns The token requested by positive approval instructions, or `undefined`.
|
|
31629
|
+
* @throws {@link KitError} If positive approval instructions reference multiple tokens.
|
|
31630
|
+
*
|
|
31631
|
+
* @internal
|
|
31632
|
+
*/ function resolveEarnApprovalToken(executionParams) {
|
|
31633
|
+
let approvedToken;
|
|
31634
|
+
executionParams.instructions.forEach((instruction, index)=>{
|
|
31635
|
+
const amount = BigInt(instruction.amountToApprove);
|
|
31636
|
+
if (amount <= 0n) {
|
|
31637
|
+
return;
|
|
31638
|
+
}
|
|
31639
|
+
const { tokenIn } = instruction;
|
|
31640
|
+
if (approvedToken === undefined) {
|
|
31641
|
+
approvedToken = tokenIn;
|
|
31642
|
+
return;
|
|
31643
|
+
}
|
|
31644
|
+
if (!isSameAddress(tokenIn, approvedToken)) {
|
|
31645
|
+
throw createValidationFailedError$1(`executionParams.instructions[${index.toString()}].tokenIn`, tokenIn, 'tokenIn must match the token approved for adapter spending');
|
|
31646
|
+
}
|
|
31647
|
+
});
|
|
31648
|
+
return approvedToken;
|
|
31649
|
+
}
|
|
31308
31650
|
|
|
31309
31651
|
/**
|
|
31310
31652
|
* Prepare an earn adapter action, execute it, wait for confirmation, and
|
|
@@ -31796,6 +32138,7 @@ const hexSignatureSchema = evmSignatureSchema;
|
|
|
31796
32138
|
const hexAddressSchema = evmAddressSchema;
|
|
31797
32139
|
// '0x' prefix + 32 bytes * 2 hex chars.
|
|
31798
32140
|
const BYTES32_HEX_LENGTH = 66;
|
|
32141
|
+
const bridgeFeeTokenSchema = hexAddressSchema;
|
|
31799
32142
|
/**
|
|
31800
32143
|
* Zod schema for a non-negative uint256-like value.
|
|
31801
32144
|
*
|
|
@@ -32082,14 +32425,12 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
32082
32425
|
*
|
|
32083
32426
|
* The bridge prepare path returns one item per collected source fee (e.g.
|
|
32084
32427
|
* `PRE_FINALITY` for the fast-burn fee and `FORWARD` for the destination
|
|
32085
|
-
* forward fee), all denominated in the shared `feeToken`.
|
|
32086
|
-
*
|
|
32087
|
-
* guard parses these to recompute the expected value. Each item's `amount` is
|
|
32088
|
-
* a base-unit decimal string in `feeToken` units.
|
|
32428
|
+
* forward fee), all denominated in the shared `feeToken`. Each item's `amount`
|
|
32429
|
+
* is a base-unit decimal string in `feeToken` units.
|
|
32089
32430
|
*
|
|
32090
32431
|
* @internal
|
|
32091
32432
|
*/ const bridgeFeeQuoteSchema = z.object({
|
|
32092
|
-
feeToken:
|
|
32433
|
+
feeToken: bridgeFeeTokenSchema,
|
|
32093
32434
|
items: z.array(z.object({
|
|
32094
32435
|
type: z.string(),
|
|
32095
32436
|
amount: uint256LikeSchema
|
|
@@ -32105,6 +32446,12 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
32105
32446
|
erc3009TypedData: erc3009TypedDataSchema,
|
|
32106
32447
|
feeQuote: bridgeFeeQuoteSchema
|
|
32107
32448
|
}).passthrough();
|
|
32449
|
+
const bridgeDepositPrepareReviewSchema = z.object({
|
|
32450
|
+
sourceChain: z.string(),
|
|
32451
|
+
destinationChain: z.string(),
|
|
32452
|
+
amount: amountJsonSchema,
|
|
32453
|
+
vaultAddress: hexAddressSchema
|
|
32454
|
+
}).passthrough();
|
|
32108
32455
|
/**
|
|
32109
32456
|
* Zod schema for the bridge deposit prepare payload.
|
|
32110
32457
|
*
|
|
@@ -32116,7 +32463,7 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
32116
32463
|
execId: bridgeDepositExecIdSchema,
|
|
32117
32464
|
erc3009TypedData: bridgeDepositPreparedBundleSchema,
|
|
32118
32465
|
expiresAt: z.string().datetime(),
|
|
32119
|
-
review:
|
|
32466
|
+
review: bridgeDepositPrepareReviewSchema
|
|
32120
32467
|
});
|
|
32121
32468
|
/**
|
|
32122
32469
|
* Zod schema for the `POST /v1/earnKit/bridge/deposit/prepare` API response.
|
|
@@ -32241,10 +32588,11 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
32241
32588
|
* Zod schema for a fee entry in an EarnKit API response.
|
|
32242
32589
|
*
|
|
32243
32590
|
* Shared across deposit and withdrawal responses (and reusable for real
|
|
32244
|
-
* charged fees, not just quote estimates). `type` identifies the fee category
|
|
32245
|
-
*
|
|
32246
|
-
* (e.g. `'FORWARD'`, `'PRE_FINALITY'`).
|
|
32247
|
-
* `'
|
|
32591
|
+
* charged fees, not just quote estimates). `type` identifies the fee category.
|
|
32592
|
+
* For cross-chain deposit quotes this is the kits-proxy fee-quote item type
|
|
32593
|
+
* (e.g. `'FORWARD'`, `'PRE_FINALITY'`). For withdrawal quotes, Circle fees use
|
|
32594
|
+
* `type: 'circle'`. `status` qualifies the fee (e.g. `'estimated'` for a
|
|
32595
|
+
* pre-sign cross-chain fee). Both are omitted on plain fees.
|
|
32248
32596
|
*
|
|
32249
32597
|
* @internal
|
|
32250
32598
|
*/ const feeSchema = z.object({
|
|
@@ -32516,6 +32864,60 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
32516
32864
|
return claimRewardsQuoteResponseSchema.safeParse(value).success;
|
|
32517
32865
|
}
|
|
32518
32866
|
|
|
32867
|
+
var name$1 = "@circle-fin/provider-earn-service";
|
|
32868
|
+
var version$1 = "1.2.2";
|
|
32869
|
+
var pkg$1 = {
|
|
32870
|
+
name: name$1,
|
|
32871
|
+
version: version$1};
|
|
32872
|
+
|
|
32873
|
+
/**
|
|
32874
|
+
* HTTP header name used to report the EarnKit SDK version to the backend.
|
|
32875
|
+
*
|
|
32876
|
+
* @internal
|
|
32877
|
+
*/ const SDK_VERSION_HEADER = 'X-EarnKit-SDK-Version';
|
|
32878
|
+
/**
|
|
32879
|
+
* Resolve the SDK version string from its source components.
|
|
32880
|
+
*
|
|
32881
|
+
* Resolution order:
|
|
32882
|
+
* 1. The registered kit identifier (preferred) — e.g. `@circle-fin/earn-kit/1.1.0`
|
|
32883
|
+
* becomes `earn-kit/1.1.0`.
|
|
32884
|
+
* 2. The provider package itself (fallback when no kit is registered) —
|
|
32885
|
+
* e.g. `provider-earn-service/1.1.0`.
|
|
32886
|
+
* 3. The literal `unknown` when neither source is available.
|
|
32887
|
+
*
|
|
32888
|
+
* Kept as a pure function (no global / module reads) so every branch is
|
|
32889
|
+
* unit-testable.
|
|
32890
|
+
*
|
|
32891
|
+
* @param kitId - The registered kit identifier, or `undefined`.
|
|
32892
|
+
* @param providerName - The provider package name (e.g. `@circle-fin/provider-earn-service`).
|
|
32893
|
+
* @param providerVersion - The provider package version, or `undefined`.
|
|
32894
|
+
* @returns The formatted SDK version string.
|
|
32895
|
+
*
|
|
32896
|
+
* @internal
|
|
32897
|
+
*/ function formatSdkVersion(kitId, providerName, providerVersion) {
|
|
32898
|
+
if (kitId !== undefined && kitId !== '') {
|
|
32899
|
+
// e.g. '@circle-fin/earn-kit/1.1.0' -> 'earn-kit/1.1.0'
|
|
32900
|
+
return resolveKitSdkName(kitId);
|
|
32901
|
+
}
|
|
32902
|
+
{
|
|
32903
|
+
// e.g. '@circle-fin/provider-earn-service' + '1.1.0' -> 'provider-earn-service/1.1.0'
|
|
32904
|
+
return `${resolveKitSdkName(providerName)}/${providerVersion}`;
|
|
32905
|
+
}
|
|
32906
|
+
}
|
|
32907
|
+
/**
|
|
32908
|
+
* Resolve the value for the {@link SDK_VERSION_HEADER} header.
|
|
32909
|
+
*
|
|
32910
|
+
* Reads the kit registered at runtime via {@link createRequestContext} and
|
|
32911
|
+
* falls back to this provider package's own version when no kit is registered.
|
|
32912
|
+
*
|
|
32913
|
+
* @returns The SDK version string, e.g. `earn-kit/1.1.0` or
|
|
32914
|
+
* `provider-earn-service/1.1.0`, or `unknown`.
|
|
32915
|
+
*
|
|
32916
|
+
* @internal
|
|
32917
|
+
*/ function resolveSdkVersionHeader() {
|
|
32918
|
+
return formatSdkVersion(createRequestContext().kit, pkg$1.name, pkg$1.version);
|
|
32919
|
+
}
|
|
32920
|
+
|
|
32519
32921
|
/**
|
|
32520
32922
|
* Build an API polling config with optional authorization header,
|
|
32521
32923
|
* and resolve the base URL (configurable for testing).
|
|
@@ -32526,9 +32928,16 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
32526
32928
|
* @internal
|
|
32527
32929
|
*/ function buildConfig(serviceConfig) {
|
|
32528
32930
|
const baseUrl = serviceConfig?.baseUrl ?? EARN_SERVICE_BASE_URL;
|
|
32931
|
+
const sdkVersion = resolveSdkVersionHeader();
|
|
32529
32932
|
if (serviceConfig?.kitKey === undefined) {
|
|
32530
32933
|
return {
|
|
32531
|
-
pollingConfig:
|
|
32934
|
+
pollingConfig: {
|
|
32935
|
+
...DEFAULT_CONFIG,
|
|
32936
|
+
headers: {
|
|
32937
|
+
...DEFAULT_CONFIG.headers,
|
|
32938
|
+
[SDK_VERSION_HEADER]: sdkVersion
|
|
32939
|
+
}
|
|
32940
|
+
},
|
|
32532
32941
|
baseUrl
|
|
32533
32942
|
};
|
|
32534
32943
|
}
|
|
@@ -32544,6 +32953,7 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
32544
32953
|
...DEFAULT_CONFIG,
|
|
32545
32954
|
headers: {
|
|
32546
32955
|
...DEFAULT_CONFIG.headers,
|
|
32956
|
+
[SDK_VERSION_HEADER]: sdkVersion,
|
|
32547
32957
|
Authorization: `Bearer ${serviceConfig.kitKey}`
|
|
32548
32958
|
}
|
|
32549
32959
|
},
|
|
@@ -32844,7 +33254,8 @@ function toPositionInfo(data) {
|
|
|
32844
33254
|
return {
|
|
32845
33255
|
execId: response.data.execId,
|
|
32846
33256
|
preparedBundle,
|
|
32847
|
-
expiresAt: response.data.expiresAt
|
|
33257
|
+
expiresAt: response.data.expiresAt,
|
|
33258
|
+
review: response.data.review
|
|
32848
33259
|
};
|
|
32849
33260
|
} catch (error) {
|
|
32850
33261
|
throw parseEarnApiError(error, {
|
|
@@ -32955,47 +33366,34 @@ const VALID_AFTER_CLOCK_SKEW_SECONDS = 60n;
|
|
|
32955
33366
|
* @throws If any typed-data field does not match the expected bridge deposit intent.
|
|
32956
33367
|
* @internal
|
|
32957
33368
|
*/ function assertBridgeDepositTypedDataMatchesIntent(intent) {
|
|
32958
|
-
const { preparedBundle, sourceChain, sourceAddress, sourceTokenAddress, authorizationRecipient, amount, maxFee, tokenDecimals, nowSeconds = BigInt(Math.floor(Date.now() / 1000)) } = intent;
|
|
33369
|
+
const { preparedBundle, sourceChain, sourceAddress, sourceTokenAddress, expectedDomain, authorizationRecipient, amount, maxFee, tokenDecimals, amountRaw, nowSeconds = BigInt(Math.floor(Date.now() / 1000)) } = intent;
|
|
32959
33370
|
const { domain, message } = preparedBundle.erc3009TypedData;
|
|
32960
|
-
// USDC deployments differ on the EIP-712 domain name per chain, so the
|
|
32961
|
-
// expected domain is looked up for the source chain being signed on.
|
|
32962
|
-
const expectedDomain = EARN_BRIDGE_ERC3009_DOMAINS[sourceChain.chain];
|
|
32963
|
-
if (expectedDomain === undefined) {
|
|
32964
|
-
throw createValidationFailedError$1('sourceChain.chain', sourceChain.chain, 'no expected ERC-3009 domain is configured for the source chain');
|
|
32965
|
-
}
|
|
32966
|
-
if (domain.name !== expectedDomain.name) {
|
|
32967
|
-
throw createValidationFailedError$1('preparedBundle.erc3009TypedData.domain.name', domain.name, `domain name must be ${expectedDomain.name}`);
|
|
32968
|
-
}
|
|
32969
|
-
if (domain.version !== expectedDomain.version) {
|
|
32970
|
-
throw createValidationFailedError$1('preparedBundle.erc3009TypedData.domain.version', domain.version, `domain version must be ${expectedDomain.version}`);
|
|
32971
|
-
}
|
|
32972
33371
|
assertSameAddress('preparedBundle.erc3009TypedData.domain.verifyingContract', domain.verifyingContract, sourceTokenAddress, 'verifyingContract must match the source token contract');
|
|
32973
33372
|
assertSameBigInt('preparedBundle.erc3009TypedData.domain.chainId', domain.chainId, BigInt(sourceChain.chainId), 'chainId must match the source chain');
|
|
33373
|
+
assertSameString('preparedBundle.erc3009TypedData.domain.name', domain.name, expectedDomain.name, 'domain name must match the source token');
|
|
33374
|
+
assertSameString('preparedBundle.erc3009TypedData.domain.version', domain.version, expectedDomain.version, 'domain version must match the source token');
|
|
32974
33375
|
assertSameAddress('preparedBundle.erc3009TypedData.message.from', message.from, sourceAddress, 'from must match the source wallet');
|
|
32975
33376
|
assertSameAddress('preparedBundle.erc3009TypedData.message.to', message.to, authorizationRecipient, 'to must match the configured authorization recipient');
|
|
32976
|
-
//
|
|
32977
|
-
//
|
|
32978
|
-
|
|
32979
|
-
|
|
32980
|
-
// token to the source token contract before summing.
|
|
32981
|
-
assertSameAddress('preparedBundle.feeQuote.feeToken', preparedBundle.feeQuote.feeToken, sourceTokenAddress, 'feeQuote.feeToken must match the source token contract');
|
|
32982
|
-
// Recompute the expected value from locally trusted inputs (the caller
|
|
32983
|
-
// principal) plus the bundle's own fee items so the guard accepts fee-bearing
|
|
32984
|
-
// deposits while still rejecting any other value tampering. The fee quote is
|
|
32985
|
-
// service-supplied, so bound it to the caller-accepted quote-derived cap
|
|
32986
|
-
// before including it in the signed value.
|
|
32987
|
-
const principal = Amount.parse(amount, {
|
|
33377
|
+
// Recompute the expected ERC-3009 value from locally trusted inputs. Bridge
|
|
33378
|
+
// source fees are paid in the source ERC-20 and are part of the authorization.
|
|
33379
|
+
assertDecimalPlaces('amount', amount, tokenDecimals, `amount must have at most ${String(tokenDecimals)} decimal places for the cross-chain Earn deposit source token`);
|
|
33380
|
+
const parsedAmount = Amount.parse(amount, {
|
|
32988
33381
|
decimals: tokenDecimals
|
|
32989
33382
|
}).raw;
|
|
33383
|
+
const principal = amountRaw === undefined ? parsedAmount : parseReviewedAmount(amountRaw, parsedAmount);
|
|
33384
|
+
const feeQuote = preparedBundle.feeQuote;
|
|
32990
33385
|
const feeTotal = sumBridgeFeeItems(preparedBundle.feeQuote);
|
|
33386
|
+
assertFeeTokenMatchesSourceToken(feeQuote.feeToken, sourceTokenAddress);
|
|
33387
|
+
const feeDecimals = tokenDecimals;
|
|
33388
|
+
assertDecimalPlaces('maxFee', maxFee, feeDecimals, `maxFee must have at most ${String(feeDecimals)} decimal places for the cross-chain Earn deposit fee token`);
|
|
32991
33389
|
const maxFeeRaw = Amount.parse(maxFee, {
|
|
32992
|
-
decimals:
|
|
33390
|
+
decimals: feeDecimals
|
|
32993
33391
|
}).raw;
|
|
32994
33392
|
if (feeTotal > maxFeeRaw) {
|
|
32995
33393
|
throw createValidationFailedError$1('preparedBundle.feeQuote.items', feeTotal.toString(), 'fee total must not exceed maxFee in base units');
|
|
32996
33394
|
}
|
|
32997
33395
|
const expectedValue = principal + feeTotal;
|
|
32998
|
-
assertSameBigInt('preparedBundle.erc3009TypedData.message.value', message.value, expectedValue, 'value must match the requested amount plus
|
|
33396
|
+
assertSameBigInt('preparedBundle.erc3009TypedData.message.value', message.value, expectedValue, 'value must match the requested amount plus source-token fees in base units');
|
|
32999
33397
|
const window = checkErc3009ValidityWindow({
|
|
33000
33398
|
validAfter: BigInt(message.validAfter),
|
|
33001
33399
|
validBefore: BigInt(message.validBefore),
|
|
@@ -33069,14 +33467,19 @@ const VALID_AFTER_CLOCK_SKEW_SECONDS = 60n;
|
|
|
33069
33467
|
/**
|
|
33070
33468
|
* Sum the source-collected fee items in a prepared bundle's fee quote.
|
|
33071
33469
|
*
|
|
33072
|
-
* Each item amount is a base-unit value in the shared `feeToken
|
|
33073
|
-
* what the prepare path adds to the principal when building `message.value`.
|
|
33470
|
+
* Each item amount is a base-unit value in the shared `feeToken`.
|
|
33074
33471
|
*
|
|
33075
33472
|
* @param feeQuote - Fee quote parsed from the prepared bundle.
|
|
33076
|
-
* @returns Total fee in
|
|
33473
|
+
* @returns Total fee in fee-token base units (0n when there are no items).
|
|
33077
33474
|
*/ function sumBridgeFeeItems(feeQuote) {
|
|
33078
33475
|
return feeQuote.items.reduce((total, item)=>total + BigInt(item.amount), 0n);
|
|
33079
33476
|
}
|
|
33477
|
+
function assertFeeTokenMatchesSourceToken(feeToken, sourceTokenAddress) {
|
|
33478
|
+
if (isSameAddress(feeToken, sourceTokenAddress)) {
|
|
33479
|
+
return;
|
|
33480
|
+
}
|
|
33481
|
+
throw createValidationFailedError$1('preparedBundle.feeQuote.feeToken', feeToken, 'fee token must match the cross-chain Earn deposit source token');
|
|
33482
|
+
}
|
|
33080
33483
|
function assertNever(value) {
|
|
33081
33484
|
throw new KitError({
|
|
33082
33485
|
...EarnError.INTERNAL_ERROR,
|
|
@@ -33084,8 +33487,23 @@ function assertNever(value) {
|
|
|
33084
33487
|
message: `Unexpected validity window reason: ${String(value)}`
|
|
33085
33488
|
});
|
|
33086
33489
|
}
|
|
33490
|
+
function parseReviewedAmount(amountRaw, parsedAmount) {
|
|
33491
|
+
let reviewedAmount;
|
|
33492
|
+
try {
|
|
33493
|
+
reviewedAmount = BigInt(amountRaw);
|
|
33494
|
+
} catch {
|
|
33495
|
+
throw createValidationFailedError$1('prepared.review.amount.raw', amountRaw, 'amount must be a non-negative integer string');
|
|
33496
|
+
}
|
|
33497
|
+
if (reviewedAmount < 0n) {
|
|
33498
|
+
throw createValidationFailedError$1('prepared.review.amount.raw', amountRaw, 'amount must be a non-negative integer string');
|
|
33499
|
+
}
|
|
33500
|
+
if (reviewedAmount !== parsedAmount) {
|
|
33501
|
+
throw createValidationFailedError$1('prepared.review.amount.raw', amountRaw, 'review amount must match the requested amount');
|
|
33502
|
+
}
|
|
33503
|
+
return reviewedAmount;
|
|
33504
|
+
}
|
|
33087
33505
|
function assertSameAddress(field, actual, expected, reason) {
|
|
33088
|
-
if (actual
|
|
33506
|
+
if (!isSameAddress(actual, expected)) {
|
|
33089
33507
|
throw createValidationFailedError$1(field, actual, reason);
|
|
33090
33508
|
}
|
|
33091
33509
|
}
|
|
@@ -33094,6 +33512,11 @@ function assertSameBigInt(field, actual, expected, reason) {
|
|
|
33094
33512
|
throw createValidationFailedError$1(field, actual, reason);
|
|
33095
33513
|
}
|
|
33096
33514
|
}
|
|
33515
|
+
function assertSameString(field, actual, expected, reason) {
|
|
33516
|
+
if (actual !== expected) {
|
|
33517
|
+
throw createValidationFailedError$1(field, actual, reason);
|
|
33518
|
+
}
|
|
33519
|
+
}
|
|
33097
33520
|
|
|
33098
33521
|
/**
|
|
33099
33522
|
* Build signed withdrawal instructions via the Earn Service API.
|
|
@@ -33263,9 +33686,15 @@ function toWithdrawalQuoteInfo(data) {
|
|
|
33263
33686
|
symbol: data.asset,
|
|
33264
33687
|
amount: Amount.fromJSON(data.maxWithdrawable)
|
|
33265
33688
|
},
|
|
33266
|
-
fees: data.fees.map((
|
|
33267
|
-
symbol:
|
|
33268
|
-
amount: Amount.fromJSON(
|
|
33689
|
+
fees: data.fees.map(({ token, amount, type, status })=>({
|
|
33690
|
+
symbol: token,
|
|
33691
|
+
amount: Amount.fromJSON(amount),
|
|
33692
|
+
...type !== undefined && {
|
|
33693
|
+
type
|
|
33694
|
+
},
|
|
33695
|
+
...status !== undefined && {
|
|
33696
|
+
status
|
|
33697
|
+
}
|
|
33269
33698
|
})),
|
|
33270
33699
|
gasFees: [],
|
|
33271
33700
|
// Wire format uses `warnings`, but the SDK surface uses
|
|
@@ -33451,6 +33880,9 @@ function toWithdrawalQuoteInfo(data) {
|
|
|
33451
33880
|
'CONFIRMED',
|
|
33452
33881
|
'COMPLETE'
|
|
33453
33882
|
]);
|
|
33883
|
+
// Intentionally built-ins-only: Earn bridge support is limited to SDK-known
|
|
33884
|
+
// token contracts plus the explicit ERC-3009 domain allowlist below.
|
|
33885
|
+
const TOKEN_REGISTRY = createTokenRegistry();
|
|
33454
33886
|
/**
|
|
33455
33887
|
* Build the typed error raised when a cross-chain wait is cancelled via its
|
|
33456
33888
|
* `AbortSignal`. Mirrors `@core/adapter-base`'s `createAbortError` (same
|
|
@@ -33780,24 +34212,20 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33780
34212
|
}
|
|
33781
34213
|
const { address, chain: apiChain, chainDefinition: chain } = await resolveAdapterContext(params.from);
|
|
33782
34214
|
const adapterContractAddress = requireAdapterContract(chain);
|
|
33783
|
-
const rawUsdcAddress = chain.usdcAddress;
|
|
33784
|
-
if (rawUsdcAddress === null) {
|
|
33785
|
-
throw createUnsupportedTokenError('USDC', chain.name);
|
|
33786
|
-
}
|
|
33787
|
-
const usdcAddress = assertHexAddress('chain.usdcAddress', rawUsdcAddress, `USDC address for chain ${chain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
33788
34215
|
const { adapter } = params.from;
|
|
34216
|
+
const vaultAddress = assertHexAddress('vaultAddress', params.vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
33789
34217
|
const { executionParams, signature } = await this.runPhase(ctx, 'deposit', 'fetchParams', async ()=>fetchDeposit({
|
|
33790
|
-
vaultAddress
|
|
34218
|
+
vaultAddress,
|
|
33791
34219
|
amount: params.amount,
|
|
33792
34220
|
address,
|
|
33793
34221
|
chain: apiChain,
|
|
33794
34222
|
config
|
|
33795
34223
|
}), ()=>undefined);
|
|
33796
34224
|
validateExecutionDeadline(executionParams);
|
|
33797
|
-
const
|
|
33798
|
-
const
|
|
34225
|
+
const approvalToken = resolveEarnApprovalToken(executionParams);
|
|
34226
|
+
const tokenInputs = approvalToken === undefined ? [] : buildEarnTokenInputs(executionParams, approvalToken);
|
|
33799
34227
|
const requiredAllowance = sumTokenInputAmounts(tokenInputs);
|
|
33800
|
-
if (!options.skipApprove && approvalToken !== undefined) {
|
|
34228
|
+
if (!options.skipApprove && approvalToken !== undefined && requiredAllowance > 0n) {
|
|
33801
34229
|
await this.runPhase(ctx, 'approve', 'approve', async ()=>approveAllowanceIfNeeded({
|
|
33802
34230
|
adapter,
|
|
33803
34231
|
chain,
|
|
@@ -33805,7 +34233,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33805
34233
|
delegate: adapterContractAddress,
|
|
33806
34234
|
address,
|
|
33807
34235
|
requiredAllowance,
|
|
33808
|
-
revertMessage: '
|
|
34236
|
+
revertMessage: 'Earn deposit token approval reverted on-chain'
|
|
33809
34237
|
}), (txHash)=>txHash);
|
|
33810
34238
|
}
|
|
33811
34239
|
const { txHash, explorerUrl } = await this.runPhase(ctx, 'deposit', 'execute', async ()=>executeEarnAction({
|
|
@@ -33824,7 +34252,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33824
34252
|
kind: 'same-chain',
|
|
33825
34253
|
txHash,
|
|
33826
34254
|
explorerUrl,
|
|
33827
|
-
vaultAddress
|
|
34255
|
+
vaultAddress,
|
|
33828
34256
|
amount: params.amount
|
|
33829
34257
|
};
|
|
33830
34258
|
} catch (error) {
|
|
@@ -33846,13 +34274,6 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33846
34274
|
const sourceAddress = assertHexAddress('from.address', await resolveCrossChainSourceAddress(params.from, sourceChain), `Source address for chain ${sourceChain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
33847
34275
|
const destinationAddress = assertHexAddress('to.recipientAddress', params.to.recipientAddress, `Destination address for chain ${destinationChain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
33848
34276
|
const vaultAddress = assertHexAddress('vaultAddress', params.vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
33849
|
-
// The signed ERC-3009 value is denominated in USDC's 6 decimals. More
|
|
33850
|
-
// precise inputs would be silently truncated by Amount.parse, so the
|
|
33851
|
-
// result would claim a larger deposit than was actually authorized.
|
|
33852
|
-
const amountDecimals = params.amount.split('.')[1]?.length ?? 0;
|
|
33853
|
-
if (amountDecimals > TOKEN_DECIMALS_6) {
|
|
33854
|
-
throw createValidationFailedError$1('amount', params.amount, `amount must have at most ${String(TOKEN_DECIMALS_6)} decimal places for cross-chain Earn deposits`);
|
|
33855
|
-
}
|
|
33856
34277
|
const execId = params.idempotencyKey;
|
|
33857
34278
|
const prepared = await this.runPhase(ctx, 'crossChainDeposit', 'prepare', async ()=>fetchBridgeDepositPrepare({
|
|
33858
34279
|
execId,
|
|
@@ -33872,17 +34293,19 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33872
34293
|
// the error trace, instead of ending the event stream unannounced
|
|
33873
34294
|
// after the prepare success event.
|
|
33874
34295
|
assertPreparedBundleMatchesTypedDataNonce(prepared.preparedBundle);
|
|
34296
|
+
const sourceToken = resolvePreparedBridgeSourceToken(prepared.preparedBundle, route.sourceChain);
|
|
34297
|
+
validatePreparedReviewAmountDecimals(prepared.review.amount.decimals, sourceToken.decimals);
|
|
33875
34298
|
assertBridgeDepositTypedDataMatchesIntent({
|
|
33876
34299
|
preparedBundle: prepared.preparedBundle,
|
|
33877
34300
|
sourceChain: route.sourceChain,
|
|
33878
34301
|
sourceAddress,
|
|
33879
|
-
sourceTokenAddress:
|
|
34302
|
+
sourceTokenAddress: sourceToken.address,
|
|
34303
|
+
expectedDomain: sourceToken.erc3009Domain,
|
|
33880
34304
|
authorizationRecipient: route.authorizationRecipient,
|
|
33881
34305
|
amount: params.amount,
|
|
33882
34306
|
maxFee: params.maxFee,
|
|
33883
|
-
|
|
33884
|
-
|
|
33885
|
-
tokenDecimals: TOKEN_DECIMALS_6
|
|
34307
|
+
tokenDecimals: sourceToken.decimals,
|
|
34308
|
+
amountRaw: prepared.review.amount.raw
|
|
33886
34309
|
});
|
|
33887
34310
|
return signBridgeDepositAuthorization({
|
|
33888
34311
|
adapter: bridgeSigningAdapter,
|
|
@@ -34105,21 +34528,17 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
34105
34528
|
const { adapter, chain, apiChain, address, vaultAddress, amount, config } = params;
|
|
34106
34529
|
try {
|
|
34107
34530
|
const adapterContractAddress = requireAdapterContract(chain);
|
|
34108
|
-
const
|
|
34109
|
-
if (rawUsdcAddress === null) {
|
|
34110
|
-
throw createUnsupportedTokenError('USDC', chain.name);
|
|
34111
|
-
}
|
|
34112
|
-
const usdcAddress = assertHexAddress('chain.usdcAddress', rawUsdcAddress, `USDC address for chain ${chain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
34531
|
+
const normalizedVaultAddress = assertHexAddress('vaultAddress', vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
34113
34532
|
const { executionParams, signature } = await fetchDeposit({
|
|
34114
|
-
vaultAddress,
|
|
34533
|
+
vaultAddress: normalizedVaultAddress,
|
|
34115
34534
|
amount,
|
|
34116
34535
|
address,
|
|
34117
34536
|
chain: apiChain,
|
|
34118
34537
|
config
|
|
34119
34538
|
});
|
|
34120
34539
|
validateExecutionDeadline(executionParams);
|
|
34121
|
-
const
|
|
34122
|
-
const
|
|
34540
|
+
const approvalToken = resolveEarnApprovalToken(executionParams);
|
|
34541
|
+
const tokenInputs = approvalToken === undefined ? [] : buildEarnTokenInputs(executionParams, approvalToken);
|
|
34123
34542
|
const requiredAllowance = sumTokenInputAmounts(tokenInputs);
|
|
34124
34543
|
return await estimateEarnQuoteGasFees({
|
|
34125
34544
|
adapter,
|
|
@@ -34132,7 +34551,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
34132
34551
|
tokenInputs,
|
|
34133
34552
|
signature
|
|
34134
34553
|
},
|
|
34135
|
-
approval: approvalToken !== undefined ? {
|
|
34554
|
+
approval: approvalToken !== undefined && requiredAllowance > 0n ? {
|
|
34136
34555
|
token: approvalToken,
|
|
34137
34556
|
delegate: adapterContractAddress,
|
|
34138
34557
|
requiredAllowance
|
|
@@ -34376,11 +34795,6 @@ function validateCrossChainDepositRoute(sourceChain, destinationChain) {
|
|
|
34376
34795
|
if (destinationApiChain === undefined) {
|
|
34377
34796
|
throw createUnsupportedCrossChainDepositError(destinationChain, 'destination chain is not enabled for cross-chain Earn deposits');
|
|
34378
34797
|
}
|
|
34379
|
-
const sourceTokenAddressRaw = sourceChain.usdcAddress;
|
|
34380
|
-
if (sourceTokenAddressRaw === null) {
|
|
34381
|
-
throw createUnsupportedTokenError('USDC', sourceChain.name);
|
|
34382
|
-
}
|
|
34383
|
-
const sourceTokenAddress = assertHexAddress('sourceChain.usdcAddress', sourceTokenAddressRaw, `USDC address for chain ${sourceChain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
34384
34798
|
if (sourceChain.cctp == null) {
|
|
34385
34799
|
throw createBridgeRouteNotConfiguredError(sourceChain, 'source chain does not support CCTP');
|
|
34386
34800
|
}
|
|
@@ -34404,10 +34818,50 @@ function validateCrossChainDepositRoute(sourceChain, destinationChain) {
|
|
|
34404
34818
|
destinationApiChain,
|
|
34405
34819
|
destinationDomain: destinationChain.cctp.domain,
|
|
34406
34820
|
sourceChain,
|
|
34407
|
-
sourceTokenAddress,
|
|
34408
34821
|
authorizationRecipient
|
|
34409
34822
|
};
|
|
34410
34823
|
}
|
|
34824
|
+
function resolvePreparedBridgeSourceToken(preparedBundle, sourceChain) {
|
|
34825
|
+
const sourceTokenAddress = assertHexAddress('preparedBundle.erc3009TypedData.domain.verifyingContract', preparedBundle.erc3009TypedData.domain.verifyingContract, `Source token for chain ${sourceChain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
34826
|
+
let sourceToken;
|
|
34827
|
+
try {
|
|
34828
|
+
sourceToken = TOKEN_REGISTRY.resolveByAddress(sourceTokenAddress, sourceChain.chain);
|
|
34829
|
+
} catch {
|
|
34830
|
+
throw createValidationFailedError$1('preparedBundle.erc3009TypedData.domain.verifyingContract', sourceTokenAddress, `source token ${sourceTokenAddress} is not configured on source chain ${sourceChain.name} for cross-chain Earn deposits`);
|
|
34831
|
+
}
|
|
34832
|
+
const registryAddress = assertHexAddress('sourceToken.locator', sourceToken.locator, `Source token ${sourceToken.symbol ?? sourceTokenAddress} for chain ${sourceChain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
34833
|
+
if (!isSameAddress(sourceTokenAddress, registryAddress)) {
|
|
34834
|
+
throw createValidationFailedError$1('preparedBundle.erc3009TypedData.domain.verifyingContract', sourceTokenAddress, `source token must match the configured ${sourceToken.symbol ?? 'token'} address on source chain ${sourceChain.name}`);
|
|
34835
|
+
}
|
|
34836
|
+
const { symbol } = sourceToken;
|
|
34837
|
+
if (symbol === undefined) {
|
|
34838
|
+
throw createValidationFailedError$1('preparedBundle.erc3009TypedData.domain.verifyingContract', sourceTokenAddress, `source token ${sourceTokenAddress} is not a registered Earn bridge token on source chain ${sourceChain.name}`);
|
|
34839
|
+
}
|
|
34840
|
+
return {
|
|
34841
|
+
address: sourceTokenAddress,
|
|
34842
|
+
erc3009Domain: resolveBridgeErc3009Domain(symbol, sourceChain),
|
|
34843
|
+
decimals: sourceToken.decimals
|
|
34844
|
+
};
|
|
34845
|
+
}
|
|
34846
|
+
function validatePreparedReviewAmountDecimals(reviewedDecimals, tokenDecimals) {
|
|
34847
|
+
if (reviewedDecimals !== tokenDecimals) {
|
|
34848
|
+
throw createValidationFailedError$1('prepared.review.amount.decimals', reviewedDecimals, `review amount decimals must match the cross-chain Earn deposit source token decimals ${String(tokenDecimals)}`);
|
|
34849
|
+
}
|
|
34850
|
+
}
|
|
34851
|
+
function resolveBridgeErc3009Domain(tokenSymbol, sourceChain) {
|
|
34852
|
+
if (!isEarnBridgeErc3009TokenSymbol(tokenSymbol)) {
|
|
34853
|
+
throw createValidationFailedError$1('sourceToken.symbol', tokenSymbol, `ERC-3009 domain is not configured for ${tokenSymbol} on source chain ${sourceChain.name}`);
|
|
34854
|
+
}
|
|
34855
|
+
const domainsByChain = EARN_BRIDGE_ERC3009_DOMAINS[tokenSymbol];
|
|
34856
|
+
const domain = domainsByChain[sourceChain.chain];
|
|
34857
|
+
if (domain === undefined) {
|
|
34858
|
+
throw createValidationFailedError$1('sourceToken.symbol', tokenSymbol, `ERC-3009 domain is not configured for ${tokenSymbol} on source chain ${sourceChain.name}`);
|
|
34859
|
+
}
|
|
34860
|
+
return domain;
|
|
34861
|
+
}
|
|
34862
|
+
function isEarnBridgeErc3009TokenSymbol(tokenSymbol) {
|
|
34863
|
+
return EARN_BRIDGE_ERC3009_TOKEN_SYMBOLS.includes(tokenSymbol);
|
|
34864
|
+
}
|
|
34411
34865
|
function createUnsupportedCrossChainDepositError(chain, reason) {
|
|
34412
34866
|
return new KitError({
|
|
34413
34867
|
...EarnError.UNSUPPORTED_CHAIN,
|
|
@@ -34480,7 +34934,6 @@ function createUnsupportedCrossChainDepositError(chain, reason) {
|
|
|
34480
34934
|
// Untyped callers can pass anything here; without these guards a null
|
|
34481
34935
|
// config crashes with a raw TypeError on `config.providers` instead of
|
|
34482
34936
|
// the structured validation error every other public surface throws.
|
|
34483
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- JS consumers may pass null or a non-object config
|
|
34484
34937
|
if (typeof config !== 'object' || config === null || Array.isArray(config)) {
|
|
34485
34938
|
throw createValidationFailedError$1('config', config, 'config must be a plain object when provided');
|
|
34486
34939
|
}
|
|
@@ -34847,10 +35300,9 @@ const sourceAdapterContextSchema = z.object({
|
|
|
34847
35300
|
* Schema for validating human-readable decimal amount strings.
|
|
34848
35301
|
*
|
|
34849
35302
|
* Accept positive decimal strings like '100', '100.50', '0.001'. Reject
|
|
34850
|
-
* zero, negative, non-numeric, and non-canonical strings.
|
|
34851
|
-
*
|
|
34852
|
-
*
|
|
34853
|
-
* 7+ decimal-place inputs to the server.
|
|
35303
|
+
* zero, negative, non-numeric, and non-canonical strings. Same-chain
|
|
35304
|
+
* EarnKit vaults can use different asset precisions, so the service enforces
|
|
35305
|
+
* the vault-specific decimal cap after resolving the vault asset.
|
|
34854
35306
|
*
|
|
34855
35307
|
* @internal
|
|
34856
35308
|
*/ const amountSchema$1 = z.string({
|
|
@@ -34859,32 +35311,15 @@ const sourceAdapterContextSchema = z.object({
|
|
|
34859
35311
|
allowZero: false,
|
|
34860
35312
|
regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
|
|
34861
35313
|
attributeName: 'amount',
|
|
34862
|
-
maxDecimals:
|
|
34863
|
-
})(z.string())).refine((value)=>CANONICAL_AMOUNT_REGEX.test(value), AMOUNT_FORMAT_ERROR_MESSAGE);
|
|
34864
|
-
/**
|
|
34865
|
-
* Schema for cross-chain deposit amount strings.
|
|
34866
|
-
*
|
|
34867
|
-
* Cross-chain Earn deposits are USDC-only, and the signed ERC-3009 value is
|
|
34868
|
-
* denominated in USDC's 6 decimals. Reject more precise inputs up front so
|
|
34869
|
-
* the signed value always equals the requested amount instead of a silent
|
|
34870
|
-
* truncation. Non-canonical forms (leading dot, leading zeros) are rejected
|
|
34871
|
-
* too, matching the Earn Service.
|
|
34872
|
-
*
|
|
34873
|
-
* @internal
|
|
34874
|
-
*/ const crossChainAmountSchema = z.string({
|
|
34875
|
-
required_error: 'amount is required'
|
|
34876
|
-
}).min(1, 'amount is required').pipe(createDecimalStringValidator({
|
|
34877
|
-
allowZero: false,
|
|
34878
|
-
regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
|
|
34879
|
-
attributeName: 'amount',
|
|
34880
|
-
maxDecimals: 6
|
|
35314
|
+
maxDecimals: 18
|
|
34881
35315
|
})(z.string())).refine((value)=>CANONICAL_AMOUNT_REGEX.test(value), AMOUNT_FORMAT_ERROR_MESSAGE);
|
|
34882
35316
|
/**
|
|
34883
35317
|
* Schema for cross-chain source fee caps.
|
|
34884
35318
|
*
|
|
34885
|
-
* Cross-chain source fees are denominated in
|
|
34886
|
-
* when the quote contains no source-collected fees; otherwise the cap
|
|
34887
|
-
* the caller-accepted total from a recent quote.
|
|
35319
|
+
* Cross-chain source fees are denominated in the quote's fee token. Zero is
|
|
35320
|
+
* valid when the quote contains no source-collected fees; otherwise the cap
|
|
35321
|
+
* should be the caller-accepted total from a recent quote. The provider
|
|
35322
|
+
* enforces the prepared bundle's fee-token precision before signing.
|
|
34888
35323
|
*
|
|
34889
35324
|
* @internal
|
|
34890
35325
|
*/ const crossChainMaxFeeSchema = z.string({
|
|
@@ -34893,7 +35328,7 @@ const sourceAdapterContextSchema = z.object({
|
|
|
34893
35328
|
allowZero: true,
|
|
34894
35329
|
regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
|
|
34895
35330
|
attributeName: 'maxFee',
|
|
34896
|
-
maxDecimals:
|
|
35331
|
+
maxDecimals: 18
|
|
34897
35332
|
})(z.string())).refine((value)=>CANONICAL_AMOUNT_REGEX.test(value), AMOUNT_FORMAT_ERROR_MESSAGE);
|
|
34898
35333
|
/**
|
|
34899
35334
|
* Build a schema for an optional non-negative decimal filter string.
|
|
@@ -35054,7 +35489,7 @@ const crossChainDepositDestinationSchema = z.object({
|
|
|
35054
35489
|
from: sourceAdapterContextSchema,
|
|
35055
35490
|
to: crossChainDepositDestinationSchema,
|
|
35056
35491
|
vaultAddress: vaultAddressSchema,
|
|
35057
|
-
amount:
|
|
35492
|
+
amount: amountSchema$1,
|
|
35058
35493
|
maxFee: crossChainMaxFeeSchema,
|
|
35059
35494
|
transferSpeed: z.enum([
|
|
35060
35495
|
'FAST',
|
|
@@ -36478,7 +36913,7 @@ function formatRetryResult(operation, result) {
|
|
|
36478
36913
|
}
|
|
36479
36914
|
|
|
36480
36915
|
// Auto-register this kit for user agent tracking
|
|
36481
|
-
registerKit(`${pkg$
|
|
36916
|
+
registerKit(`${pkg$2.name}/${pkg$2.version}`);
|
|
36482
36917
|
|
|
36483
36918
|
/**
|
|
36484
36919
|
* Create an EarnKit instance for AppKit earn operations.
|
|
@@ -36573,12 +37008,17 @@ registerKit(`${pkg$1.name}/${pkg$1.version}`);
|
|
|
36573
37008
|
* The type annotation `readonly TokenAlias[]` ensures this array stays in sync
|
|
36574
37009
|
* with the TokenAlias type definition - TypeScript will enforce any changes.
|
|
36575
37010
|
*
|
|
36576
|
-
*
|
|
37011
|
+
* `USDC`, `USDT`, and `NATIVE` map to dedicated adapter transfer actions.
|
|
37012
|
+
* `EURC` has no dedicated action — it resolves to the chain's `eurcAddress`
|
|
37013
|
+
* and uses the generic token transfer action (see {@link prepareSend}).
|
|
37014
|
+
*
|
|
37015
|
+
* For swap operations, additional tokens (DAI, USDE, PYUSD) are supported
|
|
36577
37016
|
* via SwapKit's SupportedToken type.
|
|
36578
37017
|
*/ const TOKEN_ALIASES = [
|
|
36579
37018
|
'USDC',
|
|
36580
37019
|
'USDT',
|
|
36581
|
-
'NATIVE'
|
|
37020
|
+
'NATIVE',
|
|
37021
|
+
'EURC'
|
|
36582
37022
|
];
|
|
36583
37023
|
/**
|
|
36584
37024
|
* Check if a token string is a known alias.
|
|
@@ -36605,7 +37045,7 @@ registerKit(`${pkg$1.name}/${pkg$1.version}`);
|
|
|
36605
37045
|
* Type guard to check if a string is a valid token address for a chain.
|
|
36606
37046
|
*
|
|
36607
37047
|
* This function verifies that a token string is:
|
|
36608
|
-
* 1. Not a known alias ('USDC', 'USDT', 'NATIVE')
|
|
37048
|
+
* 1. Not a known alias ('USDC', 'USDT', 'NATIVE', 'EURC')
|
|
36609
37049
|
* 2. A valid address format for the specified chain
|
|
36610
37050
|
*
|
|
36611
37051
|
* Use this to narrow the type to `TokenAddress` in TypeScript.
|
|
@@ -36635,7 +37075,7 @@ registerKit(`${pkg$1.name}/${pkg$1.version}`);
|
|
|
36635
37075
|
* Validate and classify a token identifier.
|
|
36636
37076
|
*
|
|
36637
37077
|
* This function determines whether a token string is:
|
|
36638
|
-
* 1. A known alias ('USDC', 'USDT', or '
|
|
37078
|
+
* 1. A known alias ('USDC', 'USDT', 'NATIVE', or 'EURC')
|
|
36639
37079
|
* 2. A valid token address for the given chain
|
|
36640
37080
|
* 3. An invalid/unrecognized token identifier
|
|
36641
37081
|
*
|
|
@@ -36889,8 +37329,11 @@ const tokens = createTokenRegistry();
|
|
|
36889
37329
|
* from `to` (explicit address string or adapter), and normalize the human‑readable
|
|
36890
37330
|
* `amount` into token units.
|
|
36891
37331
|
*
|
|
36892
|
-
* For
|
|
36893
|
-
* with hardcoded decimals (USDC and USDT use 6 decimals; native uses the source chain's native decimals).
|
|
37332
|
+
* For the `USDC`, `USDT`, and `NATIVE` aliases, uses dedicated transfer actions
|
|
37333
|
+
* with hardcoded decimals (USDC and USDT use 6 decimals; native uses the source chain's native decimals).
|
|
37334
|
+
* The `EURC` alias resolves to the chain's `eurcAddress` (6 decimals from the token
|
|
37335
|
+
* registry) and routes through the generic token.transfer action; it is rejected on
|
|
37336
|
+
* chains where `eurcAddress` is not set. For custom token addresses, uses the generic token.transfer
|
|
36894
37337
|
* action and fetches decimals dynamically from the contract.
|
|
36895
37338
|
*
|
|
36896
37339
|
* @param params - The send parameters including source context, destination, amount and token.
|
|
@@ -36983,6 +37426,21 @@ const tokens = createTokenRegistry();
|
|
|
36983
37426
|
preparedSendRequest = await fromAdapter.prepareAction('native.transfer', requestParams, operationContext);
|
|
36984
37427
|
break;
|
|
36985
37428
|
}
|
|
37429
|
+
case 'EURC':
|
|
37430
|
+
{
|
|
37431
|
+
// EURC has no dedicated transfer action. Resolve its contract address
|
|
37432
|
+
// from the chain definition and route through the generic
|
|
37433
|
+
// token.transfer action. `eurcAddress` is null where EURC isn't deployed.
|
|
37434
|
+
const eurcAddress = fromChain.eurcAddress;
|
|
37435
|
+
if (eurcAddress === null) {
|
|
37436
|
+
throw createUnsupportedTokenError(token, fromChain.name);
|
|
37437
|
+
}
|
|
37438
|
+
preparedSendRequest = await fromAdapter.prepareAction('token.transfer', {
|
|
37439
|
+
...requestParams,
|
|
37440
|
+
tokenAddress: eurcAddress
|
|
37441
|
+
}, operationContext);
|
|
37442
|
+
break;
|
|
37443
|
+
}
|
|
36986
37444
|
default:
|
|
36987
37445
|
{
|
|
36988
37446
|
throw createUnsupportedTokenError(String(token), fromChain.name);
|
|
@@ -37009,12 +37467,13 @@ const tokens = createTokenRegistry();
|
|
|
37009
37467
|
* Fully prepares and executes the send transaction to transfer the specified amount and
|
|
37010
37468
|
* token to the specified destination.
|
|
37011
37469
|
*
|
|
37012
|
-
* For
|
|
37013
|
-
*
|
|
37014
|
-
*
|
|
37470
|
+
* For the 'USDC', 'USDT', and 'NATIVE' aliases, uses optimized dedicated actions.
|
|
37471
|
+
* The 'EURC' alias resolves to the chain's `eurcAddress` and routes through the generic
|
|
37472
|
+
* token.transfer action. For custom token addresses, uses the generic token.transfer action
|
|
37473
|
+
* with dynamically fetched decimals.
|
|
37015
37474
|
*
|
|
37016
37475
|
* @param params - The send parameters: source context, destination (address or adapter),
|
|
37017
|
-
* human-readable `amount`, and optional `token` ('USDC' | 'USDT' | 'NATIVE' or custom address, defaults to 'USDC').
|
|
37476
|
+
* human-readable `amount`, and optional `token` ('USDC' | 'USDT' | 'NATIVE' | 'EURC' or custom address, defaults to 'USDC').
|
|
37018
37477
|
* @returns A BridgeStep object with transaction details including hash, status, and explorer URL.
|
|
37019
37478
|
* @throws KitError INPUT_VALIDATION_FAILED if parameters are invalid.
|
|
37020
37479
|
* @throws KitError INPUT_UNSUPPORTED_TOKEN if the token is invalid for the chain.
|
|
@@ -37032,6 +37491,17 @@ const tokens = createTokenRegistry();
|
|
|
37032
37491
|
* console.log('Transaction hash:', step.txHash)
|
|
37033
37492
|
* ```
|
|
37034
37493
|
*
|
|
37494
|
+
* @example Sending EURC on a chain where EURC is deployed (e.g. Arc Testnet)
|
|
37495
|
+
* ```typescript
|
|
37496
|
+
* const step = await send({
|
|
37497
|
+
* from: { adapter: sourceAdapter, chain: 'Arc_Testnet' },
|
|
37498
|
+
* to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
|
|
37499
|
+
* amount: '5.0',
|
|
37500
|
+
* token: 'EURC',
|
|
37501
|
+
* })
|
|
37502
|
+
* console.log('Transaction hash:', step.txHash)
|
|
37503
|
+
* ```
|
|
37504
|
+
*
|
|
37035
37505
|
* @example Sending a custom token (DAI) to an explicit address
|
|
37036
37506
|
* ```typescript
|
|
37037
37507
|
* const step = await send({
|
|
@@ -37073,21 +37543,22 @@ const tokens = createTokenRegistry();
|
|
|
37073
37543
|
};
|
|
37074
37544
|
|
|
37075
37545
|
/**
|
|
37076
|
-
* Estimate the network fees to send USDC, USDT, or a supported native token.
|
|
37546
|
+
* Estimate the network fees to send USDC, USDT, EURC, or a supported native token.
|
|
37077
37547
|
*
|
|
37078
37548
|
* Prepare the send operation (including validating inputs and resolving the recipient when
|
|
37079
37549
|
* provided as an adapter) and return an {@link EstimatedGas} object. This function does
|
|
37080
37550
|
* not submit any transaction.
|
|
37081
37551
|
*
|
|
37082
37552
|
* @remarks
|
|
37083
|
-
* - Selects the transfer handler based on `token` ('USDC' by default, 'USDT', or '
|
|
37553
|
+
* - Selects the transfer handler based on `token` ('USDC' by default, 'USDT', 'NATIVE', or 'EURC').
|
|
37554
|
+
* 'EURC' is only supported on chains where `eurcAddress` is configured.
|
|
37084
37555
|
* - When `to` is an {@link Adapter}, the recipient address is derived from the adapter.
|
|
37085
37556
|
* - Rejects transfers where the resolved `to` equals the source `from` address.
|
|
37086
37557
|
* - Interprets `amount` as a human-readable decimal string (USDC and USDT scaled to 6 decimals,
|
|
37087
37558
|
* native EVM value typically scaled to 18 decimals).
|
|
37088
37559
|
*
|
|
37089
37560
|
* @param params - The send parameters: source context, destination (address or adapter),
|
|
37090
|
-
* human-readable `amount`, and optional `token` ('USDC' | 'USDT' | 'NATIVE', defaults to 'USDC').
|
|
37561
|
+
* human-readable `amount`, and optional `token` ('USDC' | 'USDT' | 'NATIVE' | 'EURC', defaults to 'USDC').
|
|
37091
37562
|
* @returns The estimated gas information including `gas`, `gasPrice`, and total `fee`.
|
|
37092
37563
|
* @throws Error If parameters are invalid.
|
|
37093
37564
|
* @throws Error If the token is unsupported or the send cannot be prepared.
|
|
@@ -37794,7 +38265,7 @@ async function deposit$2(context, params) {
|
|
|
37794
38265
|
}
|
|
37795
38266
|
|
|
37796
38267
|
var name = "@circle-fin/unified-balance-kit";
|
|
37797
|
-
var version = "1.2.
|
|
38268
|
+
var version = "1.2.2";
|
|
37798
38269
|
var pkg = {
|
|
37799
38270
|
name: name,
|
|
37800
38271
|
version: version};
|