@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.cjs
CHANGED
|
@@ -3657,7 +3657,7 @@ function getOptionalString(value) {
|
|
|
3657
3657
|
* - vault-not-found, unsupported-chain, unsupported-vault,
|
|
3658
3658
|
* signature-rejected, invalid-id, invalid-batch-size, position-not-registered,
|
|
3659
3659
|
* withdrawal-max-exceeded, insufficient-balance, bridge prepare idempotency
|
|
3660
|
-
* conflicts
|
|
3660
|
+
* conflicts, invalid vault-asset amount precision
|
|
3661
3661
|
*
|
|
3662
3662
|
* SERVICE errors (RETRYABLE) — try again later:
|
|
3663
3663
|
* - signing-failed, provider-error, rewards-fetch-failed,
|
|
@@ -3841,6 +3841,13 @@ function getOptionalString(value) {
|
|
|
3841
3841
|
recoverability: 'RETRYABLE'
|
|
3842
3842
|
}
|
|
3843
3843
|
],
|
|
3844
|
+
[
|
|
3845
|
+
380415,
|
|
3846
|
+
{
|
|
3847
|
+
errorDef: EarnError.INVALID_INPUT,
|
|
3848
|
+
recoverability: 'FATAL'
|
|
3849
|
+
}
|
|
3850
|
+
],
|
|
3844
3851
|
// Bridge (380_5XX)
|
|
3845
3852
|
[
|
|
3846
3853
|
380500,
|
|
@@ -3876,6 +3883,13 @@ function getOptionalString(value) {
|
|
|
3876
3883
|
errorDef: EarnError.PROVIDER_ERROR,
|
|
3877
3884
|
recoverability: 'RETRYABLE'
|
|
3878
3885
|
}
|
|
3886
|
+
],
|
|
3887
|
+
[
|
|
3888
|
+
380505,
|
|
3889
|
+
{
|
|
3890
|
+
errorDef: EarnError.PROVIDER_ERROR,
|
|
3891
|
+
recoverability: 'FATAL'
|
|
3892
|
+
}
|
|
3879
3893
|
]
|
|
3880
3894
|
]);
|
|
3881
3895
|
/**
|
|
@@ -4335,23 +4349,6 @@ exports.EarnChain = void 0;
|
|
|
4335
4349
|
}
|
|
4336
4350
|
});
|
|
4337
4351
|
|
|
4338
|
-
/**
|
|
4339
|
-
* Standard decimal places for tokens.
|
|
4340
|
-
*
|
|
4341
|
-
* These constants define the decimal precision used by different token types
|
|
4342
|
-
* across the SDK. They are used for amount conversions between human-readable
|
|
4343
|
-
* format and smallest units (base units).
|
|
4344
|
-
*
|
|
4345
|
-
* @remarks
|
|
4346
|
-
* - Most stablecoins (USDC, EURC, USDT, PYUSD) use 6 decimal places
|
|
4347
|
-
* - Some stablecoins (DAI, USDE) and most native tokens (ETH, POL, PLUME) use 18 decimals
|
|
4348
|
-
* - The NATIVE alias uses chain-specific decimals which may vary (e.g., SOL uses 9)
|
|
4349
|
-
*/ /**
|
|
4350
|
-
* Standard decimal places for 6-decimal tokens.
|
|
4351
|
-
*
|
|
4352
|
-
* Used by most stablecoins: USDC, EURC, USDT, PYUSD
|
|
4353
|
-
*/ const TOKEN_DECIMALS_6 = 6;
|
|
4354
|
-
|
|
4355
4352
|
/**
|
|
4356
4353
|
* @packageDocumentation
|
|
4357
4354
|
* @module SwapTokenRegistry
|
|
@@ -9992,13 +9989,20 @@ function parseOrThrow(value, schema, context) {
|
|
|
9992
9989
|
symbol: 'EURC',
|
|
9993
9990
|
decimals: 6,
|
|
9994
9991
|
locators: {
|
|
9992
|
+
// =========================================================================
|
|
9993
|
+
// Mainnets
|
|
9994
|
+
// =========================================================================
|
|
9995
9995
|
[exports.Blockchain.Avalanche]: '0xc891EB4cbdEFf6e073e859e987815Ed1505c2ACD',
|
|
9996
9996
|
[exports.Blockchain.Base]: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42',
|
|
9997
9997
|
[exports.Blockchain.Ethereum]: '0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c',
|
|
9998
9998
|
[exports.Blockchain.Solana]: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr',
|
|
9999
9999
|
[exports.Blockchain.World_Chain]: '0x1C60ba0A0eD1019e8Eb035E6daF4155A5cE2380B',
|
|
10000
|
+
// =========================================================================
|
|
10000
10001
|
// Testnets
|
|
10001
|
-
|
|
10002
|
+
// =========================================================================
|
|
10003
|
+
[exports.Blockchain.Arc_Testnet]: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
|
|
10004
|
+
[exports.Blockchain.Base_Sepolia]: '0x808456652fdb597867f38412077A9182bf77359F',
|
|
10005
|
+
[exports.Blockchain.Ethereum_Sepolia]: '0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4'
|
|
10002
10006
|
}
|
|
10003
10007
|
};
|
|
10004
10008
|
|
|
@@ -10208,8 +10212,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
10208
10212
|
* cirBTC (Circle Bitcoin) token definition with addresses and metadata.
|
|
10209
10213
|
*
|
|
10210
10214
|
* @remarks
|
|
10211
|
-
* Built-in cirBTC definition for the TokenRegistry.
|
|
10212
|
-
* on Arc Testnet.
|
|
10215
|
+
* Built-in cirBTC definition for the TokenRegistry.
|
|
10213
10216
|
*
|
|
10214
10217
|
* @example
|
|
10215
10218
|
* ```typescript
|
|
@@ -10225,7 +10228,15 @@ function parseOrThrow(value, schema, context) {
|
|
|
10225
10228
|
symbol: 'cirBTC',
|
|
10226
10229
|
decimals: 8,
|
|
10227
10230
|
locators: {
|
|
10228
|
-
|
|
10231
|
+
// =========================================================================
|
|
10232
|
+
// Mainnets
|
|
10233
|
+
// =========================================================================
|
|
10234
|
+
[exports.Blockchain.Ethereum]: '0x72DFB2E44f59C5AD2bAFE84314E5b99a7cd5075E',
|
|
10235
|
+
// =========================================================================
|
|
10236
|
+
// Testnets
|
|
10237
|
+
// =========================================================================
|
|
10238
|
+
[exports.Blockchain.Arc_Testnet]: '0xf0C4a4CE82A5746AbAAd9425360Ab04fbBA432BF',
|
|
10239
|
+
[exports.Blockchain.Ethereum_Sepolia]: '0x3a3fe695F684Bf9b9e43CF43C2b895Ea5e392bB3'
|
|
10229
10240
|
}
|
|
10230
10241
|
};
|
|
10231
10242
|
|
|
@@ -11506,7 +11517,7 @@ function resolveOptions(options) {
|
|
|
11506
11517
|
}
|
|
11507
11518
|
|
|
11508
11519
|
var name$3 = "@circle-fin/bridge-kit";
|
|
11509
|
-
var version$4 = "1.11.
|
|
11520
|
+
var version$4 = "1.11.1";
|
|
11510
11521
|
var pkg$4 = {
|
|
11511
11522
|
name: name$3,
|
|
11512
11523
|
version: version$4};
|
|
@@ -16297,7 +16308,7 @@ const mockAttestationMessage = {
|
|
|
16297
16308
|
return step;
|
|
16298
16309
|
}
|
|
16299
16310
|
|
|
16300
|
-
var version$3 = "1.8.
|
|
16311
|
+
var version$3 = "1.8.5";
|
|
16301
16312
|
var pkg$3 = {
|
|
16302
16313
|
version: version$3};
|
|
16303
16314
|
|
|
@@ -18704,7 +18715,7 @@ registerKit(`${pkg$4.name}/${pkg$4.version}`);
|
|
|
18704
18715
|
};
|
|
18705
18716
|
|
|
18706
18717
|
var name$2 = "@circle-fin/swap-kit";
|
|
18707
|
-
var version$2 = "1.3.
|
|
18718
|
+
var version$2 = "1.3.1";
|
|
18708
18719
|
var pkg$2 = {
|
|
18709
18720
|
name: name$2,
|
|
18710
18721
|
version: version$2};
|
|
@@ -23639,7 +23650,7 @@ function writeBytes32(buffer, hex, offset) {
|
|
|
23639
23650
|
}
|
|
23640
23651
|
}
|
|
23641
23652
|
|
|
23642
|
-
const TOKEN_REGISTRY$
|
|
23653
|
+
const TOKEN_REGISTRY$4 = createTokenRegistry();
|
|
23643
23654
|
/**
|
|
23644
23655
|
* Resolve token alias to contract address for the given chain.
|
|
23645
23656
|
*
|
|
@@ -23728,7 +23739,7 @@ const TOKEN_REGISTRY$3 = createTokenRegistry();
|
|
|
23728
23739
|
}
|
|
23729
23740
|
});
|
|
23730
23741
|
}
|
|
23731
|
-
const tokenDefinition = TOKEN_REGISTRY$
|
|
23742
|
+
const tokenDefinition = TOKEN_REGISTRY$4.get(upperToken);
|
|
23732
23743
|
if (tokenDefinition !== undefined) {
|
|
23733
23744
|
const resolvedAddress = tokenDefinition.locators[chain.chain];
|
|
23734
23745
|
if (!resolvedAddress) {
|
|
@@ -23745,7 +23756,7 @@ const TOKEN_REGISTRY$3 = createTokenRegistry();
|
|
|
23745
23756
|
}
|
|
23746
23757
|
});
|
|
23747
23758
|
}
|
|
23748
|
-
return TOKEN_REGISTRY$
|
|
23759
|
+
return TOKEN_REGISTRY$4.resolve(upperToken, chain.chain).locator;
|
|
23749
23760
|
}
|
|
23750
23761
|
// Not an alias - return as-is (assumed to be address)
|
|
23751
23762
|
return token;
|
|
@@ -23842,11 +23853,11 @@ function resolveFeePayoutChain(tokenIn, tokenOut, sourceChain, destinationChain)
|
|
|
23842
23853
|
if (destinationChain.chain !== sourceChain.chain) {
|
|
23843
23854
|
return sourceChain;
|
|
23844
23855
|
}
|
|
23845
|
-
const inputIsOk = isOkToken(tokenIn, sourceChain, TOKEN_REGISTRY$
|
|
23856
|
+
const inputIsOk = isOkToken(tokenIn, sourceChain, TOKEN_REGISTRY$4, OK_TOKEN_SYMBOLS$1);
|
|
23846
23857
|
if (inputIsOk) {
|
|
23847
23858
|
return sourceChain;
|
|
23848
23859
|
}
|
|
23849
|
-
const outputIsOk = isOkToken(tokenOut, destinationChain, TOKEN_REGISTRY$
|
|
23860
|
+
const outputIsOk = isOkToken(tokenOut, destinationChain, TOKEN_REGISTRY$4, OK_TOKEN_SYMBOLS$1);
|
|
23850
23861
|
return outputIsOk ? destinationChain : sourceChain;
|
|
23851
23862
|
}
|
|
23852
23863
|
/**
|
|
@@ -25137,7 +25148,7 @@ function resolveFeePayoutChain(tokenIn, tokenOut, sourceChain, destinationChain)
|
|
|
25137
25148
|
}
|
|
25138
25149
|
}
|
|
25139
25150
|
|
|
25140
|
-
const TOKEN_REGISTRY$
|
|
25151
|
+
const TOKEN_REGISTRY$3 = createTokenRegistry();
|
|
25141
25152
|
/**
|
|
25142
25153
|
* Resolve a token identifier to its canonical symbol for the Stablecoin Service Swap Provider.
|
|
25143
25154
|
*
|
|
@@ -25181,7 +25192,7 @@ const TOKEN_REGISTRY$2 = createTokenRegistry();
|
|
|
25181
25192
|
return NATIVE_TOKEN;
|
|
25182
25193
|
}
|
|
25183
25194
|
// For known registry aliases, return canonical symbol directly.
|
|
25184
|
-
if (TOKEN_REGISTRY$
|
|
25195
|
+
if (TOKEN_REGISTRY$3.get(upperToken) !== undefined) {
|
|
25185
25196
|
return upperToken;
|
|
25186
25197
|
}
|
|
25187
25198
|
// For EVM chains, normalize addresses to lowercase for case-insensitive comparison.
|
|
@@ -25191,7 +25202,7 @@ const TOKEN_REGISTRY$2 = createTokenRegistry();
|
|
|
25191
25202
|
// Native address is given
|
|
25192
25203
|
if (isNativeEvmAddress(token) || isNativeSolanaAddress(token)) return NATIVE_TOKEN;
|
|
25193
25204
|
try {
|
|
25194
|
-
const { symbol } = TOKEN_REGISTRY$
|
|
25205
|
+
const { symbol } = TOKEN_REGISTRY$3.resolveByAddress(normalizedToken, chain.chain);
|
|
25195
25206
|
if (symbol === undefined) {
|
|
25196
25207
|
return null;
|
|
25197
25208
|
}
|
|
@@ -25322,7 +25333,7 @@ const TOKEN_REGISTRY$2 = createTokenRegistry();
|
|
|
25322
25333
|
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.`;
|
|
25323
25334
|
}
|
|
25324
25335
|
|
|
25325
|
-
const TOKEN_REGISTRY$
|
|
25336
|
+
const TOKEN_REGISTRY$2 = createTokenRegistry();
|
|
25326
25337
|
/**
|
|
25327
25338
|
* Format a raw base-unit amount into a human-readable decimal string.
|
|
25328
25339
|
*
|
|
@@ -25382,12 +25393,12 @@ const TOKEN_REGISTRY$1 = createTokenRegistry();
|
|
|
25382
25393
|
value,
|
|
25383
25394
|
token: resolvedSymbol,
|
|
25384
25395
|
chain,
|
|
25385
|
-
tokens: TOKEN_REGISTRY$
|
|
25396
|
+
tokens: TOKEN_REGISTRY$2
|
|
25386
25397
|
}),
|
|
25387
25398
|
token: resolvedSymbol
|
|
25388
25399
|
};
|
|
25389
25400
|
}
|
|
25390
|
-
const tokenDef = TOKEN_REGISTRY$
|
|
25401
|
+
const tokenDef = TOKEN_REGISTRY$2.get(resolvedSymbol);
|
|
25391
25402
|
if (tokenDef) {
|
|
25392
25403
|
const decimals = resolveTokenDecimals$1(tokenDef, chain.chain);
|
|
25393
25404
|
return {
|
|
@@ -25418,7 +25429,7 @@ const TOKEN_REGISTRY$1 = createTokenRegistry();
|
|
|
25418
25429
|
* i.e. 1 + 0.2 + 0.1 = 1.3. Use the greater of (local estimate × this multiplier)
|
|
25419
25430
|
* or the proxy's gasLimit.
|
|
25420
25431
|
*/ const GAS_SAFETY_MULTIPLIER = 1.3;
|
|
25421
|
-
const TOKEN_REGISTRY = createTokenRegistry();
|
|
25432
|
+
const TOKEN_REGISTRY$1 = createTokenRegistry();
|
|
25422
25433
|
/**
|
|
25423
25434
|
* Enhances a KitError with transaction details (txHash and explorerUrl).
|
|
25424
25435
|
*
|
|
@@ -25494,6 +25505,19 @@ const TOKEN_REGISTRY = createTokenRegistry();
|
|
|
25494
25505
|
*
|
|
25495
25506
|
* @internal
|
|
25496
25507
|
*/ const SUPPORTED_CHAINS$1 = getSwapSupportedChains(Chains);
|
|
25508
|
+
/**
|
|
25509
|
+
* Inter-poll delays (ms) for the same-chain `amountOut` enrichment loop.
|
|
25510
|
+
*
|
|
25511
|
+
* A same-chain swap is already terminal `DONE` once its source transaction
|
|
25512
|
+
* confirms; this short, escalating budget (~5s total across up to four polls)
|
|
25513
|
+
* gives the service a brief window to resolve the output amount before
|
|
25514
|
+
* `swap()` returns. The loop exits the instant `amountOut` is available.
|
|
25515
|
+
*/ const SAME_CHAIN_AMOUNT_OUT_POLL_DELAYS_MS = [
|
|
25516
|
+
1_000,
|
|
25517
|
+
2_000,
|
|
25518
|
+
2_000
|
|
25519
|
+
];
|
|
25520
|
+
const sleep$3 = async (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
|
|
25497
25521
|
function buildSwapProgress(statusResult) {
|
|
25498
25522
|
return {
|
|
25499
25523
|
status: statusResult.status,
|
|
@@ -25513,29 +25537,46 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
25513
25537
|
}
|
|
25514
25538
|
};
|
|
25515
25539
|
}
|
|
25516
|
-
|
|
25517
|
-
|
|
25518
|
-
|
|
25519
|
-
|
|
25520
|
-
|
|
25521
|
-
|
|
25522
|
-
|
|
25523
|
-
|
|
25540
|
+
// A same-chain swap completes atomically in the source transaction, which
|
|
25541
|
+
// has already been confirmed (and receipt-checked for revert) by the time we
|
|
25542
|
+
// get here. Its terminal status is therefore `DONE`. We briefly poll the
|
|
25543
|
+
// status endpoint to enrich `amountOut`, which the service usually resolves
|
|
25544
|
+
// within a few seconds. A non-DONE/erroneous peek never downgrades the
|
|
25545
|
+
// status — once the short budget is exhausted we return `DONE` without
|
|
25546
|
+
// `amountOut`, and the caller can fetch it later via `getSwapStatus`.
|
|
25547
|
+
for(let attempt = 0;; attempt += 1){
|
|
25548
|
+
try {
|
|
25549
|
+
const statusResult = await getSwapStatus$2({
|
|
25550
|
+
txHash,
|
|
25551
|
+
chain: chain.chain,
|
|
25552
|
+
apiKey
|
|
25553
|
+
});
|
|
25554
|
+
if (statusResult.status === 'DONE' && statusResult.amountOut !== undefined) {
|
|
25555
|
+
return {
|
|
25556
|
+
progress: buildSwapProgress(statusResult),
|
|
25557
|
+
amountOut: statusResult.amountOut
|
|
25558
|
+
};
|
|
25559
|
+
}
|
|
25560
|
+
} catch {
|
|
25561
|
+
// Reachability/parse failure during enrichment — non-fatal; the swap
|
|
25562
|
+
// already succeeded on-chain. `getSwapStatus` already retried transient
|
|
25563
|
+
// network errors internally, so further polling is unlikely to help.
|
|
25524
25564
|
return {
|
|
25525
|
-
progress
|
|
25526
|
-
|
|
25565
|
+
progress: {
|
|
25566
|
+
status: 'DONE'
|
|
25567
|
+
}
|
|
25527
25568
|
};
|
|
25528
25569
|
}
|
|
25529
|
-
|
|
25530
|
-
|
|
25531
|
-
|
|
25532
|
-
|
|
25533
|
-
|
|
25534
|
-
|
|
25535
|
-
|
|
25536
|
-
|
|
25537
|
-
|
|
25538
|
-
|
|
25570
|
+
const delayMs = SAME_CHAIN_AMOUNT_OUT_POLL_DELAYS_MS[attempt];
|
|
25571
|
+
if (delayMs === undefined) {
|
|
25572
|
+
// Budget exhausted — terminal `DONE`, `amountOut` not yet available.
|
|
25573
|
+
return {
|
|
25574
|
+
progress: {
|
|
25575
|
+
status: 'DONE'
|
|
25576
|
+
}
|
|
25577
|
+
};
|
|
25578
|
+
}
|
|
25579
|
+
await sleep$3(delayMs);
|
|
25539
25580
|
}
|
|
25540
25581
|
}
|
|
25541
25582
|
/**
|
|
@@ -25816,7 +25857,7 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
25816
25857
|
return true;
|
|
25817
25858
|
}
|
|
25818
25859
|
// At least one token must be supported for fee collection
|
|
25819
|
-
return getSwapOkTokenStatus(tokenInAddress, tokenOutAddress, chain, TOKEN_REGISTRY, OK_TOKEN_SYMBOLS$1).hasOkToken;
|
|
25860
|
+
return getSwapOkTokenStatus(tokenInAddress, tokenOutAddress, chain, TOKEN_REGISTRY$1, OK_TOKEN_SYMBOLS$1).hasOkToken;
|
|
25820
25861
|
}
|
|
25821
25862
|
/**
|
|
25822
25863
|
* Estimate swap costs and output amount by delegating to the Stablecoin Service quote API.
|
|
@@ -26413,14 +26454,14 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
26413
26454
|
}
|
|
26414
26455
|
// Build swap fees with adapter-backed formatting for unregistered tokens
|
|
26415
26456
|
const swapResultFees = serviceResponse.fees ? await this.buildFormattedFees(serviceResponse.fees, chain, destinationChain, adapter, config?.customFee?.recipientAddress) : undefined;
|
|
26416
|
-
// Best-effort enrichment:
|
|
26417
|
-
//
|
|
26418
|
-
//
|
|
26419
|
-
//
|
|
26420
|
-
// destination mint takes minutes (CCTPv2 attestation),
|
|
26421
|
-
//
|
|
26422
|
-
//
|
|
26423
|
-
//
|
|
26457
|
+
// Best-effort enrichment: briefly poll for amountOut on same-chain swaps.
|
|
26458
|
+
// They complete atomically in the (already-confirmed) source tx, so the
|
|
26459
|
+
// result is terminal `DONE` regardless; the short poll only adds amountOut
|
|
26460
|
+
// once the service has resolved it. Cross-chain swaps are skipped because
|
|
26461
|
+
// the destination mint takes minutes (CCTPv2 attestation), so polling would
|
|
26462
|
+
// always return PENDING/WAIT_DESTINATION_TRANSACTION while burning the
|
|
26463
|
+
// budget. Callers should use `kit.getSwapStatus()` to poll cross-chain
|
|
26464
|
+
// swaps to DONE.
|
|
26424
26465
|
const statusSnapshot = await fetchSameChainStatusSnapshot({
|
|
26425
26466
|
isCrossChainSwap,
|
|
26426
26467
|
txHash,
|
|
@@ -30722,11 +30763,14 @@ registerKit(`${pkg$2.name}/${pkg$2.version}`);
|
|
|
30722
30763
|
};
|
|
30723
30764
|
|
|
30724
30765
|
var name$1 = "@circle-fin/earn-kit";
|
|
30725
|
-
var version$1 = "1.2.
|
|
30766
|
+
var version$1 = "1.2.1";
|
|
30726
30767
|
var pkg$1 = {
|
|
30727
30768
|
name: name$1,
|
|
30728
30769
|
version: version$1};
|
|
30729
30770
|
|
|
30771
|
+
const EARN_BRIDGE_ERC3009_TOKEN_SYMBOLS = [
|
|
30772
|
+
'USDC'
|
|
30773
|
+
];
|
|
30730
30774
|
/**
|
|
30731
30775
|
* Default base URL for the Earn Service API.
|
|
30732
30776
|
*
|
|
@@ -30771,20 +30815,7 @@ var pkg$1 = {
|
|
|
30771
30815
|
*/ const EARN_BRIDGE_DESTINATION_CHAIN_TO_API = {
|
|
30772
30816
|
[exports.Blockchain.Arc_Testnet]: CHAIN_TO_API[exports.Blockchain.Arc_Testnet]
|
|
30773
30817
|
};
|
|
30774
|
-
|
|
30775
|
-
* Expected EIP-712 domain of the ERC-3009 authorization token per source
|
|
30776
|
-
* chain.
|
|
30777
|
-
*
|
|
30778
|
-
* Cross-chain Earn deposits are USDC-only, but USDC deployments differ on the
|
|
30779
|
-
* EIP-712 domain name: newer deployments use `USDC` while older ones (for
|
|
30780
|
-
* example Arbitrum Sepolia) kept `USD Coin` from the FiatToken V2 upgrade.
|
|
30781
|
-
* These values mirror the bridge service's per-chain domain table and the
|
|
30782
|
-
* on-chain `DOMAIN_SEPARATOR` inputs. Asserted before signing so a tampered
|
|
30783
|
-
* prepare response cannot point the signature at another token. The
|
|
30784
|
-
* `satisfies` check forces a domain entry whenever a source chain is added.
|
|
30785
|
-
*
|
|
30786
|
-
* @internal
|
|
30787
|
-
*/ const EARN_BRIDGE_ERC3009_DOMAINS = {
|
|
30818
|
+
const EARN_BRIDGE_USDC_ERC3009_DOMAINS = {
|
|
30788
30819
|
[exports.Blockchain.Arbitrum_Sepolia]: {
|
|
30789
30820
|
name: 'USD Coin',
|
|
30790
30821
|
version: '2'
|
|
@@ -30798,6 +30829,21 @@ var pkg$1 = {
|
|
|
30798
30829
|
version: '2'
|
|
30799
30830
|
}
|
|
30800
30831
|
};
|
|
30832
|
+
/**
|
|
30833
|
+
* Expected EIP-712 domain of the ERC-3009 authorization token per
|
|
30834
|
+
* cross-chain-supported source token and source chain.
|
|
30835
|
+
*
|
|
30836
|
+
* The bridge service prepares an ERC-3009 payload for the deposited token. We
|
|
30837
|
+
* keep this table as an explicit SDK allowlist because token deployments can
|
|
30838
|
+
* differ by source chain and by domain name/version. These values mirror the
|
|
30839
|
+
* bridge service's per-chain domain table and the on-chain DOMAIN_SEPARATOR
|
|
30840
|
+
* inputs. Asserted before signing so a tampered prepare response cannot point
|
|
30841
|
+
* the signature at another token or unexpected token domain.
|
|
30842
|
+
*
|
|
30843
|
+
* @internal
|
|
30844
|
+
*/ const EARN_BRIDGE_ERC3009_DOMAINS = {
|
|
30845
|
+
USDC: EARN_BRIDGE_USDC_ERC3009_DOMAINS
|
|
30846
|
+
};
|
|
30801
30847
|
/**
|
|
30802
30848
|
* Maximum tolerated remaining lifetime of a prepared ERC-3009 authorization.
|
|
30803
30849
|
*
|
|
@@ -31257,6 +31303,16 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
31257
31303
|
return approvalTxHash;
|
|
31258
31304
|
}
|
|
31259
31305
|
|
|
31306
|
+
/** @internal */ function isSameAddress(actual, expected) {
|
|
31307
|
+
return actual.toLowerCase() === expected.toLowerCase();
|
|
31308
|
+
}
|
|
31309
|
+
/** @internal */ function assertDecimalPlaces(field, value, tokenDecimals, reason) {
|
|
31310
|
+
const decimals = value.split('.')[1]?.length ?? 0;
|
|
31311
|
+
if (decimals > tokenDecimals) {
|
|
31312
|
+
throw createValidationFailedError$1(field, value, reason);
|
|
31313
|
+
}
|
|
31314
|
+
}
|
|
31315
|
+
|
|
31260
31316
|
/**
|
|
31261
31317
|
* Build the `tokenInputs` array forwarded to the adapter contract's
|
|
31262
31318
|
* `execute(executeParams, tokenInputs, signature)` call.
|
|
@@ -31280,15 +31336,17 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
31280
31336
|
*
|
|
31281
31337
|
* @example
|
|
31282
31338
|
* ```typescript
|
|
31283
|
-
* const
|
|
31284
|
-
* 'chain.usdcAddress',
|
|
31285
|
-
* chain.usdcAddress,
|
|
31286
|
-
* )
|
|
31287
|
-
*
|
|
31288
|
-
* const tokenInputs = buildEarnTokenInputs(
|
|
31339
|
+
* const approvalToken = resolveEarnApprovalToken(
|
|
31289
31340
|
* executionPayload.executionParams,
|
|
31290
|
-
* approvedToken,
|
|
31291
31341
|
* )
|
|
31342
|
+
*
|
|
31343
|
+
* const tokenInputs =
|
|
31344
|
+
* approvalToken === undefined
|
|
31345
|
+
* ? []
|
|
31346
|
+
* : buildEarnTokenInputs(
|
|
31347
|
+
* executionPayload.executionParams,
|
|
31348
|
+
* approvalToken,
|
|
31349
|
+
* )
|
|
31292
31350
|
* ```
|
|
31293
31351
|
*
|
|
31294
31352
|
* @internal
|
|
@@ -31300,7 +31358,7 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
31300
31358
|
return;
|
|
31301
31359
|
}
|
|
31302
31360
|
const { tokenIn } = instruction;
|
|
31303
|
-
if (tokenIn
|
|
31361
|
+
if (!isSameAddress(tokenIn, approvedToken)) {
|
|
31304
31362
|
throw createValidationFailedError$1(`executionParams.instructions[${index.toString()}].tokenIn`, tokenIn, 'tokenIn must match the token approved for adapter spending');
|
|
31305
31363
|
}
|
|
31306
31364
|
tokenInputs.push({
|
|
@@ -31312,6 +31370,33 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
31312
31370
|
});
|
|
31313
31371
|
return tokenInputs;
|
|
31314
31372
|
}
|
|
31373
|
+
/**
|
|
31374
|
+
* Resolve the single token that needs adapter allowance from signed Earn
|
|
31375
|
+
* instructions.
|
|
31376
|
+
*
|
|
31377
|
+
* @param executionParams - Service-signed `ExecutionParams` forwarded to the adapter.
|
|
31378
|
+
* @returns The token requested by positive approval instructions, or `undefined`.
|
|
31379
|
+
* @throws {@link KitError} If positive approval instructions reference multiple tokens.
|
|
31380
|
+
*
|
|
31381
|
+
* @internal
|
|
31382
|
+
*/ function resolveEarnApprovalToken(executionParams) {
|
|
31383
|
+
let approvedToken;
|
|
31384
|
+
executionParams.instructions.forEach((instruction, index)=>{
|
|
31385
|
+
const amount = BigInt(instruction.amountToApprove);
|
|
31386
|
+
if (amount <= 0n) {
|
|
31387
|
+
return;
|
|
31388
|
+
}
|
|
31389
|
+
const { tokenIn } = instruction;
|
|
31390
|
+
if (approvedToken === undefined) {
|
|
31391
|
+
approvedToken = tokenIn;
|
|
31392
|
+
return;
|
|
31393
|
+
}
|
|
31394
|
+
if (!isSameAddress(tokenIn, approvedToken)) {
|
|
31395
|
+
throw createValidationFailedError$1(`executionParams.instructions[${index.toString()}].tokenIn`, tokenIn, 'tokenIn must match the token approved for adapter spending');
|
|
31396
|
+
}
|
|
31397
|
+
});
|
|
31398
|
+
return approvedToken;
|
|
31399
|
+
}
|
|
31315
31400
|
|
|
31316
31401
|
/**
|
|
31317
31402
|
* Prepare an earn adapter action, execute it, wait for confirmation, and
|
|
@@ -31803,6 +31888,7 @@ const hexSignatureSchema = evmSignatureSchema;
|
|
|
31803
31888
|
const hexAddressSchema = evmAddressSchema;
|
|
31804
31889
|
// '0x' prefix + 32 bytes * 2 hex chars.
|
|
31805
31890
|
const BYTES32_HEX_LENGTH = 66;
|
|
31891
|
+
const bridgeFeeTokenSchema = hexAddressSchema;
|
|
31806
31892
|
/**
|
|
31807
31893
|
* Zod schema for a non-negative uint256-like value.
|
|
31808
31894
|
*
|
|
@@ -32089,14 +32175,12 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
32089
32175
|
*
|
|
32090
32176
|
* The bridge prepare path returns one item per collected source fee (e.g.
|
|
32091
32177
|
* `PRE_FINALITY` for the fast-burn fee and `FORWARD` for the destination
|
|
32092
|
-
* forward fee), all denominated in the shared `feeToken`.
|
|
32093
|
-
*
|
|
32094
|
-
* guard parses these to recompute the expected value. Each item's `amount` is
|
|
32095
|
-
* a base-unit decimal string in `feeToken` units.
|
|
32178
|
+
* forward fee), all denominated in the shared `feeToken`. Each item's `amount`
|
|
32179
|
+
* is a base-unit decimal string in `feeToken` units.
|
|
32096
32180
|
*
|
|
32097
32181
|
* @internal
|
|
32098
32182
|
*/ const bridgeFeeQuoteSchema = zod.z.object({
|
|
32099
|
-
feeToken:
|
|
32183
|
+
feeToken: bridgeFeeTokenSchema,
|
|
32100
32184
|
items: zod.z.array(zod.z.object({
|
|
32101
32185
|
type: zod.z.string(),
|
|
32102
32186
|
amount: uint256LikeSchema
|
|
@@ -32112,6 +32196,12 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
32112
32196
|
erc3009TypedData: erc3009TypedDataSchema,
|
|
32113
32197
|
feeQuote: bridgeFeeQuoteSchema
|
|
32114
32198
|
}).passthrough();
|
|
32199
|
+
const bridgeDepositPrepareReviewSchema = zod.z.object({
|
|
32200
|
+
sourceChain: zod.z.string(),
|
|
32201
|
+
destinationChain: zod.z.string(),
|
|
32202
|
+
amount: amountJsonSchema,
|
|
32203
|
+
vaultAddress: hexAddressSchema
|
|
32204
|
+
}).passthrough();
|
|
32115
32205
|
/**
|
|
32116
32206
|
* Zod schema for the bridge deposit prepare payload.
|
|
32117
32207
|
*
|
|
@@ -32123,7 +32213,7 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
32123
32213
|
execId: bridgeDepositExecIdSchema,
|
|
32124
32214
|
erc3009TypedData: bridgeDepositPreparedBundleSchema,
|
|
32125
32215
|
expiresAt: zod.z.string().datetime(),
|
|
32126
|
-
review:
|
|
32216
|
+
review: bridgeDepositPrepareReviewSchema
|
|
32127
32217
|
});
|
|
32128
32218
|
/**
|
|
32129
32219
|
* Zod schema for the `POST /v1/earnKit/bridge/deposit/prepare` API response.
|
|
@@ -32851,7 +32941,8 @@ function toPositionInfo(data) {
|
|
|
32851
32941
|
return {
|
|
32852
32942
|
execId: response.data.execId,
|
|
32853
32943
|
preparedBundle,
|
|
32854
|
-
expiresAt: response.data.expiresAt
|
|
32944
|
+
expiresAt: response.data.expiresAt,
|
|
32945
|
+
review: response.data.review
|
|
32855
32946
|
};
|
|
32856
32947
|
} catch (error) {
|
|
32857
32948
|
throw parseEarnApiError(error, {
|
|
@@ -32962,47 +33053,34 @@ const VALID_AFTER_CLOCK_SKEW_SECONDS = 60n;
|
|
|
32962
33053
|
* @throws If any typed-data field does not match the expected bridge deposit intent.
|
|
32963
33054
|
* @internal
|
|
32964
33055
|
*/ function assertBridgeDepositTypedDataMatchesIntent(intent) {
|
|
32965
|
-
const { preparedBundle, sourceChain, sourceAddress, sourceTokenAddress, authorizationRecipient, amount, maxFee, tokenDecimals, nowSeconds = BigInt(Math.floor(Date.now() / 1000)) } = intent;
|
|
33056
|
+
const { preparedBundle, sourceChain, sourceAddress, sourceTokenAddress, expectedDomain, authorizationRecipient, amount, maxFee, tokenDecimals, amountRaw, nowSeconds = BigInt(Math.floor(Date.now() / 1000)) } = intent;
|
|
32966
33057
|
const { domain, message } = preparedBundle.erc3009TypedData;
|
|
32967
|
-
// USDC deployments differ on the EIP-712 domain name per chain, so the
|
|
32968
|
-
// expected domain is looked up for the source chain being signed on.
|
|
32969
|
-
const expectedDomain = EARN_BRIDGE_ERC3009_DOMAINS[sourceChain.chain];
|
|
32970
|
-
if (expectedDomain === undefined) {
|
|
32971
|
-
throw createValidationFailedError$1('sourceChain.chain', sourceChain.chain, 'no expected ERC-3009 domain is configured for the source chain');
|
|
32972
|
-
}
|
|
32973
|
-
if (domain.name !== expectedDomain.name) {
|
|
32974
|
-
throw createValidationFailedError$1('preparedBundle.erc3009TypedData.domain.name', domain.name, `domain name must be ${expectedDomain.name}`);
|
|
32975
|
-
}
|
|
32976
|
-
if (domain.version !== expectedDomain.version) {
|
|
32977
|
-
throw createValidationFailedError$1('preparedBundle.erc3009TypedData.domain.version', domain.version, `domain version must be ${expectedDomain.version}`);
|
|
32978
|
-
}
|
|
32979
33058
|
assertSameAddress('preparedBundle.erc3009TypedData.domain.verifyingContract', domain.verifyingContract, sourceTokenAddress, 'verifyingContract must match the source token contract');
|
|
32980
33059
|
assertSameBigInt('preparedBundle.erc3009TypedData.domain.chainId', domain.chainId, BigInt(sourceChain.chainId), 'chainId must match the source chain');
|
|
33060
|
+
assertSameString('preparedBundle.erc3009TypedData.domain.name', domain.name, expectedDomain.name, 'domain name must match the source token');
|
|
33061
|
+
assertSameString('preparedBundle.erc3009TypedData.domain.version', domain.version, expectedDomain.version, 'domain version must match the source token');
|
|
32981
33062
|
assertSameAddress('preparedBundle.erc3009TypedData.message.from', message.from, sourceAddress, 'from must match the source wallet');
|
|
32982
33063
|
assertSameAddress('preparedBundle.erc3009TypedData.message.to', message.to, authorizationRecipient, 'to must match the configured authorization recipient');
|
|
32983
|
-
//
|
|
32984
|
-
//
|
|
32985
|
-
|
|
32986
|
-
|
|
32987
|
-
// token to the source token contract before summing.
|
|
32988
|
-
assertSameAddress('preparedBundle.feeQuote.feeToken', preparedBundle.feeQuote.feeToken, sourceTokenAddress, 'feeQuote.feeToken must match the source token contract');
|
|
32989
|
-
// Recompute the expected value from locally trusted inputs (the caller
|
|
32990
|
-
// principal) plus the bundle's own fee items so the guard accepts fee-bearing
|
|
32991
|
-
// deposits while still rejecting any other value tampering. The fee quote is
|
|
32992
|
-
// service-supplied, so bound it to the caller-accepted quote-derived cap
|
|
32993
|
-
// before including it in the signed value.
|
|
32994
|
-
const principal = Amount.parse(amount, {
|
|
33064
|
+
// Recompute the expected ERC-3009 value from locally trusted inputs. Bridge
|
|
33065
|
+
// source fees are paid in the source ERC-20 and are part of the authorization.
|
|
33066
|
+
assertDecimalPlaces('amount', amount, tokenDecimals, `amount must have at most ${String(tokenDecimals)} decimal places for the cross-chain Earn deposit source token`);
|
|
33067
|
+
const parsedAmount = Amount.parse(amount, {
|
|
32995
33068
|
decimals: tokenDecimals
|
|
32996
33069
|
}).raw;
|
|
33070
|
+
const principal = amountRaw === undefined ? parsedAmount : parseReviewedAmount(amountRaw, parsedAmount);
|
|
33071
|
+
const feeQuote = preparedBundle.feeQuote;
|
|
32997
33072
|
const feeTotal = sumBridgeFeeItems(preparedBundle.feeQuote);
|
|
33073
|
+
assertFeeTokenMatchesSourceToken(feeQuote.feeToken, sourceTokenAddress);
|
|
33074
|
+
const feeDecimals = tokenDecimals;
|
|
33075
|
+
assertDecimalPlaces('maxFee', maxFee, feeDecimals, `maxFee must have at most ${String(feeDecimals)} decimal places for the cross-chain Earn deposit fee token`);
|
|
32998
33076
|
const maxFeeRaw = Amount.parse(maxFee, {
|
|
32999
|
-
decimals:
|
|
33077
|
+
decimals: feeDecimals
|
|
33000
33078
|
}).raw;
|
|
33001
33079
|
if (feeTotal > maxFeeRaw) {
|
|
33002
33080
|
throw createValidationFailedError$1('preparedBundle.feeQuote.items', feeTotal.toString(), 'fee total must not exceed maxFee in base units');
|
|
33003
33081
|
}
|
|
33004
33082
|
const expectedValue = principal + feeTotal;
|
|
33005
|
-
assertSameBigInt('preparedBundle.erc3009TypedData.message.value', message.value, expectedValue, 'value must match the requested amount plus
|
|
33083
|
+
assertSameBigInt('preparedBundle.erc3009TypedData.message.value', message.value, expectedValue, 'value must match the requested amount plus source-token fees in base units');
|
|
33006
33084
|
const window = checkErc3009ValidityWindow({
|
|
33007
33085
|
validAfter: BigInt(message.validAfter),
|
|
33008
33086
|
validBefore: BigInt(message.validBefore),
|
|
@@ -33076,14 +33154,19 @@ const VALID_AFTER_CLOCK_SKEW_SECONDS = 60n;
|
|
|
33076
33154
|
/**
|
|
33077
33155
|
* Sum the source-collected fee items in a prepared bundle's fee quote.
|
|
33078
33156
|
*
|
|
33079
|
-
* Each item amount is a base-unit value in the shared `feeToken
|
|
33080
|
-
* what the prepare path adds to the principal when building `message.value`.
|
|
33157
|
+
* Each item amount is a base-unit value in the shared `feeToken`.
|
|
33081
33158
|
*
|
|
33082
33159
|
* @param feeQuote - Fee quote parsed from the prepared bundle.
|
|
33083
|
-
* @returns Total fee in
|
|
33160
|
+
* @returns Total fee in fee-token base units (0n when there are no items).
|
|
33084
33161
|
*/ function sumBridgeFeeItems(feeQuote) {
|
|
33085
33162
|
return feeQuote.items.reduce((total, item)=>total + BigInt(item.amount), 0n);
|
|
33086
33163
|
}
|
|
33164
|
+
function assertFeeTokenMatchesSourceToken(feeToken, sourceTokenAddress) {
|
|
33165
|
+
if (isSameAddress(feeToken, sourceTokenAddress)) {
|
|
33166
|
+
return;
|
|
33167
|
+
}
|
|
33168
|
+
throw createValidationFailedError$1('preparedBundle.feeQuote.feeToken', feeToken, 'fee token must match the cross-chain Earn deposit source token');
|
|
33169
|
+
}
|
|
33087
33170
|
function assertNever(value) {
|
|
33088
33171
|
throw new KitError({
|
|
33089
33172
|
...EarnError.INTERNAL_ERROR,
|
|
@@ -33091,8 +33174,23 @@ function assertNever(value) {
|
|
|
33091
33174
|
message: `Unexpected validity window reason: ${String(value)}`
|
|
33092
33175
|
});
|
|
33093
33176
|
}
|
|
33177
|
+
function parseReviewedAmount(amountRaw, parsedAmount) {
|
|
33178
|
+
let reviewedAmount;
|
|
33179
|
+
try {
|
|
33180
|
+
reviewedAmount = BigInt(amountRaw);
|
|
33181
|
+
} catch {
|
|
33182
|
+
throw createValidationFailedError$1('prepared.review.amount.raw', amountRaw, 'amount must be a non-negative integer string');
|
|
33183
|
+
}
|
|
33184
|
+
if (reviewedAmount < 0n) {
|
|
33185
|
+
throw createValidationFailedError$1('prepared.review.amount.raw', amountRaw, 'amount must be a non-negative integer string');
|
|
33186
|
+
}
|
|
33187
|
+
if (reviewedAmount !== parsedAmount) {
|
|
33188
|
+
throw createValidationFailedError$1('prepared.review.amount.raw', amountRaw, 'review amount must match the requested amount');
|
|
33189
|
+
}
|
|
33190
|
+
return reviewedAmount;
|
|
33191
|
+
}
|
|
33094
33192
|
function assertSameAddress(field, actual, expected, reason) {
|
|
33095
|
-
if (actual
|
|
33193
|
+
if (!isSameAddress(actual, expected)) {
|
|
33096
33194
|
throw createValidationFailedError$1(field, actual, reason);
|
|
33097
33195
|
}
|
|
33098
33196
|
}
|
|
@@ -33101,6 +33199,11 @@ function assertSameBigInt(field, actual, expected, reason) {
|
|
|
33101
33199
|
throw createValidationFailedError$1(field, actual, reason);
|
|
33102
33200
|
}
|
|
33103
33201
|
}
|
|
33202
|
+
function assertSameString(field, actual, expected, reason) {
|
|
33203
|
+
if (actual !== expected) {
|
|
33204
|
+
throw createValidationFailedError$1(field, actual, reason);
|
|
33205
|
+
}
|
|
33206
|
+
}
|
|
33104
33207
|
|
|
33105
33208
|
/**
|
|
33106
33209
|
* Build signed withdrawal instructions via the Earn Service API.
|
|
@@ -33458,6 +33561,9 @@ function toWithdrawalQuoteInfo(data) {
|
|
|
33458
33561
|
'CONFIRMED',
|
|
33459
33562
|
'COMPLETE'
|
|
33460
33563
|
]);
|
|
33564
|
+
// Intentionally built-ins-only: Earn bridge support is limited to SDK-known
|
|
33565
|
+
// token contracts plus the explicit ERC-3009 domain allowlist below.
|
|
33566
|
+
const TOKEN_REGISTRY = createTokenRegistry();
|
|
33461
33567
|
/**
|
|
33462
33568
|
* Build the typed error raised when a cross-chain wait is cancelled via its
|
|
33463
33569
|
* `AbortSignal`. Mirrors `@core/adapter-base`'s `createAbortError` (same
|
|
@@ -33787,24 +33893,20 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33787
33893
|
}
|
|
33788
33894
|
const { address, chain: apiChain, chainDefinition: chain } = await resolveAdapterContext(params.from);
|
|
33789
33895
|
const adapterContractAddress = requireAdapterContract(chain);
|
|
33790
|
-
const rawUsdcAddress = chain.usdcAddress;
|
|
33791
|
-
if (rawUsdcAddress === null) {
|
|
33792
|
-
throw createUnsupportedTokenError('USDC', chain.name);
|
|
33793
|
-
}
|
|
33794
|
-
const usdcAddress = assertHexAddress('chain.usdcAddress', rawUsdcAddress, `USDC address for chain ${chain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
33795
33896
|
const { adapter } = params.from;
|
|
33897
|
+
const vaultAddress = assertHexAddress('vaultAddress', params.vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
33796
33898
|
const { executionParams, signature } = await this.runPhase(ctx, 'deposit', 'fetchParams', async ()=>fetchDeposit({
|
|
33797
|
-
vaultAddress
|
|
33899
|
+
vaultAddress,
|
|
33798
33900
|
amount: params.amount,
|
|
33799
33901
|
address,
|
|
33800
33902
|
chain: apiChain,
|
|
33801
33903
|
config
|
|
33802
33904
|
}), ()=>undefined);
|
|
33803
33905
|
validateExecutionDeadline(executionParams);
|
|
33804
|
-
const
|
|
33805
|
-
const
|
|
33906
|
+
const approvalToken = resolveEarnApprovalToken(executionParams);
|
|
33907
|
+
const tokenInputs = approvalToken === undefined ? [] : buildEarnTokenInputs(executionParams, approvalToken);
|
|
33806
33908
|
const requiredAllowance = sumTokenInputAmounts(tokenInputs);
|
|
33807
|
-
if (!options.skipApprove && approvalToken !== undefined) {
|
|
33909
|
+
if (!options.skipApprove && approvalToken !== undefined && requiredAllowance > 0n) {
|
|
33808
33910
|
await this.runPhase(ctx, 'approve', 'approve', async ()=>approveAllowanceIfNeeded({
|
|
33809
33911
|
adapter,
|
|
33810
33912
|
chain,
|
|
@@ -33812,7 +33914,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33812
33914
|
delegate: adapterContractAddress,
|
|
33813
33915
|
address,
|
|
33814
33916
|
requiredAllowance,
|
|
33815
|
-
revertMessage: '
|
|
33917
|
+
revertMessage: 'Earn deposit token approval reverted on-chain'
|
|
33816
33918
|
}), (txHash)=>txHash);
|
|
33817
33919
|
}
|
|
33818
33920
|
const { txHash, explorerUrl } = await this.runPhase(ctx, 'deposit', 'execute', async ()=>executeEarnAction({
|
|
@@ -33831,7 +33933,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33831
33933
|
kind: 'same-chain',
|
|
33832
33934
|
txHash,
|
|
33833
33935
|
explorerUrl,
|
|
33834
|
-
vaultAddress
|
|
33936
|
+
vaultAddress,
|
|
33835
33937
|
amount: params.amount
|
|
33836
33938
|
};
|
|
33837
33939
|
} catch (error) {
|
|
@@ -33853,13 +33955,6 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33853
33955
|
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.`);
|
|
33854
33956
|
const destinationAddress = assertHexAddress('to.recipientAddress', params.to.recipientAddress, `Destination address for chain ${destinationChain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
33855
33957
|
const vaultAddress = assertHexAddress('vaultAddress', params.vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
33856
|
-
// The signed ERC-3009 value is denominated in USDC's 6 decimals. More
|
|
33857
|
-
// precise inputs would be silently truncated by Amount.parse, so the
|
|
33858
|
-
// result would claim a larger deposit than was actually authorized.
|
|
33859
|
-
const amountDecimals = params.amount.split('.')[1]?.length ?? 0;
|
|
33860
|
-
if (amountDecimals > TOKEN_DECIMALS_6) {
|
|
33861
|
-
throw createValidationFailedError$1('amount', params.amount, `amount must have at most ${String(TOKEN_DECIMALS_6)} decimal places for cross-chain Earn deposits`);
|
|
33862
|
-
}
|
|
33863
33958
|
const execId = params.idempotencyKey;
|
|
33864
33959
|
const prepared = await this.runPhase(ctx, 'crossChainDeposit', 'prepare', async ()=>fetchBridgeDepositPrepare({
|
|
33865
33960
|
execId,
|
|
@@ -33879,17 +33974,19 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
33879
33974
|
// the error trace, instead of ending the event stream unannounced
|
|
33880
33975
|
// after the prepare success event.
|
|
33881
33976
|
assertPreparedBundleMatchesTypedDataNonce(prepared.preparedBundle);
|
|
33977
|
+
const sourceToken = resolvePreparedBridgeSourceToken(prepared.preparedBundle, route.sourceChain);
|
|
33978
|
+
validatePreparedReviewAmountDecimals(prepared.review.amount.decimals, sourceToken.decimals);
|
|
33882
33979
|
assertBridgeDepositTypedDataMatchesIntent({
|
|
33883
33980
|
preparedBundle: prepared.preparedBundle,
|
|
33884
33981
|
sourceChain: route.sourceChain,
|
|
33885
33982
|
sourceAddress,
|
|
33886
|
-
sourceTokenAddress:
|
|
33983
|
+
sourceTokenAddress: sourceToken.address,
|
|
33984
|
+
expectedDomain: sourceToken.erc3009Domain,
|
|
33887
33985
|
authorizationRecipient: route.authorizationRecipient,
|
|
33888
33986
|
amount: params.amount,
|
|
33889
33987
|
maxFee: params.maxFee,
|
|
33890
|
-
|
|
33891
|
-
|
|
33892
|
-
tokenDecimals: TOKEN_DECIMALS_6
|
|
33988
|
+
tokenDecimals: sourceToken.decimals,
|
|
33989
|
+
amountRaw: prepared.review.amount.raw
|
|
33893
33990
|
});
|
|
33894
33991
|
return signBridgeDepositAuthorization({
|
|
33895
33992
|
adapter: bridgeSigningAdapter,
|
|
@@ -34112,21 +34209,17 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
34112
34209
|
const { adapter, chain, apiChain, address, vaultAddress, amount, config } = params;
|
|
34113
34210
|
try {
|
|
34114
34211
|
const adapterContractAddress = requireAdapterContract(chain);
|
|
34115
|
-
const
|
|
34116
|
-
if (rawUsdcAddress === null) {
|
|
34117
|
-
throw createUnsupportedTokenError('USDC', chain.name);
|
|
34118
|
-
}
|
|
34119
|
-
const usdcAddress = assertHexAddress('chain.usdcAddress', rawUsdcAddress, `USDC address for chain ${chain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
34212
|
+
const normalizedVaultAddress = assertHexAddress('vaultAddress', vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
34120
34213
|
const { executionParams, signature } = await fetchDeposit({
|
|
34121
|
-
vaultAddress,
|
|
34214
|
+
vaultAddress: normalizedVaultAddress,
|
|
34122
34215
|
amount,
|
|
34123
34216
|
address,
|
|
34124
34217
|
chain: apiChain,
|
|
34125
34218
|
config
|
|
34126
34219
|
});
|
|
34127
34220
|
validateExecutionDeadline(executionParams);
|
|
34128
|
-
const
|
|
34129
|
-
const
|
|
34221
|
+
const approvalToken = resolveEarnApprovalToken(executionParams);
|
|
34222
|
+
const tokenInputs = approvalToken === undefined ? [] : buildEarnTokenInputs(executionParams, approvalToken);
|
|
34130
34223
|
const requiredAllowance = sumTokenInputAmounts(tokenInputs);
|
|
34131
34224
|
return await estimateEarnQuoteGasFees({
|
|
34132
34225
|
adapter,
|
|
@@ -34139,7 +34232,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
34139
34232
|
tokenInputs,
|
|
34140
34233
|
signature
|
|
34141
34234
|
},
|
|
34142
|
-
approval: approvalToken !== undefined ? {
|
|
34235
|
+
approval: approvalToken !== undefined && requiredAllowance > 0n ? {
|
|
34143
34236
|
token: approvalToken,
|
|
34144
34237
|
delegate: adapterContractAddress,
|
|
34145
34238
|
requiredAllowance
|
|
@@ -34383,11 +34476,6 @@ function validateCrossChainDepositRoute(sourceChain, destinationChain) {
|
|
|
34383
34476
|
if (destinationApiChain === undefined) {
|
|
34384
34477
|
throw createUnsupportedCrossChainDepositError(destinationChain, 'destination chain is not enabled for cross-chain Earn deposits');
|
|
34385
34478
|
}
|
|
34386
|
-
const sourceTokenAddressRaw = sourceChain.usdcAddress;
|
|
34387
|
-
if (sourceTokenAddressRaw === null) {
|
|
34388
|
-
throw createUnsupportedTokenError('USDC', sourceChain.name);
|
|
34389
|
-
}
|
|
34390
|
-
const sourceTokenAddress = assertHexAddress('sourceChain.usdcAddress', sourceTokenAddressRaw, `USDC address for chain ${sourceChain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
34391
34479
|
if (sourceChain.cctp == null) {
|
|
34392
34480
|
throw createBridgeRouteNotConfiguredError(sourceChain, 'source chain does not support CCTP');
|
|
34393
34481
|
}
|
|
@@ -34411,10 +34499,50 @@ function validateCrossChainDepositRoute(sourceChain, destinationChain) {
|
|
|
34411
34499
|
destinationApiChain,
|
|
34412
34500
|
destinationDomain: destinationChain.cctp.domain,
|
|
34413
34501
|
sourceChain,
|
|
34414
|
-
sourceTokenAddress,
|
|
34415
34502
|
authorizationRecipient
|
|
34416
34503
|
};
|
|
34417
34504
|
}
|
|
34505
|
+
function resolvePreparedBridgeSourceToken(preparedBundle, sourceChain) {
|
|
34506
|
+
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.`);
|
|
34507
|
+
let sourceToken;
|
|
34508
|
+
try {
|
|
34509
|
+
sourceToken = TOKEN_REGISTRY.resolveByAddress(sourceTokenAddress, sourceChain.chain);
|
|
34510
|
+
} catch {
|
|
34511
|
+
throw createValidationFailedError$1('preparedBundle.erc3009TypedData.domain.verifyingContract', sourceTokenAddress, `source token ${sourceTokenAddress} is not configured on source chain ${sourceChain.name} for cross-chain Earn deposits`);
|
|
34512
|
+
}
|
|
34513
|
+
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.`);
|
|
34514
|
+
if (!isSameAddress(sourceTokenAddress, registryAddress)) {
|
|
34515
|
+
throw createValidationFailedError$1('preparedBundle.erc3009TypedData.domain.verifyingContract', sourceTokenAddress, `source token must match the configured ${sourceToken.symbol ?? 'token'} address on source chain ${sourceChain.name}`);
|
|
34516
|
+
}
|
|
34517
|
+
const { symbol } = sourceToken;
|
|
34518
|
+
if (symbol === undefined) {
|
|
34519
|
+
throw createValidationFailedError$1('preparedBundle.erc3009TypedData.domain.verifyingContract', sourceTokenAddress, `source token ${sourceTokenAddress} is not a registered Earn bridge token on source chain ${sourceChain.name}`);
|
|
34520
|
+
}
|
|
34521
|
+
return {
|
|
34522
|
+
address: sourceTokenAddress,
|
|
34523
|
+
erc3009Domain: resolveBridgeErc3009Domain(symbol, sourceChain),
|
|
34524
|
+
decimals: sourceToken.decimals
|
|
34525
|
+
};
|
|
34526
|
+
}
|
|
34527
|
+
function validatePreparedReviewAmountDecimals(reviewedDecimals, tokenDecimals) {
|
|
34528
|
+
if (reviewedDecimals !== tokenDecimals) {
|
|
34529
|
+
throw createValidationFailedError$1('prepared.review.amount.decimals', reviewedDecimals, `review amount decimals must match the cross-chain Earn deposit source token decimals ${String(tokenDecimals)}`);
|
|
34530
|
+
}
|
|
34531
|
+
}
|
|
34532
|
+
function resolveBridgeErc3009Domain(tokenSymbol, sourceChain) {
|
|
34533
|
+
if (!isEarnBridgeErc3009TokenSymbol(tokenSymbol)) {
|
|
34534
|
+
throw createValidationFailedError$1('sourceToken.symbol', tokenSymbol, `ERC-3009 domain is not configured for ${tokenSymbol} on source chain ${sourceChain.name}`);
|
|
34535
|
+
}
|
|
34536
|
+
const domainsByChain = EARN_BRIDGE_ERC3009_DOMAINS[tokenSymbol];
|
|
34537
|
+
const domain = domainsByChain[sourceChain.chain];
|
|
34538
|
+
if (domain === undefined) {
|
|
34539
|
+
throw createValidationFailedError$1('sourceToken.symbol', tokenSymbol, `ERC-3009 domain is not configured for ${tokenSymbol} on source chain ${sourceChain.name}`);
|
|
34540
|
+
}
|
|
34541
|
+
return domain;
|
|
34542
|
+
}
|
|
34543
|
+
function isEarnBridgeErc3009TokenSymbol(tokenSymbol) {
|
|
34544
|
+
return EARN_BRIDGE_ERC3009_TOKEN_SYMBOLS.includes(tokenSymbol);
|
|
34545
|
+
}
|
|
34418
34546
|
function createUnsupportedCrossChainDepositError(chain, reason) {
|
|
34419
34547
|
return new KitError({
|
|
34420
34548
|
...EarnError.UNSUPPORTED_CHAIN,
|
|
@@ -34487,7 +34615,6 @@ function createUnsupportedCrossChainDepositError(chain, reason) {
|
|
|
34487
34615
|
// Untyped callers can pass anything here; without these guards a null
|
|
34488
34616
|
// config crashes with a raw TypeError on `config.providers` instead of
|
|
34489
34617
|
// the structured validation error every other public surface throws.
|
|
34490
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- JS consumers may pass null or a non-object config
|
|
34491
34618
|
if (typeof config !== 'object' || config === null || Array.isArray(config)) {
|
|
34492
34619
|
throw createValidationFailedError$1('config', config, 'config must be a plain object when provided');
|
|
34493
34620
|
}
|
|
@@ -34854,10 +34981,9 @@ const sourceAdapterContextSchema = zod.z.object({
|
|
|
34854
34981
|
* Schema for validating human-readable decimal amount strings.
|
|
34855
34982
|
*
|
|
34856
34983
|
* Accept positive decimal strings like '100', '100.50', '0.001'. Reject
|
|
34857
|
-
* zero, negative, non-numeric, and non-canonical strings.
|
|
34858
|
-
*
|
|
34859
|
-
*
|
|
34860
|
-
* 7+ decimal-place inputs to the server.
|
|
34984
|
+
* zero, negative, non-numeric, and non-canonical strings. Same-chain
|
|
34985
|
+
* EarnKit vaults can use different asset precisions, so the service enforces
|
|
34986
|
+
* the vault-specific decimal cap after resolving the vault asset.
|
|
34861
34987
|
*
|
|
34862
34988
|
* @internal
|
|
34863
34989
|
*/ const amountSchema$1 = zod.z.string({
|
|
@@ -34866,32 +34992,15 @@ const sourceAdapterContextSchema = zod.z.object({
|
|
|
34866
34992
|
allowZero: false,
|
|
34867
34993
|
regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
|
|
34868
34994
|
attributeName: 'amount',
|
|
34869
|
-
maxDecimals:
|
|
34870
|
-
})(zod.z.string())).refine((value)=>CANONICAL_AMOUNT_REGEX.test(value), AMOUNT_FORMAT_ERROR_MESSAGE);
|
|
34871
|
-
/**
|
|
34872
|
-
* Schema for cross-chain deposit amount strings.
|
|
34873
|
-
*
|
|
34874
|
-
* Cross-chain Earn deposits are USDC-only, and the signed ERC-3009 value is
|
|
34875
|
-
* denominated in USDC's 6 decimals. Reject more precise inputs up front so
|
|
34876
|
-
* the signed value always equals the requested amount instead of a silent
|
|
34877
|
-
* truncation. Non-canonical forms (leading dot, leading zeros) are rejected
|
|
34878
|
-
* too, matching the Earn Service.
|
|
34879
|
-
*
|
|
34880
|
-
* @internal
|
|
34881
|
-
*/ const crossChainAmountSchema = zod.z.string({
|
|
34882
|
-
required_error: 'amount is required'
|
|
34883
|
-
}).min(1, 'amount is required').pipe(createDecimalStringValidator({
|
|
34884
|
-
allowZero: false,
|
|
34885
|
-
regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
|
|
34886
|
-
attributeName: 'amount',
|
|
34887
|
-
maxDecimals: 6
|
|
34995
|
+
maxDecimals: 18
|
|
34888
34996
|
})(zod.z.string())).refine((value)=>CANONICAL_AMOUNT_REGEX.test(value), AMOUNT_FORMAT_ERROR_MESSAGE);
|
|
34889
34997
|
/**
|
|
34890
34998
|
* Schema for cross-chain source fee caps.
|
|
34891
34999
|
*
|
|
34892
|
-
* Cross-chain source fees are denominated in
|
|
34893
|
-
* when the quote contains no source-collected fees; otherwise the cap
|
|
34894
|
-
* the caller-accepted total from a recent quote.
|
|
35000
|
+
* Cross-chain source fees are denominated in the quote's fee token. Zero is
|
|
35001
|
+
* valid when the quote contains no source-collected fees; otherwise the cap
|
|
35002
|
+
* should be the caller-accepted total from a recent quote. The provider
|
|
35003
|
+
* enforces the prepared bundle's fee-token precision before signing.
|
|
34895
35004
|
*
|
|
34896
35005
|
* @internal
|
|
34897
35006
|
*/ const crossChainMaxFeeSchema = zod.z.string({
|
|
@@ -34900,7 +35009,7 @@ const sourceAdapterContextSchema = zod.z.object({
|
|
|
34900
35009
|
allowZero: true,
|
|
34901
35010
|
regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
|
|
34902
35011
|
attributeName: 'maxFee',
|
|
34903
|
-
maxDecimals:
|
|
35012
|
+
maxDecimals: 18
|
|
34904
35013
|
})(zod.z.string())).refine((value)=>CANONICAL_AMOUNT_REGEX.test(value), AMOUNT_FORMAT_ERROR_MESSAGE);
|
|
34905
35014
|
/**
|
|
34906
35015
|
* Build a schema for an optional non-negative decimal filter string.
|
|
@@ -35061,7 +35170,7 @@ const crossChainDepositDestinationSchema = zod.z.object({
|
|
|
35061
35170
|
from: sourceAdapterContextSchema,
|
|
35062
35171
|
to: crossChainDepositDestinationSchema,
|
|
35063
35172
|
vaultAddress: vaultAddressSchema,
|
|
35064
|
-
amount:
|
|
35173
|
+
amount: amountSchema$1,
|
|
35065
35174
|
maxFee: crossChainMaxFeeSchema,
|
|
35066
35175
|
transferSpeed: zod.z.enum([
|
|
35067
35176
|
'FAST',
|
|
@@ -37801,7 +37910,7 @@ async function deposit$2(context, params) {
|
|
|
37801
37910
|
}
|
|
37802
37911
|
|
|
37803
37912
|
var name = "@circle-fin/unified-balance-kit";
|
|
37804
|
-
var version = "1.2.
|
|
37913
|
+
var version = "1.2.1";
|
|
37805
37914
|
var pkg = {
|
|
37806
37915
|
name: name,
|
|
37807
37916
|
version: version};
|