@circle-fin/app-kit 1.8.0 → 1.8.1
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 +8 -0
- package/bridge.cjs +48 -56
- package/bridge.d.cts +2 -2
- package/bridge.d.mts +2 -2
- package/bridge.d.ts +2 -2
- package/bridge.mjs +48 -56
- package/context.d.cts +2 -2
- package/context.d.mts +2 -2
- package/context.d.ts +2 -2
- package/earn.cjs +230 -181
- package/earn.d.cts +8 -8
- package/earn.d.mts +8 -8
- package/earn.d.ts +8 -8
- package/earn.mjs +230 -181
- package/estimateBridge.cjs +48 -56
- package/estimateBridge.d.cts +2 -2
- package/estimateBridge.d.mts +2 -2
- package/estimateBridge.d.ts +2 -2
- package/estimateBridge.mjs +48 -56
- package/estimateSwap.cjs +105 -83
- package/estimateSwap.d.cts +2 -2
- package/estimateSwap.d.mts +2 -2
- package/estimateSwap.d.ts +2 -2
- package/estimateSwap.mjs +105 -83
- package/index.cjs +303 -194
- package/index.d.cts +10 -10
- package/index.d.mts +10 -10
- package/index.d.ts +10 -10
- package/index.mjs +303 -194
- package/package.json +6 -6
- package/swap.cjs +105 -83
- package/swap.d.cts +2 -2
- package/swap.d.mts +2 -2
- package/swap.d.ts +2 -2
- package/swap.mjs +105 -83
- package/unifiedBalance.cjs +19 -5
- package/unifiedBalance.mjs +19 -5
package/index.mjs
CHANGED
|
@@ -3650,7 +3650,7 @@ function getOptionalString(value) {
|
|
|
3650
3650
|
* - vault-not-found, unsupported-chain, unsupported-vault,
|
|
3651
3651
|
* signature-rejected, invalid-id, invalid-batch-size, position-not-registered,
|
|
3652
3652
|
* withdrawal-max-exceeded, insufficient-balance, bridge prepare idempotency
|
|
3653
|
-
* conflicts
|
|
3653
|
+
* conflicts, invalid vault-asset amount precision
|
|
3654
3654
|
*
|
|
3655
3655
|
* SERVICE errors (RETRYABLE) — try again later:
|
|
3656
3656
|
* - signing-failed, provider-error, rewards-fetch-failed,
|
|
@@ -3834,6 +3834,13 @@ function getOptionalString(value) {
|
|
|
3834
3834
|
recoverability: 'RETRYABLE'
|
|
3835
3835
|
}
|
|
3836
3836
|
],
|
|
3837
|
+
[
|
|
3838
|
+
380415,
|
|
3839
|
+
{
|
|
3840
|
+
errorDef: EarnError.INVALID_INPUT,
|
|
3841
|
+
recoverability: 'FATAL'
|
|
3842
|
+
}
|
|
3843
|
+
],
|
|
3837
3844
|
// Bridge (380_5XX)
|
|
3838
3845
|
[
|
|
3839
3846
|
380500,
|
|
@@ -3869,6 +3876,13 @@ function getOptionalString(value) {
|
|
|
3869
3876
|
errorDef: EarnError.PROVIDER_ERROR,
|
|
3870
3877
|
recoverability: 'RETRYABLE'
|
|
3871
3878
|
}
|
|
3879
|
+
],
|
|
3880
|
+
[
|
|
3881
|
+
380505,
|
|
3882
|
+
{
|
|
3883
|
+
errorDef: EarnError.PROVIDER_ERROR,
|
|
3884
|
+
recoverability: 'FATAL'
|
|
3885
|
+
}
|
|
3872
3886
|
]
|
|
3873
3887
|
]);
|
|
3874
3888
|
/**
|
|
@@ -4328,23 +4342,6 @@ var EarnChain;
|
|
|
4328
4342
|
}
|
|
4329
4343
|
});
|
|
4330
4344
|
|
|
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
4345
|
/**
|
|
4349
4346
|
* @packageDocumentation
|
|
4350
4347
|
* @module SwapTokenRegistry
|
|
@@ -9985,13 +9982,20 @@ function parseOrThrow(value, schema, context) {
|
|
|
9985
9982
|
symbol: 'EURC',
|
|
9986
9983
|
decimals: 6,
|
|
9987
9984
|
locators: {
|
|
9985
|
+
// =========================================================================
|
|
9986
|
+
// Mainnets
|
|
9987
|
+
// =========================================================================
|
|
9988
9988
|
[Blockchain.Avalanche]: '0xc891EB4cbdEFf6e073e859e987815Ed1505c2ACD',
|
|
9989
9989
|
[Blockchain.Base]: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42',
|
|
9990
9990
|
[Blockchain.Ethereum]: '0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c',
|
|
9991
9991
|
[Blockchain.Solana]: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr',
|
|
9992
9992
|
[Blockchain.World_Chain]: '0x1C60ba0A0eD1019e8Eb035E6daF4155A5cE2380B',
|
|
9993
|
+
// =========================================================================
|
|
9993
9994
|
// Testnets
|
|
9994
|
-
|
|
9995
|
+
// =========================================================================
|
|
9996
|
+
[Blockchain.Arc_Testnet]: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
|
|
9997
|
+
[Blockchain.Base_Sepolia]: '0x808456652fdb597867f38412077A9182bf77359F',
|
|
9998
|
+
[Blockchain.Ethereum_Sepolia]: '0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4'
|
|
9995
9999
|
}
|
|
9996
10000
|
};
|
|
9997
10001
|
|
|
@@ -10201,8 +10205,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
10201
10205
|
* cirBTC (Circle Bitcoin) token definition with addresses and metadata.
|
|
10202
10206
|
*
|
|
10203
10207
|
* @remarks
|
|
10204
|
-
* Built-in cirBTC definition for the TokenRegistry.
|
|
10205
|
-
* on Arc Testnet.
|
|
10208
|
+
* Built-in cirBTC definition for the TokenRegistry.
|
|
10206
10209
|
*
|
|
10207
10210
|
* @example
|
|
10208
10211
|
* ```typescript
|
|
@@ -10218,7 +10221,15 @@ function parseOrThrow(value, schema, context) {
|
|
|
10218
10221
|
symbol: 'cirBTC',
|
|
10219
10222
|
decimals: 8,
|
|
10220
10223
|
locators: {
|
|
10221
|
-
|
|
10224
|
+
// =========================================================================
|
|
10225
|
+
// Mainnets
|
|
10226
|
+
// =========================================================================
|
|
10227
|
+
[Blockchain.Ethereum]: '0x72DFB2E44f59C5AD2bAFE84314E5b99a7cd5075E',
|
|
10228
|
+
// =========================================================================
|
|
10229
|
+
// Testnets
|
|
10230
|
+
// =========================================================================
|
|
10231
|
+
[Blockchain.Arc_Testnet]: '0xf0C4a4CE82A5746AbAAd9425360Ab04fbBA432BF',
|
|
10232
|
+
[Blockchain.Ethereum_Sepolia]: '0x3a3fe695F684Bf9b9e43CF43C2b895Ea5e392bB3'
|
|
10222
10233
|
}
|
|
10223
10234
|
};
|
|
10224
10235
|
|
|
@@ -11499,7 +11510,7 @@ function resolveOptions(options) {
|
|
|
11499
11510
|
}
|
|
11500
11511
|
|
|
11501
11512
|
var name$3 = "@circle-fin/bridge-kit";
|
|
11502
|
-
var version$4 = "1.11.
|
|
11513
|
+
var version$4 = "1.11.1";
|
|
11503
11514
|
var pkg$4 = {
|
|
11504
11515
|
name: name$3,
|
|
11505
11516
|
version: version$4};
|
|
@@ -16290,7 +16301,7 @@ const mockAttestationMessage = {
|
|
|
16290
16301
|
return step;
|
|
16291
16302
|
}
|
|
16292
16303
|
|
|
16293
|
-
var version$3 = "1.8.
|
|
16304
|
+
var version$3 = "1.8.5";
|
|
16294
16305
|
var pkg$3 = {
|
|
16295
16306
|
version: version$3};
|
|
16296
16307
|
|
|
@@ -18697,7 +18708,7 @@ registerKit(`${pkg$4.name}/${pkg$4.version}`);
|
|
|
18697
18708
|
};
|
|
18698
18709
|
|
|
18699
18710
|
var name$2 = "@circle-fin/swap-kit";
|
|
18700
|
-
var version$2 = "1.3.
|
|
18711
|
+
var version$2 = "1.3.1";
|
|
18701
18712
|
var pkg$2 = {
|
|
18702
18713
|
name: name$2,
|
|
18703
18714
|
version: version$2};
|
|
@@ -23632,7 +23643,7 @@ function writeBytes32(buffer, hex, offset) {
|
|
|
23632
23643
|
}
|
|
23633
23644
|
}
|
|
23634
23645
|
|
|
23635
|
-
const TOKEN_REGISTRY$
|
|
23646
|
+
const TOKEN_REGISTRY$4 = createTokenRegistry();
|
|
23636
23647
|
/**
|
|
23637
23648
|
* Resolve token alias to contract address for the given chain.
|
|
23638
23649
|
*
|
|
@@ -23721,7 +23732,7 @@ const TOKEN_REGISTRY$3 = createTokenRegistry();
|
|
|
23721
23732
|
}
|
|
23722
23733
|
});
|
|
23723
23734
|
}
|
|
23724
|
-
const tokenDefinition = TOKEN_REGISTRY$
|
|
23735
|
+
const tokenDefinition = TOKEN_REGISTRY$4.get(upperToken);
|
|
23725
23736
|
if (tokenDefinition !== undefined) {
|
|
23726
23737
|
const resolvedAddress = tokenDefinition.locators[chain.chain];
|
|
23727
23738
|
if (!resolvedAddress) {
|
|
@@ -23738,7 +23749,7 @@ const TOKEN_REGISTRY$3 = createTokenRegistry();
|
|
|
23738
23749
|
}
|
|
23739
23750
|
});
|
|
23740
23751
|
}
|
|
23741
|
-
return TOKEN_REGISTRY$
|
|
23752
|
+
return TOKEN_REGISTRY$4.resolve(upperToken, chain.chain).locator;
|
|
23742
23753
|
}
|
|
23743
23754
|
// Not an alias - return as-is (assumed to be address)
|
|
23744
23755
|
return token;
|
|
@@ -23835,11 +23846,11 @@ function resolveFeePayoutChain(tokenIn, tokenOut, sourceChain, destinationChain)
|
|
|
23835
23846
|
if (destinationChain.chain !== sourceChain.chain) {
|
|
23836
23847
|
return sourceChain;
|
|
23837
23848
|
}
|
|
23838
|
-
const inputIsOk = isOkToken(tokenIn, sourceChain, TOKEN_REGISTRY$
|
|
23849
|
+
const inputIsOk = isOkToken(tokenIn, sourceChain, TOKEN_REGISTRY$4, OK_TOKEN_SYMBOLS$1);
|
|
23839
23850
|
if (inputIsOk) {
|
|
23840
23851
|
return sourceChain;
|
|
23841
23852
|
}
|
|
23842
|
-
const outputIsOk = isOkToken(tokenOut, destinationChain, TOKEN_REGISTRY$
|
|
23853
|
+
const outputIsOk = isOkToken(tokenOut, destinationChain, TOKEN_REGISTRY$4, OK_TOKEN_SYMBOLS$1);
|
|
23843
23854
|
return outputIsOk ? destinationChain : sourceChain;
|
|
23844
23855
|
}
|
|
23845
23856
|
/**
|
|
@@ -25130,7 +25141,7 @@ function resolveFeePayoutChain(tokenIn, tokenOut, sourceChain, destinationChain)
|
|
|
25130
25141
|
}
|
|
25131
25142
|
}
|
|
25132
25143
|
|
|
25133
|
-
const TOKEN_REGISTRY$
|
|
25144
|
+
const TOKEN_REGISTRY$3 = createTokenRegistry();
|
|
25134
25145
|
/**
|
|
25135
25146
|
* Resolve a token identifier to its canonical symbol for the Stablecoin Service Swap Provider.
|
|
25136
25147
|
*
|
|
@@ -25174,7 +25185,7 @@ const TOKEN_REGISTRY$2 = createTokenRegistry();
|
|
|
25174
25185
|
return NATIVE_TOKEN;
|
|
25175
25186
|
}
|
|
25176
25187
|
// For known registry aliases, return canonical symbol directly.
|
|
25177
|
-
if (TOKEN_REGISTRY$
|
|
25188
|
+
if (TOKEN_REGISTRY$3.get(upperToken) !== undefined) {
|
|
25178
25189
|
return upperToken;
|
|
25179
25190
|
}
|
|
25180
25191
|
// For EVM chains, normalize addresses to lowercase for case-insensitive comparison.
|
|
@@ -25184,7 +25195,7 @@ const TOKEN_REGISTRY$2 = createTokenRegistry();
|
|
|
25184
25195
|
// Native address is given
|
|
25185
25196
|
if (isNativeEvmAddress(token) || isNativeSolanaAddress(token)) return NATIVE_TOKEN;
|
|
25186
25197
|
try {
|
|
25187
|
-
const { symbol } = TOKEN_REGISTRY$
|
|
25198
|
+
const { symbol } = TOKEN_REGISTRY$3.resolveByAddress(normalizedToken, chain.chain);
|
|
25188
25199
|
if (symbol === undefined) {
|
|
25189
25200
|
return null;
|
|
25190
25201
|
}
|
|
@@ -25315,7 +25326,7 @@ const TOKEN_REGISTRY$2 = createTokenRegistry();
|
|
|
25315
25326
|
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
25327
|
}
|
|
25317
25328
|
|
|
25318
|
-
const TOKEN_REGISTRY$
|
|
25329
|
+
const TOKEN_REGISTRY$2 = createTokenRegistry();
|
|
25319
25330
|
/**
|
|
25320
25331
|
* Format a raw base-unit amount into a human-readable decimal string.
|
|
25321
25332
|
*
|
|
@@ -25375,12 +25386,12 @@ const TOKEN_REGISTRY$1 = createTokenRegistry();
|
|
|
25375
25386
|
value,
|
|
25376
25387
|
token: resolvedSymbol,
|
|
25377
25388
|
chain,
|
|
25378
|
-
tokens: TOKEN_REGISTRY$
|
|
25389
|
+
tokens: TOKEN_REGISTRY$2
|
|
25379
25390
|
}),
|
|
25380
25391
|
token: resolvedSymbol
|
|
25381
25392
|
};
|
|
25382
25393
|
}
|
|
25383
|
-
const tokenDef = TOKEN_REGISTRY$
|
|
25394
|
+
const tokenDef = TOKEN_REGISTRY$2.get(resolvedSymbol);
|
|
25384
25395
|
if (tokenDef) {
|
|
25385
25396
|
const decimals = resolveTokenDecimals$1(tokenDef, chain.chain);
|
|
25386
25397
|
return {
|
|
@@ -25411,7 +25422,7 @@ const TOKEN_REGISTRY$1 = createTokenRegistry();
|
|
|
25411
25422
|
* i.e. 1 + 0.2 + 0.1 = 1.3. Use the greater of (local estimate × this multiplier)
|
|
25412
25423
|
* or the proxy's gasLimit.
|
|
25413
25424
|
*/ const GAS_SAFETY_MULTIPLIER = 1.3;
|
|
25414
|
-
const TOKEN_REGISTRY = createTokenRegistry();
|
|
25425
|
+
const TOKEN_REGISTRY$1 = createTokenRegistry();
|
|
25415
25426
|
/**
|
|
25416
25427
|
* Enhances a KitError with transaction details (txHash and explorerUrl).
|
|
25417
25428
|
*
|
|
@@ -25487,6 +25498,19 @@ const TOKEN_REGISTRY = createTokenRegistry();
|
|
|
25487
25498
|
*
|
|
25488
25499
|
* @internal
|
|
25489
25500
|
*/ const SUPPORTED_CHAINS$1 = getSwapSupportedChains(Chains);
|
|
25501
|
+
/**
|
|
25502
|
+
* Inter-poll delays (ms) for the same-chain `amountOut` enrichment loop.
|
|
25503
|
+
*
|
|
25504
|
+
* A same-chain swap is already terminal `DONE` once its source transaction
|
|
25505
|
+
* confirms; this short, escalating budget (~5s total across up to four polls)
|
|
25506
|
+
* gives the service a brief window to resolve the output amount before
|
|
25507
|
+
* `swap()` returns. The loop exits the instant `amountOut` is available.
|
|
25508
|
+
*/ const SAME_CHAIN_AMOUNT_OUT_POLL_DELAYS_MS = [
|
|
25509
|
+
1_000,
|
|
25510
|
+
2_000,
|
|
25511
|
+
2_000
|
|
25512
|
+
];
|
|
25513
|
+
const sleep$3 = async (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
|
|
25490
25514
|
function buildSwapProgress(statusResult) {
|
|
25491
25515
|
return {
|
|
25492
25516
|
status: statusResult.status,
|
|
@@ -25506,29 +25530,46 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
25506
25530
|
}
|
|
25507
25531
|
};
|
|
25508
25532
|
}
|
|
25509
|
-
|
|
25510
|
-
|
|
25511
|
-
|
|
25512
|
-
|
|
25513
|
-
|
|
25514
|
-
|
|
25515
|
-
|
|
25516
|
-
|
|
25533
|
+
// A same-chain swap completes atomically in the source transaction, which
|
|
25534
|
+
// has already been confirmed (and receipt-checked for revert) by the time we
|
|
25535
|
+
// get here. Its terminal status is therefore `DONE`. We briefly poll the
|
|
25536
|
+
// status endpoint to enrich `amountOut`, which the service usually resolves
|
|
25537
|
+
// within a few seconds. A non-DONE/erroneous peek never downgrades the
|
|
25538
|
+
// status — once the short budget is exhausted we return `DONE` without
|
|
25539
|
+
// `amountOut`, and the caller can fetch it later via `getSwapStatus`.
|
|
25540
|
+
for(let attempt = 0;; attempt += 1){
|
|
25541
|
+
try {
|
|
25542
|
+
const statusResult = await getSwapStatus$2({
|
|
25543
|
+
txHash,
|
|
25544
|
+
chain: chain.chain,
|
|
25545
|
+
apiKey
|
|
25546
|
+
});
|
|
25547
|
+
if (statusResult.status === 'DONE' && statusResult.amountOut !== undefined) {
|
|
25548
|
+
return {
|
|
25549
|
+
progress: buildSwapProgress(statusResult),
|
|
25550
|
+
amountOut: statusResult.amountOut
|
|
25551
|
+
};
|
|
25552
|
+
}
|
|
25553
|
+
} catch {
|
|
25554
|
+
// Reachability/parse failure during enrichment — non-fatal; the swap
|
|
25555
|
+
// already succeeded on-chain. `getSwapStatus` already retried transient
|
|
25556
|
+
// network errors internally, so further polling is unlikely to help.
|
|
25517
25557
|
return {
|
|
25518
|
-
progress
|
|
25519
|
-
|
|
25558
|
+
progress: {
|
|
25559
|
+
status: 'DONE'
|
|
25560
|
+
}
|
|
25520
25561
|
};
|
|
25521
25562
|
}
|
|
25522
|
-
|
|
25523
|
-
|
|
25524
|
-
|
|
25525
|
-
|
|
25526
|
-
|
|
25527
|
-
|
|
25528
|
-
|
|
25529
|
-
|
|
25530
|
-
|
|
25531
|
-
|
|
25563
|
+
const delayMs = SAME_CHAIN_AMOUNT_OUT_POLL_DELAYS_MS[attempt];
|
|
25564
|
+
if (delayMs === undefined) {
|
|
25565
|
+
// Budget exhausted — terminal `DONE`, `amountOut` not yet available.
|
|
25566
|
+
return {
|
|
25567
|
+
progress: {
|
|
25568
|
+
status: 'DONE'
|
|
25569
|
+
}
|
|
25570
|
+
};
|
|
25571
|
+
}
|
|
25572
|
+
await sleep$3(delayMs);
|
|
25532
25573
|
}
|
|
25533
25574
|
}
|
|
25534
25575
|
/**
|
|
@@ -25809,7 +25850,7 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
25809
25850
|
return true;
|
|
25810
25851
|
}
|
|
25811
25852
|
// At least one token must be supported for fee collection
|
|
25812
|
-
return getSwapOkTokenStatus(tokenInAddress, tokenOutAddress, chain, TOKEN_REGISTRY, OK_TOKEN_SYMBOLS$1).hasOkToken;
|
|
25853
|
+
return getSwapOkTokenStatus(tokenInAddress, tokenOutAddress, chain, TOKEN_REGISTRY$1, OK_TOKEN_SYMBOLS$1).hasOkToken;
|
|
25813
25854
|
}
|
|
25814
25855
|
/**
|
|
25815
25856
|
* Estimate swap costs and output amount by delegating to the Stablecoin Service quote API.
|
|
@@ -26406,14 +26447,14 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
26406
26447
|
}
|
|
26407
26448
|
// Build swap fees with adapter-backed formatting for unregistered tokens
|
|
26408
26449
|
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
|
-
//
|
|
26450
|
+
// Best-effort enrichment: briefly poll for amountOut on same-chain swaps.
|
|
26451
|
+
// They complete atomically in the (already-confirmed) source tx, so the
|
|
26452
|
+
// result is terminal `DONE` regardless; the short poll only adds amountOut
|
|
26453
|
+
// once the service has resolved it. Cross-chain swaps are skipped because
|
|
26454
|
+
// the destination mint takes minutes (CCTPv2 attestation), so polling would
|
|
26455
|
+
// always return PENDING/WAIT_DESTINATION_TRANSACTION while burning the
|
|
26456
|
+
// budget. Callers should use `kit.getSwapStatus()` to poll cross-chain
|
|
26457
|
+
// swaps to DONE.
|
|
26417
26458
|
const statusSnapshot = await fetchSameChainStatusSnapshot({
|
|
26418
26459
|
isCrossChainSwap,
|
|
26419
26460
|
txHash,
|
|
@@ -30715,11 +30756,14 @@ registerKit(`${pkg$2.name}/${pkg$2.version}`);
|
|
|
30715
30756
|
};
|
|
30716
30757
|
|
|
30717
30758
|
var name$1 = "@circle-fin/earn-kit";
|
|
30718
|
-
var version$1 = "1.2.
|
|
30759
|
+
var version$1 = "1.2.1";
|
|
30719
30760
|
var pkg$1 = {
|
|
30720
30761
|
name: name$1,
|
|
30721
30762
|
version: version$1};
|
|
30722
30763
|
|
|
30764
|
+
const EARN_BRIDGE_ERC3009_TOKEN_SYMBOLS = [
|
|
30765
|
+
'USDC'
|
|
30766
|
+
];
|
|
30723
30767
|
/**
|
|
30724
30768
|
* Default base URL for the Earn Service API.
|
|
30725
30769
|
*
|
|
@@ -30764,20 +30808,7 @@ var pkg$1 = {
|
|
|
30764
30808
|
*/ const EARN_BRIDGE_DESTINATION_CHAIN_TO_API = {
|
|
30765
30809
|
[Blockchain.Arc_Testnet]: CHAIN_TO_API[Blockchain.Arc_Testnet]
|
|
30766
30810
|
};
|
|
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 = {
|
|
30811
|
+
const EARN_BRIDGE_USDC_ERC3009_DOMAINS = {
|
|
30781
30812
|
[Blockchain.Arbitrum_Sepolia]: {
|
|
30782
30813
|
name: 'USD Coin',
|
|
30783
30814
|
version: '2'
|
|
@@ -30791,6 +30822,21 @@ var pkg$1 = {
|
|
|
30791
30822
|
version: '2'
|
|
30792
30823
|
}
|
|
30793
30824
|
};
|
|
30825
|
+
/**
|
|
30826
|
+
* Expected EIP-712 domain of the ERC-3009 authorization token per
|
|
30827
|
+
* cross-chain-supported source token and source chain.
|
|
30828
|
+
*
|
|
30829
|
+
* The bridge service prepares an ERC-3009 payload for the deposited token. We
|
|
30830
|
+
* keep this table as an explicit SDK allowlist because token deployments can
|
|
30831
|
+
* differ by source chain and by domain name/version. These values mirror the
|
|
30832
|
+
* bridge service's per-chain domain table and the on-chain DOMAIN_SEPARATOR
|
|
30833
|
+
* inputs. Asserted before signing so a tampered prepare response cannot point
|
|
30834
|
+
* the signature at another token or unexpected token domain.
|
|
30835
|
+
*
|
|
30836
|
+
* @internal
|
|
30837
|
+
*/ const EARN_BRIDGE_ERC3009_DOMAINS = {
|
|
30838
|
+
USDC: EARN_BRIDGE_USDC_ERC3009_DOMAINS
|
|
30839
|
+
};
|
|
30794
30840
|
/**
|
|
30795
30841
|
* Maximum tolerated remaining lifetime of a prepared ERC-3009 authorization.
|
|
30796
30842
|
*
|
|
@@ -31250,6 +31296,16 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
31250
31296
|
return approvalTxHash;
|
|
31251
31297
|
}
|
|
31252
31298
|
|
|
31299
|
+
/** @internal */ function isSameAddress(actual, expected) {
|
|
31300
|
+
return actual.toLowerCase() === expected.toLowerCase();
|
|
31301
|
+
}
|
|
31302
|
+
/** @internal */ function assertDecimalPlaces(field, value, tokenDecimals, reason) {
|
|
31303
|
+
const decimals = value.split('.')[1]?.length ?? 0;
|
|
31304
|
+
if (decimals > tokenDecimals) {
|
|
31305
|
+
throw createValidationFailedError$1(field, value, reason);
|
|
31306
|
+
}
|
|
31307
|
+
}
|
|
31308
|
+
|
|
31253
31309
|
/**
|
|
31254
31310
|
* Build the `tokenInputs` array forwarded to the adapter contract's
|
|
31255
31311
|
* `execute(executeParams, tokenInputs, signature)` call.
|
|
@@ -31273,15 +31329,17 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
31273
31329
|
*
|
|
31274
31330
|
* @example
|
|
31275
31331
|
* ```typescript
|
|
31276
|
-
* const
|
|
31277
|
-
* 'chain.usdcAddress',
|
|
31278
|
-
* chain.usdcAddress,
|
|
31279
|
-
* )
|
|
31280
|
-
*
|
|
31281
|
-
* const tokenInputs = buildEarnTokenInputs(
|
|
31332
|
+
* const approvalToken = resolveEarnApprovalToken(
|
|
31282
31333
|
* executionPayload.executionParams,
|
|
31283
|
-
* approvedToken,
|
|
31284
31334
|
* )
|
|
31335
|
+
*
|
|
31336
|
+
* const tokenInputs =
|
|
31337
|
+
* approvalToken === undefined
|
|
31338
|
+
* ? []
|
|
31339
|
+
* : buildEarnTokenInputs(
|
|
31340
|
+
* executionPayload.executionParams,
|
|
31341
|
+
* approvalToken,
|
|
31342
|
+
* )
|
|
31285
31343
|
* ```
|
|
31286
31344
|
*
|
|
31287
31345
|
* @internal
|
|
@@ -31293,7 +31351,7 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
31293
31351
|
return;
|
|
31294
31352
|
}
|
|
31295
31353
|
const { tokenIn } = instruction;
|
|
31296
|
-
if (tokenIn
|
|
31354
|
+
if (!isSameAddress(tokenIn, approvedToken)) {
|
|
31297
31355
|
throw createValidationFailedError$1(`executionParams.instructions[${index.toString()}].tokenIn`, tokenIn, 'tokenIn must match the token approved for adapter spending');
|
|
31298
31356
|
}
|
|
31299
31357
|
tokenInputs.push({
|
|
@@ -31305,6 +31363,33 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
31305
31363
|
});
|
|
31306
31364
|
return tokenInputs;
|
|
31307
31365
|
}
|
|
31366
|
+
/**
|
|
31367
|
+
* Resolve the single token that needs adapter allowance from signed Earn
|
|
31368
|
+
* instructions.
|
|
31369
|
+
*
|
|
31370
|
+
* @param executionParams - Service-signed `ExecutionParams` forwarded to the adapter.
|
|
31371
|
+
* @returns The token requested by positive approval instructions, or `undefined`.
|
|
31372
|
+
* @throws {@link KitError} If positive approval instructions reference multiple tokens.
|
|
31373
|
+
*
|
|
31374
|
+
* @internal
|
|
31375
|
+
*/ function resolveEarnApprovalToken(executionParams) {
|
|
31376
|
+
let approvedToken;
|
|
31377
|
+
executionParams.instructions.forEach((instruction, index)=>{
|
|
31378
|
+
const amount = BigInt(instruction.amountToApprove);
|
|
31379
|
+
if (amount <= 0n) {
|
|
31380
|
+
return;
|
|
31381
|
+
}
|
|
31382
|
+
const { tokenIn } = instruction;
|
|
31383
|
+
if (approvedToken === undefined) {
|
|
31384
|
+
approvedToken = tokenIn;
|
|
31385
|
+
return;
|
|
31386
|
+
}
|
|
31387
|
+
if (!isSameAddress(tokenIn, approvedToken)) {
|
|
31388
|
+
throw createValidationFailedError$1(`executionParams.instructions[${index.toString()}].tokenIn`, tokenIn, 'tokenIn must match the token approved for adapter spending');
|
|
31389
|
+
}
|
|
31390
|
+
});
|
|
31391
|
+
return approvedToken;
|
|
31392
|
+
}
|
|
31308
31393
|
|
|
31309
31394
|
/**
|
|
31310
31395
|
* Prepare an earn adapter action, execute it, wait for confirmation, and
|
|
@@ -31796,6 +31881,7 @@ const hexSignatureSchema = evmSignatureSchema;
|
|
|
31796
31881
|
const hexAddressSchema = evmAddressSchema;
|
|
31797
31882
|
// '0x' prefix + 32 bytes * 2 hex chars.
|
|
31798
31883
|
const BYTES32_HEX_LENGTH = 66;
|
|
31884
|
+
const bridgeFeeTokenSchema = hexAddressSchema;
|
|
31799
31885
|
/**
|
|
31800
31886
|
* Zod schema for a non-negative uint256-like value.
|
|
31801
31887
|
*
|
|
@@ -32082,14 +32168,12 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
32082
32168
|
*
|
|
32083
32169
|
* The bridge prepare path returns one item per collected source fee (e.g.
|
|
32084
32170
|
* `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.
|
|
32171
|
+
* forward fee), all denominated in the shared `feeToken`. Each item's `amount`
|
|
32172
|
+
* is a base-unit decimal string in `feeToken` units.
|
|
32089
32173
|
*
|
|
32090
32174
|
* @internal
|
|
32091
32175
|
*/ const bridgeFeeQuoteSchema = z.object({
|
|
32092
|
-
feeToken:
|
|
32176
|
+
feeToken: bridgeFeeTokenSchema,
|
|
32093
32177
|
items: z.array(z.object({
|
|
32094
32178
|
type: z.string(),
|
|
32095
32179
|
amount: uint256LikeSchema
|
|
@@ -32105,6 +32189,12 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
32105
32189
|
erc3009TypedData: erc3009TypedDataSchema,
|
|
32106
32190
|
feeQuote: bridgeFeeQuoteSchema
|
|
32107
32191
|
}).passthrough();
|
|
32192
|
+
const bridgeDepositPrepareReviewSchema = z.object({
|
|
32193
|
+
sourceChain: z.string(),
|
|
32194
|
+
destinationChain: z.string(),
|
|
32195
|
+
amount: amountJsonSchema,
|
|
32196
|
+
vaultAddress: hexAddressSchema
|
|
32197
|
+
}).passthrough();
|
|
32108
32198
|
/**
|
|
32109
32199
|
* Zod schema for the bridge deposit prepare payload.
|
|
32110
32200
|
*
|
|
@@ -32116,7 +32206,7 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
32116
32206
|
execId: bridgeDepositExecIdSchema,
|
|
32117
32207
|
erc3009TypedData: bridgeDepositPreparedBundleSchema,
|
|
32118
32208
|
expiresAt: z.string().datetime(),
|
|
32119
|
-
review:
|
|
32209
|
+
review: bridgeDepositPrepareReviewSchema
|
|
32120
32210
|
});
|
|
32121
32211
|
/**
|
|
32122
32212
|
* Zod schema for the `POST /v1/earnKit/bridge/deposit/prepare` API response.
|
|
@@ -32844,7 +32934,8 @@ function toPositionInfo(data) {
|
|
|
32844
32934
|
return {
|
|
32845
32935
|
execId: response.data.execId,
|
|
32846
32936
|
preparedBundle,
|
|
32847
|
-
expiresAt: response.data.expiresAt
|
|
32937
|
+
expiresAt: response.data.expiresAt,
|
|
32938
|
+
review: response.data.review
|
|
32848
32939
|
};
|
|
32849
32940
|
} catch (error) {
|
|
32850
32941
|
throw parseEarnApiError(error, {
|
|
@@ -32955,47 +33046,34 @@ const VALID_AFTER_CLOCK_SKEW_SECONDS = 60n;
|
|
|
32955
33046
|
* @throws If any typed-data field does not match the expected bridge deposit intent.
|
|
32956
33047
|
* @internal
|
|
32957
33048
|
*/ function assertBridgeDepositTypedDataMatchesIntent(intent) {
|
|
32958
|
-
const { preparedBundle, sourceChain, sourceAddress, sourceTokenAddress, authorizationRecipient, amount, maxFee, tokenDecimals, nowSeconds = BigInt(Math.floor(Date.now() / 1000)) } = intent;
|
|
33049
|
+
const { preparedBundle, sourceChain, sourceAddress, sourceTokenAddress, expectedDomain, authorizationRecipient, amount, maxFee, tokenDecimals, amountRaw, nowSeconds = BigInt(Math.floor(Date.now() / 1000)) } = intent;
|
|
32959
33050
|
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
33051
|
assertSameAddress('preparedBundle.erc3009TypedData.domain.verifyingContract', domain.verifyingContract, sourceTokenAddress, 'verifyingContract must match the source token contract');
|
|
32973
33052
|
assertSameBigInt('preparedBundle.erc3009TypedData.domain.chainId', domain.chainId, BigInt(sourceChain.chainId), 'chainId must match the source chain');
|
|
33053
|
+
assertSameString('preparedBundle.erc3009TypedData.domain.name', domain.name, expectedDomain.name, 'domain name must match the source token');
|
|
33054
|
+
assertSameString('preparedBundle.erc3009TypedData.domain.version', domain.version, expectedDomain.version, 'domain version must match the source token');
|
|
32974
33055
|
assertSameAddress('preparedBundle.erc3009TypedData.message.from', message.from, sourceAddress, 'from must match the source wallet');
|
|
32975
33056
|
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, {
|
|
33057
|
+
// Recompute the expected ERC-3009 value from locally trusted inputs. Bridge
|
|
33058
|
+
// source fees are paid in the source ERC-20 and are part of the authorization.
|
|
33059
|
+
assertDecimalPlaces('amount', amount, tokenDecimals, `amount must have at most ${String(tokenDecimals)} decimal places for the cross-chain Earn deposit source token`);
|
|
33060
|
+
const parsedAmount = Amount.parse(amount, {
|
|
32988
33061
|
decimals: tokenDecimals
|
|
32989
33062
|
}).raw;
|
|
33063
|
+
const principal = amountRaw === undefined ? parsedAmount : parseReviewedAmount(amountRaw, parsedAmount);
|
|
33064
|
+
const feeQuote = preparedBundle.feeQuote;
|
|
32990
33065
|
const feeTotal = sumBridgeFeeItems(preparedBundle.feeQuote);
|
|
33066
|
+
assertFeeTokenMatchesSourceToken(feeQuote.feeToken, sourceTokenAddress);
|
|
33067
|
+
const feeDecimals = tokenDecimals;
|
|
33068
|
+
assertDecimalPlaces('maxFee', maxFee, feeDecimals, `maxFee must have at most ${String(feeDecimals)} decimal places for the cross-chain Earn deposit fee token`);
|
|
32991
33069
|
const maxFeeRaw = Amount.parse(maxFee, {
|
|
32992
|
-
decimals:
|
|
33070
|
+
decimals: feeDecimals
|
|
32993
33071
|
}).raw;
|
|
32994
33072
|
if (feeTotal > maxFeeRaw) {
|
|
32995
33073
|
throw createValidationFailedError$1('preparedBundle.feeQuote.items', feeTotal.toString(), 'fee total must not exceed maxFee in base units');
|
|
32996
33074
|
}
|
|
32997
33075
|
const expectedValue = principal + feeTotal;
|
|
32998
|
-
assertSameBigInt('preparedBundle.erc3009TypedData.message.value', message.value, expectedValue, 'value must match the requested amount plus
|
|
33076
|
+
assertSameBigInt('preparedBundle.erc3009TypedData.message.value', message.value, expectedValue, 'value must match the requested amount plus source-token fees in base units');
|
|
32999
33077
|
const window = checkErc3009ValidityWindow({
|
|
33000
33078
|
validAfter: BigInt(message.validAfter),
|
|
33001
33079
|
validBefore: BigInt(message.validBefore),
|
|
@@ -33069,14 +33147,19 @@ const VALID_AFTER_CLOCK_SKEW_SECONDS = 60n;
|
|
|
33069
33147
|
/**
|
|
33070
33148
|
* Sum the source-collected fee items in a prepared bundle's fee quote.
|
|
33071
33149
|
*
|
|
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`.
|
|
33150
|
+
* Each item amount is a base-unit value in the shared `feeToken`.
|
|
33074
33151
|
*
|
|
33075
33152
|
* @param feeQuote - Fee quote parsed from the prepared bundle.
|
|
33076
|
-
* @returns Total fee in
|
|
33153
|
+
* @returns Total fee in fee-token base units (0n when there are no items).
|
|
33077
33154
|
*/ function sumBridgeFeeItems(feeQuote) {
|
|
33078
33155
|
return feeQuote.items.reduce((total, item)=>total + BigInt(item.amount), 0n);
|
|
33079
33156
|
}
|
|
33157
|
+
function assertFeeTokenMatchesSourceToken(feeToken, sourceTokenAddress) {
|
|
33158
|
+
if (isSameAddress(feeToken, sourceTokenAddress)) {
|
|
33159
|
+
return;
|
|
33160
|
+
}
|
|
33161
|
+
throw createValidationFailedError$1('preparedBundle.feeQuote.feeToken', feeToken, 'fee token must match the cross-chain Earn deposit source token');
|
|
33162
|
+
}
|
|
33080
33163
|
function assertNever(value) {
|
|
33081
33164
|
throw new KitError({
|
|
33082
33165
|
...EarnError.INTERNAL_ERROR,
|
|
@@ -33084,8 +33167,23 @@ function assertNever(value) {
|
|
|
33084
33167
|
message: `Unexpected validity window reason: ${String(value)}`
|
|
33085
33168
|
});
|
|
33086
33169
|
}
|
|
33170
|
+
function parseReviewedAmount(amountRaw, parsedAmount) {
|
|
33171
|
+
let reviewedAmount;
|
|
33172
|
+
try {
|
|
33173
|
+
reviewedAmount = BigInt(amountRaw);
|
|
33174
|
+
} catch {
|
|
33175
|
+
throw createValidationFailedError$1('prepared.review.amount.raw', amountRaw, 'amount must be a non-negative integer string');
|
|
33176
|
+
}
|
|
33177
|
+
if (reviewedAmount < 0n) {
|
|
33178
|
+
throw createValidationFailedError$1('prepared.review.amount.raw', amountRaw, 'amount must be a non-negative integer string');
|
|
33179
|
+
}
|
|
33180
|
+
if (reviewedAmount !== parsedAmount) {
|
|
33181
|
+
throw createValidationFailedError$1('prepared.review.amount.raw', amountRaw, 'review amount must match the requested amount');
|
|
33182
|
+
}
|
|
33183
|
+
return reviewedAmount;
|
|
33184
|
+
}
|
|
33087
33185
|
function assertSameAddress(field, actual, expected, reason) {
|
|
33088
|
-
if (actual
|
|
33186
|
+
if (!isSameAddress(actual, expected)) {
|
|
33089
33187
|
throw createValidationFailedError$1(field, actual, reason);
|
|
33090
33188
|
}
|
|
33091
33189
|
}
|
|
@@ -33094,6 +33192,11 @@ function assertSameBigInt(field, actual, expected, reason) {
|
|
|
33094
33192
|
throw createValidationFailedError$1(field, actual, reason);
|
|
33095
33193
|
}
|
|
33096
33194
|
}
|
|
33195
|
+
function assertSameString(field, actual, expected, reason) {
|
|
33196
|
+
if (actual !== expected) {
|
|
33197
|
+
throw createValidationFailedError$1(field, actual, reason);
|
|
33198
|
+
}
|
|
33199
|
+
}
|
|
33097
33200
|
|
|
33098
33201
|
/**
|
|
33099
33202
|
* Build signed withdrawal instructions via the Earn Service API.
|
|
@@ -33451,6 +33554,9 @@ function toWithdrawalQuoteInfo(data) {
|
|
|
33451
33554
|
'CONFIRMED',
|
|
33452
33555
|
'COMPLETE'
|
|
33453
33556
|
]);
|
|
33557
|
+
// Intentionally built-ins-only: Earn bridge support is limited to SDK-known
|
|
33558
|
+
// token contracts plus the explicit ERC-3009 domain allowlist below.
|
|
33559
|
+
const TOKEN_REGISTRY = createTokenRegistry();
|
|
33454
33560
|
/**
|
|
33455
33561
|
* Build the typed error raised when a cross-chain wait is cancelled via its
|
|
33456
33562
|
* `AbortSignal`. Mirrors `@core/adapter-base`'s `createAbortError` (same
|
|
@@ -33780,24 +33886,20 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33780
33886
|
}
|
|
33781
33887
|
const { address, chain: apiChain, chainDefinition: chain } = await resolveAdapterContext(params.from);
|
|
33782
33888
|
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
33889
|
const { adapter } = params.from;
|
|
33890
|
+
const vaultAddress = assertHexAddress('vaultAddress', params.vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
33789
33891
|
const { executionParams, signature } = await this.runPhase(ctx, 'deposit', 'fetchParams', async ()=>fetchDeposit({
|
|
33790
|
-
vaultAddress
|
|
33892
|
+
vaultAddress,
|
|
33791
33893
|
amount: params.amount,
|
|
33792
33894
|
address,
|
|
33793
33895
|
chain: apiChain,
|
|
33794
33896
|
config
|
|
33795
33897
|
}), ()=>undefined);
|
|
33796
33898
|
validateExecutionDeadline(executionParams);
|
|
33797
|
-
const
|
|
33798
|
-
const
|
|
33899
|
+
const approvalToken = resolveEarnApprovalToken(executionParams);
|
|
33900
|
+
const tokenInputs = approvalToken === undefined ? [] : buildEarnTokenInputs(executionParams, approvalToken);
|
|
33799
33901
|
const requiredAllowance = sumTokenInputAmounts(tokenInputs);
|
|
33800
|
-
if (!options.skipApprove && approvalToken !== undefined) {
|
|
33902
|
+
if (!options.skipApprove && approvalToken !== undefined && requiredAllowance > 0n) {
|
|
33801
33903
|
await this.runPhase(ctx, 'approve', 'approve', async ()=>approveAllowanceIfNeeded({
|
|
33802
33904
|
adapter,
|
|
33803
33905
|
chain,
|
|
@@ -33805,7 +33907,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33805
33907
|
delegate: adapterContractAddress,
|
|
33806
33908
|
address,
|
|
33807
33909
|
requiredAllowance,
|
|
33808
|
-
revertMessage: '
|
|
33910
|
+
revertMessage: 'Earn deposit token approval reverted on-chain'
|
|
33809
33911
|
}), (txHash)=>txHash);
|
|
33810
33912
|
}
|
|
33811
33913
|
const { txHash, explorerUrl } = await this.runPhase(ctx, 'deposit', 'execute', async ()=>executeEarnAction({
|
|
@@ -33824,7 +33926,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33824
33926
|
kind: 'same-chain',
|
|
33825
33927
|
txHash,
|
|
33826
33928
|
explorerUrl,
|
|
33827
|
-
vaultAddress
|
|
33929
|
+
vaultAddress,
|
|
33828
33930
|
amount: params.amount
|
|
33829
33931
|
};
|
|
33830
33932
|
} catch (error) {
|
|
@@ -33846,13 +33948,6 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33846
33948
|
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
33949
|
const destinationAddress = assertHexAddress('to.recipientAddress', params.to.recipientAddress, `Destination address for chain ${destinationChain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
33848
33950
|
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
33951
|
const execId = params.idempotencyKey;
|
|
33857
33952
|
const prepared = await this.runPhase(ctx, 'crossChainDeposit', 'prepare', async ()=>fetchBridgeDepositPrepare({
|
|
33858
33953
|
execId,
|
|
@@ -33872,17 +33967,19 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33872
33967
|
// the error trace, instead of ending the event stream unannounced
|
|
33873
33968
|
// after the prepare success event.
|
|
33874
33969
|
assertPreparedBundleMatchesTypedDataNonce(prepared.preparedBundle);
|
|
33970
|
+
const sourceToken = resolvePreparedBridgeSourceToken(prepared.preparedBundle, route.sourceChain);
|
|
33971
|
+
validatePreparedReviewAmountDecimals(prepared.review.amount.decimals, sourceToken.decimals);
|
|
33875
33972
|
assertBridgeDepositTypedDataMatchesIntent({
|
|
33876
33973
|
preparedBundle: prepared.preparedBundle,
|
|
33877
33974
|
sourceChain: route.sourceChain,
|
|
33878
33975
|
sourceAddress,
|
|
33879
|
-
sourceTokenAddress:
|
|
33976
|
+
sourceTokenAddress: sourceToken.address,
|
|
33977
|
+
expectedDomain: sourceToken.erc3009Domain,
|
|
33880
33978
|
authorizationRecipient: route.authorizationRecipient,
|
|
33881
33979
|
amount: params.amount,
|
|
33882
33980
|
maxFee: params.maxFee,
|
|
33883
|
-
|
|
33884
|
-
|
|
33885
|
-
tokenDecimals: TOKEN_DECIMALS_6
|
|
33981
|
+
tokenDecimals: sourceToken.decimals,
|
|
33982
|
+
amountRaw: prepared.review.amount.raw
|
|
33886
33983
|
});
|
|
33887
33984
|
return signBridgeDepositAuthorization({
|
|
33888
33985
|
adapter: bridgeSigningAdapter,
|
|
@@ -34105,21 +34202,17 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
34105
34202
|
const { adapter, chain, apiChain, address, vaultAddress, amount, config } = params;
|
|
34106
34203
|
try {
|
|
34107
34204
|
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.`);
|
|
34205
|
+
const normalizedVaultAddress = assertHexAddress('vaultAddress', vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
34113
34206
|
const { executionParams, signature } = await fetchDeposit({
|
|
34114
|
-
vaultAddress,
|
|
34207
|
+
vaultAddress: normalizedVaultAddress,
|
|
34115
34208
|
amount,
|
|
34116
34209
|
address,
|
|
34117
34210
|
chain: apiChain,
|
|
34118
34211
|
config
|
|
34119
34212
|
});
|
|
34120
34213
|
validateExecutionDeadline(executionParams);
|
|
34121
|
-
const
|
|
34122
|
-
const
|
|
34214
|
+
const approvalToken = resolveEarnApprovalToken(executionParams);
|
|
34215
|
+
const tokenInputs = approvalToken === undefined ? [] : buildEarnTokenInputs(executionParams, approvalToken);
|
|
34123
34216
|
const requiredAllowance = sumTokenInputAmounts(tokenInputs);
|
|
34124
34217
|
return await estimateEarnQuoteGasFees({
|
|
34125
34218
|
adapter,
|
|
@@ -34132,7 +34225,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
34132
34225
|
tokenInputs,
|
|
34133
34226
|
signature
|
|
34134
34227
|
},
|
|
34135
|
-
approval: approvalToken !== undefined ? {
|
|
34228
|
+
approval: approvalToken !== undefined && requiredAllowance > 0n ? {
|
|
34136
34229
|
token: approvalToken,
|
|
34137
34230
|
delegate: adapterContractAddress,
|
|
34138
34231
|
requiredAllowance
|
|
@@ -34376,11 +34469,6 @@ function validateCrossChainDepositRoute(sourceChain, destinationChain) {
|
|
|
34376
34469
|
if (destinationApiChain === undefined) {
|
|
34377
34470
|
throw createUnsupportedCrossChainDepositError(destinationChain, 'destination chain is not enabled for cross-chain Earn deposits');
|
|
34378
34471
|
}
|
|
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
34472
|
if (sourceChain.cctp == null) {
|
|
34385
34473
|
throw createBridgeRouteNotConfiguredError(sourceChain, 'source chain does not support CCTP');
|
|
34386
34474
|
}
|
|
@@ -34404,10 +34492,50 @@ function validateCrossChainDepositRoute(sourceChain, destinationChain) {
|
|
|
34404
34492
|
destinationApiChain,
|
|
34405
34493
|
destinationDomain: destinationChain.cctp.domain,
|
|
34406
34494
|
sourceChain,
|
|
34407
|
-
sourceTokenAddress,
|
|
34408
34495
|
authorizationRecipient
|
|
34409
34496
|
};
|
|
34410
34497
|
}
|
|
34498
|
+
function resolvePreparedBridgeSourceToken(preparedBundle, sourceChain) {
|
|
34499
|
+
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.`);
|
|
34500
|
+
let sourceToken;
|
|
34501
|
+
try {
|
|
34502
|
+
sourceToken = TOKEN_REGISTRY.resolveByAddress(sourceTokenAddress, sourceChain.chain);
|
|
34503
|
+
} catch {
|
|
34504
|
+
throw createValidationFailedError$1('preparedBundle.erc3009TypedData.domain.verifyingContract', sourceTokenAddress, `source token ${sourceTokenAddress} is not configured on source chain ${sourceChain.name} for cross-chain Earn deposits`);
|
|
34505
|
+
}
|
|
34506
|
+
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.`);
|
|
34507
|
+
if (!isSameAddress(sourceTokenAddress, registryAddress)) {
|
|
34508
|
+
throw createValidationFailedError$1('preparedBundle.erc3009TypedData.domain.verifyingContract', sourceTokenAddress, `source token must match the configured ${sourceToken.symbol ?? 'token'} address on source chain ${sourceChain.name}`);
|
|
34509
|
+
}
|
|
34510
|
+
const { symbol } = sourceToken;
|
|
34511
|
+
if (symbol === undefined) {
|
|
34512
|
+
throw createValidationFailedError$1('preparedBundle.erc3009TypedData.domain.verifyingContract', sourceTokenAddress, `source token ${sourceTokenAddress} is not a registered Earn bridge token on source chain ${sourceChain.name}`);
|
|
34513
|
+
}
|
|
34514
|
+
return {
|
|
34515
|
+
address: sourceTokenAddress,
|
|
34516
|
+
erc3009Domain: resolveBridgeErc3009Domain(symbol, sourceChain),
|
|
34517
|
+
decimals: sourceToken.decimals
|
|
34518
|
+
};
|
|
34519
|
+
}
|
|
34520
|
+
function validatePreparedReviewAmountDecimals(reviewedDecimals, tokenDecimals) {
|
|
34521
|
+
if (reviewedDecimals !== tokenDecimals) {
|
|
34522
|
+
throw createValidationFailedError$1('prepared.review.amount.decimals', reviewedDecimals, `review amount decimals must match the cross-chain Earn deposit source token decimals ${String(tokenDecimals)}`);
|
|
34523
|
+
}
|
|
34524
|
+
}
|
|
34525
|
+
function resolveBridgeErc3009Domain(tokenSymbol, sourceChain) {
|
|
34526
|
+
if (!isEarnBridgeErc3009TokenSymbol(tokenSymbol)) {
|
|
34527
|
+
throw createValidationFailedError$1('sourceToken.symbol', tokenSymbol, `ERC-3009 domain is not configured for ${tokenSymbol} on source chain ${sourceChain.name}`);
|
|
34528
|
+
}
|
|
34529
|
+
const domainsByChain = EARN_BRIDGE_ERC3009_DOMAINS[tokenSymbol];
|
|
34530
|
+
const domain = domainsByChain[sourceChain.chain];
|
|
34531
|
+
if (domain === undefined) {
|
|
34532
|
+
throw createValidationFailedError$1('sourceToken.symbol', tokenSymbol, `ERC-3009 domain is not configured for ${tokenSymbol} on source chain ${sourceChain.name}`);
|
|
34533
|
+
}
|
|
34534
|
+
return domain;
|
|
34535
|
+
}
|
|
34536
|
+
function isEarnBridgeErc3009TokenSymbol(tokenSymbol) {
|
|
34537
|
+
return EARN_BRIDGE_ERC3009_TOKEN_SYMBOLS.includes(tokenSymbol);
|
|
34538
|
+
}
|
|
34411
34539
|
function createUnsupportedCrossChainDepositError(chain, reason) {
|
|
34412
34540
|
return new KitError({
|
|
34413
34541
|
...EarnError.UNSUPPORTED_CHAIN,
|
|
@@ -34480,7 +34608,6 @@ function createUnsupportedCrossChainDepositError(chain, reason) {
|
|
|
34480
34608
|
// Untyped callers can pass anything here; without these guards a null
|
|
34481
34609
|
// config crashes with a raw TypeError on `config.providers` instead of
|
|
34482
34610
|
// 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
34611
|
if (typeof config !== 'object' || config === null || Array.isArray(config)) {
|
|
34485
34612
|
throw createValidationFailedError$1('config', config, 'config must be a plain object when provided');
|
|
34486
34613
|
}
|
|
@@ -34847,10 +34974,9 @@ const sourceAdapterContextSchema = z.object({
|
|
|
34847
34974
|
* Schema for validating human-readable decimal amount strings.
|
|
34848
34975
|
*
|
|
34849
34976
|
* 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.
|
|
34977
|
+
* zero, negative, non-numeric, and non-canonical strings. Same-chain
|
|
34978
|
+
* EarnKit vaults can use different asset precisions, so the service enforces
|
|
34979
|
+
* the vault-specific decimal cap after resolving the vault asset.
|
|
34854
34980
|
*
|
|
34855
34981
|
* @internal
|
|
34856
34982
|
*/ const amountSchema$1 = z.string({
|
|
@@ -34859,32 +34985,15 @@ const sourceAdapterContextSchema = z.object({
|
|
|
34859
34985
|
allowZero: false,
|
|
34860
34986
|
regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
|
|
34861
34987
|
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
|
|
34988
|
+
maxDecimals: 18
|
|
34881
34989
|
})(z.string())).refine((value)=>CANONICAL_AMOUNT_REGEX.test(value), AMOUNT_FORMAT_ERROR_MESSAGE);
|
|
34882
34990
|
/**
|
|
34883
34991
|
* Schema for cross-chain source fee caps.
|
|
34884
34992
|
*
|
|
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.
|
|
34993
|
+
* Cross-chain source fees are denominated in the quote's fee token. Zero is
|
|
34994
|
+
* valid when the quote contains no source-collected fees; otherwise the cap
|
|
34995
|
+
* should be the caller-accepted total from a recent quote. The provider
|
|
34996
|
+
* enforces the prepared bundle's fee-token precision before signing.
|
|
34888
34997
|
*
|
|
34889
34998
|
* @internal
|
|
34890
34999
|
*/ const crossChainMaxFeeSchema = z.string({
|
|
@@ -34893,7 +35002,7 @@ const sourceAdapterContextSchema = z.object({
|
|
|
34893
35002
|
allowZero: true,
|
|
34894
35003
|
regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
|
|
34895
35004
|
attributeName: 'maxFee',
|
|
34896
|
-
maxDecimals:
|
|
35005
|
+
maxDecimals: 18
|
|
34897
35006
|
})(z.string())).refine((value)=>CANONICAL_AMOUNT_REGEX.test(value), AMOUNT_FORMAT_ERROR_MESSAGE);
|
|
34898
35007
|
/**
|
|
34899
35008
|
* Build a schema for an optional non-negative decimal filter string.
|
|
@@ -35054,7 +35163,7 @@ const crossChainDepositDestinationSchema = z.object({
|
|
|
35054
35163
|
from: sourceAdapterContextSchema,
|
|
35055
35164
|
to: crossChainDepositDestinationSchema,
|
|
35056
35165
|
vaultAddress: vaultAddressSchema,
|
|
35057
|
-
amount:
|
|
35166
|
+
amount: amountSchema$1,
|
|
35058
35167
|
maxFee: crossChainMaxFeeSchema,
|
|
35059
35168
|
transferSpeed: z.enum([
|
|
35060
35169
|
'FAST',
|
|
@@ -37794,7 +37903,7 @@ async function deposit$2(context, params) {
|
|
|
37794
37903
|
}
|
|
37795
37904
|
|
|
37796
37905
|
var name = "@circle-fin/unified-balance-kit";
|
|
37797
|
-
var version = "1.2.
|
|
37906
|
+
var version = "1.2.1";
|
|
37798
37907
|
var pkg = {
|
|
37799
37908
|
name: name,
|
|
37800
37909
|
version: version};
|