@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/earn.cjs
CHANGED
|
@@ -843,36 +843,6 @@ class KitError extends Error {
|
|
|
843
843
|
};
|
|
844
844
|
return new KitError(errorDetails);
|
|
845
845
|
}
|
|
846
|
-
/**
|
|
847
|
-
* Creates error for unsupported token on chain.
|
|
848
|
-
*
|
|
849
|
-
* This error is thrown when a token is not supported on the specified chain.
|
|
850
|
-
*
|
|
851
|
-
* @param token - The token symbol (e.g., 'USDC', 'USDT')
|
|
852
|
-
* @param chain - The chain name where the token is unsupported
|
|
853
|
-
* @returns KitError with specific token support details
|
|
854
|
-
*
|
|
855
|
-
* @example
|
|
856
|
-
* ```typescript
|
|
857
|
-
* import { createUnsupportedTokenError } from '@core/errors'
|
|
858
|
-
*
|
|
859
|
-
* throw createUnsupportedTokenError('USDC', 'UnknownChain')
|
|
860
|
-
* // Message: "USDC is not supported on UnknownChain"
|
|
861
|
-
* ```
|
|
862
|
-
*/ function createUnsupportedTokenError(token, chain) {
|
|
863
|
-
const errorDetails = {
|
|
864
|
-
...InputError.UNSUPPORTED_TOKEN,
|
|
865
|
-
recoverability: 'FATAL',
|
|
866
|
-
message: `${token} is not supported on ${chain}.`,
|
|
867
|
-
cause: {
|
|
868
|
-
trace: {
|
|
869
|
-
token,
|
|
870
|
-
chain
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
};
|
|
874
|
-
return new KitError(errorDetails);
|
|
875
|
-
}
|
|
876
846
|
/**
|
|
877
847
|
* Creates error for invalid chain configuration.
|
|
878
848
|
*
|
|
@@ -1898,7 +1868,7 @@ function getOptionalString(value) {
|
|
|
1898
1868
|
* - vault-not-found, unsupported-chain, unsupported-vault,
|
|
1899
1869
|
* signature-rejected, invalid-id, invalid-batch-size, position-not-registered,
|
|
1900
1870
|
* withdrawal-max-exceeded, insufficient-balance, bridge prepare idempotency
|
|
1901
|
-
* conflicts
|
|
1871
|
+
* conflicts, invalid vault-asset amount precision
|
|
1902
1872
|
*
|
|
1903
1873
|
* SERVICE errors (RETRYABLE) — try again later:
|
|
1904
1874
|
* - signing-failed, provider-error, rewards-fetch-failed,
|
|
@@ -2082,6 +2052,13 @@ function getOptionalString(value) {
|
|
|
2082
2052
|
recoverability: 'RETRYABLE'
|
|
2083
2053
|
}
|
|
2084
2054
|
],
|
|
2055
|
+
[
|
|
2056
|
+
380415,
|
|
2057
|
+
{
|
|
2058
|
+
errorDef: EarnError.INVALID_INPUT,
|
|
2059
|
+
recoverability: 'FATAL'
|
|
2060
|
+
}
|
|
2061
|
+
],
|
|
2085
2062
|
// Bridge (380_5XX)
|
|
2086
2063
|
[
|
|
2087
2064
|
380500,
|
|
@@ -2117,6 +2094,13 @@ function getOptionalString(value) {
|
|
|
2117
2094
|
errorDef: EarnError.PROVIDER_ERROR,
|
|
2118
2095
|
recoverability: 'RETRYABLE'
|
|
2119
2096
|
}
|
|
2097
|
+
],
|
|
2098
|
+
[
|
|
2099
|
+
380505,
|
|
2100
|
+
{
|
|
2101
|
+
errorDef: EarnError.PROVIDER_ERROR,
|
|
2102
|
+
recoverability: 'FATAL'
|
|
2103
|
+
}
|
|
2120
2104
|
]
|
|
2121
2105
|
]);
|
|
2122
2106
|
/**
|
|
@@ -2576,23 +2560,6 @@ var EarnChain;
|
|
|
2576
2560
|
}
|
|
2577
2561
|
});
|
|
2578
2562
|
|
|
2579
|
-
/**
|
|
2580
|
-
* Standard decimal places for tokens.
|
|
2581
|
-
*
|
|
2582
|
-
* These constants define the decimal precision used by different token types
|
|
2583
|
-
* across the SDK. They are used for amount conversions between human-readable
|
|
2584
|
-
* format and smallest units (base units).
|
|
2585
|
-
*
|
|
2586
|
-
* @remarks
|
|
2587
|
-
* - Most stablecoins (USDC, EURC, USDT, PYUSD) use 6 decimal places
|
|
2588
|
-
* - Some stablecoins (DAI, USDE) and most native tokens (ETH, POL, PLUME) use 18 decimals
|
|
2589
|
-
* - The NATIVE alias uses chain-specific decimals which may vary (e.g., SOL uses 9)
|
|
2590
|
-
*/ /**
|
|
2591
|
-
* Standard decimal places for 6-decimal tokens.
|
|
2592
|
-
*
|
|
2593
|
-
* Used by most stablecoins: USDC, EURC, USDT, PYUSD
|
|
2594
|
-
*/ const TOKEN_DECIMALS_6 = 6;
|
|
2595
|
-
|
|
2596
2563
|
/**
|
|
2597
2564
|
* @packageDocumentation
|
|
2598
2565
|
* @module SwapTokenRegistry
|
|
@@ -7269,13 +7236,20 @@ const swapTokenEnumSchema = zod.z.enum([
|
|
|
7269
7236
|
symbol: 'EURC',
|
|
7270
7237
|
decimals: 6,
|
|
7271
7238
|
locators: {
|
|
7239
|
+
// =========================================================================
|
|
7240
|
+
// Mainnets
|
|
7241
|
+
// =========================================================================
|
|
7272
7242
|
[Blockchain.Avalanche]: '0xc891EB4cbdEFf6e073e859e987815Ed1505c2ACD',
|
|
7273
7243
|
[Blockchain.Base]: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42',
|
|
7274
7244
|
[Blockchain.Ethereum]: '0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c',
|
|
7275
7245
|
[Blockchain.Solana]: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr',
|
|
7276
7246
|
[Blockchain.World_Chain]: '0x1C60ba0A0eD1019e8Eb035E6daF4155A5cE2380B',
|
|
7247
|
+
// =========================================================================
|
|
7277
7248
|
// Testnets
|
|
7278
|
-
|
|
7249
|
+
// =========================================================================
|
|
7250
|
+
[Blockchain.Arc_Testnet]: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
|
|
7251
|
+
[Blockchain.Base_Sepolia]: '0x808456652fdb597867f38412077A9182bf77359F',
|
|
7252
|
+
[Blockchain.Ethereum_Sepolia]: '0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4'
|
|
7279
7253
|
}
|
|
7280
7254
|
};
|
|
7281
7255
|
|
|
@@ -7485,8 +7459,7 @@ const swapTokenEnumSchema = zod.z.enum([
|
|
|
7485
7459
|
* cirBTC (Circle Bitcoin) token definition with addresses and metadata.
|
|
7486
7460
|
*
|
|
7487
7461
|
* @remarks
|
|
7488
|
-
* Built-in cirBTC definition for the TokenRegistry.
|
|
7489
|
-
* on Arc Testnet.
|
|
7462
|
+
* Built-in cirBTC definition for the TokenRegistry.
|
|
7490
7463
|
*
|
|
7491
7464
|
* @example
|
|
7492
7465
|
* ```typescript
|
|
@@ -7502,7 +7475,15 @@ const swapTokenEnumSchema = zod.z.enum([
|
|
|
7502
7475
|
symbol: 'cirBTC',
|
|
7503
7476
|
decimals: 8,
|
|
7504
7477
|
locators: {
|
|
7505
|
-
|
|
7478
|
+
// =========================================================================
|
|
7479
|
+
// Mainnets
|
|
7480
|
+
// =========================================================================
|
|
7481
|
+
[Blockchain.Ethereum]: '0x72DFB2E44f59C5AD2bAFE84314E5b99a7cd5075E',
|
|
7482
|
+
// =========================================================================
|
|
7483
|
+
// Testnets
|
|
7484
|
+
// =========================================================================
|
|
7485
|
+
[Blockchain.Arc_Testnet]: '0xf0C4a4CE82A5746AbAAd9425360Ab04fbBA432BF',
|
|
7486
|
+
[Blockchain.Ethereum_Sepolia]: '0x3a3fe695F684Bf9b9e43CF43C2b895Ea5e392bB3'
|
|
7506
7487
|
}
|
|
7507
7488
|
};
|
|
7508
7489
|
|
|
@@ -7942,7 +7923,7 @@ const swapTokenEnumSchema = zod.z.enum([
|
|
|
7942
7923
|
}
|
|
7943
7924
|
|
|
7944
7925
|
var name$2 = "@circle-fin/bridge-kit";
|
|
7945
|
-
var version$2 = "1.11.
|
|
7926
|
+
var version$2 = "1.11.1";
|
|
7946
7927
|
var pkg$2 = {
|
|
7947
7928
|
name: name$2,
|
|
7948
7929
|
version: version$2};
|
|
@@ -8799,7 +8780,7 @@ var TransferSpeed;
|
|
|
8799
8780
|
registerKit(`${pkg$2.name}/${pkg$2.version}`);
|
|
8800
8781
|
|
|
8801
8782
|
var name$1 = "@circle-fin/swap-kit";
|
|
8802
|
-
var version$1 = "1.3.
|
|
8783
|
+
var version$1 = "1.3.1";
|
|
8803
8784
|
var pkg$1 = {
|
|
8804
8785
|
name: name$1,
|
|
8805
8786
|
version: version$1};
|
|
@@ -12093,11 +12074,14 @@ new Set(Object.values(Blockchain));
|
|
|
12093
12074
|
registerKit(`${pkg$1.name}/${pkg$1.version}`);
|
|
12094
12075
|
|
|
12095
12076
|
var name = "@circle-fin/earn-kit";
|
|
12096
|
-
var version = "1.2.
|
|
12077
|
+
var version = "1.2.1";
|
|
12097
12078
|
var pkg = {
|
|
12098
12079
|
name: name,
|
|
12099
12080
|
version: version};
|
|
12100
12081
|
|
|
12082
|
+
const EARN_BRIDGE_ERC3009_TOKEN_SYMBOLS = [
|
|
12083
|
+
'USDC'
|
|
12084
|
+
];
|
|
12101
12085
|
/**
|
|
12102
12086
|
* Default base URL for the Earn Service API.
|
|
12103
12087
|
*
|
|
@@ -12142,20 +12126,7 @@ var pkg = {
|
|
|
12142
12126
|
*/ const EARN_BRIDGE_DESTINATION_CHAIN_TO_API = {
|
|
12143
12127
|
[Blockchain.Arc_Testnet]: CHAIN_TO_API[Blockchain.Arc_Testnet]
|
|
12144
12128
|
};
|
|
12145
|
-
|
|
12146
|
-
* Expected EIP-712 domain of the ERC-3009 authorization token per source
|
|
12147
|
-
* chain.
|
|
12148
|
-
*
|
|
12149
|
-
* Cross-chain Earn deposits are USDC-only, but USDC deployments differ on the
|
|
12150
|
-
* EIP-712 domain name: newer deployments use `USDC` while older ones (for
|
|
12151
|
-
* example Arbitrum Sepolia) kept `USD Coin` from the FiatToken V2 upgrade.
|
|
12152
|
-
* These values mirror the bridge service's per-chain domain table and the
|
|
12153
|
-
* on-chain `DOMAIN_SEPARATOR` inputs. Asserted before signing so a tampered
|
|
12154
|
-
* prepare response cannot point the signature at another token. The
|
|
12155
|
-
* `satisfies` check forces a domain entry whenever a source chain is added.
|
|
12156
|
-
*
|
|
12157
|
-
* @internal
|
|
12158
|
-
*/ const EARN_BRIDGE_ERC3009_DOMAINS = {
|
|
12129
|
+
const EARN_BRIDGE_USDC_ERC3009_DOMAINS = {
|
|
12159
12130
|
[Blockchain.Arbitrum_Sepolia]: {
|
|
12160
12131
|
name: 'USD Coin',
|
|
12161
12132
|
version: '2'
|
|
@@ -12169,6 +12140,21 @@ var pkg = {
|
|
|
12169
12140
|
version: '2'
|
|
12170
12141
|
}
|
|
12171
12142
|
};
|
|
12143
|
+
/**
|
|
12144
|
+
* Expected EIP-712 domain of the ERC-3009 authorization token per
|
|
12145
|
+
* cross-chain-supported source token and source chain.
|
|
12146
|
+
*
|
|
12147
|
+
* The bridge service prepares an ERC-3009 payload for the deposited token. We
|
|
12148
|
+
* keep this table as an explicit SDK allowlist because token deployments can
|
|
12149
|
+
* differ by source chain and by domain name/version. These values mirror the
|
|
12150
|
+
* bridge service's per-chain domain table and the on-chain DOMAIN_SEPARATOR
|
|
12151
|
+
* inputs. Asserted before signing so a tampered prepare response cannot point
|
|
12152
|
+
* the signature at another token or unexpected token domain.
|
|
12153
|
+
*
|
|
12154
|
+
* @internal
|
|
12155
|
+
*/ const EARN_BRIDGE_ERC3009_DOMAINS = {
|
|
12156
|
+
USDC: EARN_BRIDGE_USDC_ERC3009_DOMAINS
|
|
12157
|
+
};
|
|
12172
12158
|
/**
|
|
12173
12159
|
* Maximum tolerated remaining lifetime of a prepared ERC-3009 authorization.
|
|
12174
12160
|
*
|
|
@@ -12628,6 +12614,16 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
12628
12614
|
return approvalTxHash;
|
|
12629
12615
|
}
|
|
12630
12616
|
|
|
12617
|
+
/** @internal */ function isSameAddress(actual, expected) {
|
|
12618
|
+
return actual.toLowerCase() === expected.toLowerCase();
|
|
12619
|
+
}
|
|
12620
|
+
/** @internal */ function assertDecimalPlaces(field, value, tokenDecimals, reason) {
|
|
12621
|
+
const decimals = value.split('.')[1]?.length ?? 0;
|
|
12622
|
+
if (decimals > tokenDecimals) {
|
|
12623
|
+
throw createValidationFailedError(field, value, reason);
|
|
12624
|
+
}
|
|
12625
|
+
}
|
|
12626
|
+
|
|
12631
12627
|
/**
|
|
12632
12628
|
* Build the `tokenInputs` array forwarded to the adapter contract's
|
|
12633
12629
|
* `execute(executeParams, tokenInputs, signature)` call.
|
|
@@ -12651,15 +12647,17 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
12651
12647
|
*
|
|
12652
12648
|
* @example
|
|
12653
12649
|
* ```typescript
|
|
12654
|
-
* const
|
|
12655
|
-
* 'chain.usdcAddress',
|
|
12656
|
-
* chain.usdcAddress,
|
|
12657
|
-
* )
|
|
12658
|
-
*
|
|
12659
|
-
* const tokenInputs = buildEarnTokenInputs(
|
|
12650
|
+
* const approvalToken = resolveEarnApprovalToken(
|
|
12660
12651
|
* executionPayload.executionParams,
|
|
12661
|
-
* approvedToken,
|
|
12662
12652
|
* )
|
|
12653
|
+
*
|
|
12654
|
+
* const tokenInputs =
|
|
12655
|
+
* approvalToken === undefined
|
|
12656
|
+
* ? []
|
|
12657
|
+
* : buildEarnTokenInputs(
|
|
12658
|
+
* executionPayload.executionParams,
|
|
12659
|
+
* approvalToken,
|
|
12660
|
+
* )
|
|
12663
12661
|
* ```
|
|
12664
12662
|
*
|
|
12665
12663
|
* @internal
|
|
@@ -12671,7 +12669,7 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
12671
12669
|
return;
|
|
12672
12670
|
}
|
|
12673
12671
|
const { tokenIn } = instruction;
|
|
12674
|
-
if (tokenIn
|
|
12672
|
+
if (!isSameAddress(tokenIn, approvedToken)) {
|
|
12675
12673
|
throw createValidationFailedError(`executionParams.instructions[${index.toString()}].tokenIn`, tokenIn, 'tokenIn must match the token approved for adapter spending');
|
|
12676
12674
|
}
|
|
12677
12675
|
tokenInputs.push({
|
|
@@ -12683,6 +12681,33 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
12683
12681
|
});
|
|
12684
12682
|
return tokenInputs;
|
|
12685
12683
|
}
|
|
12684
|
+
/**
|
|
12685
|
+
* Resolve the single token that needs adapter allowance from signed Earn
|
|
12686
|
+
* instructions.
|
|
12687
|
+
*
|
|
12688
|
+
* @param executionParams - Service-signed `ExecutionParams` forwarded to the adapter.
|
|
12689
|
+
* @returns The token requested by positive approval instructions, or `undefined`.
|
|
12690
|
+
* @throws {@link KitError} If positive approval instructions reference multiple tokens.
|
|
12691
|
+
*
|
|
12692
|
+
* @internal
|
|
12693
|
+
*/ function resolveEarnApprovalToken(executionParams) {
|
|
12694
|
+
let approvedToken;
|
|
12695
|
+
executionParams.instructions.forEach((instruction, index)=>{
|
|
12696
|
+
const amount = BigInt(instruction.amountToApprove);
|
|
12697
|
+
if (amount <= 0n) {
|
|
12698
|
+
return;
|
|
12699
|
+
}
|
|
12700
|
+
const { tokenIn } = instruction;
|
|
12701
|
+
if (approvedToken === undefined) {
|
|
12702
|
+
approvedToken = tokenIn;
|
|
12703
|
+
return;
|
|
12704
|
+
}
|
|
12705
|
+
if (!isSameAddress(tokenIn, approvedToken)) {
|
|
12706
|
+
throw createValidationFailedError(`executionParams.instructions[${index.toString()}].tokenIn`, tokenIn, 'tokenIn must match the token approved for adapter spending');
|
|
12707
|
+
}
|
|
12708
|
+
});
|
|
12709
|
+
return approvedToken;
|
|
12710
|
+
}
|
|
12686
12711
|
|
|
12687
12712
|
/**
|
|
12688
12713
|
* Prepare an earn adapter action, execute it, wait for confirmation, and
|
|
@@ -13174,6 +13199,7 @@ const hexSignatureSchema = evmSignatureSchema;
|
|
|
13174
13199
|
const hexAddressSchema = evmAddressSchema;
|
|
13175
13200
|
// '0x' prefix + 32 bytes * 2 hex chars.
|
|
13176
13201
|
const BYTES32_HEX_LENGTH = 66;
|
|
13202
|
+
const bridgeFeeTokenSchema = hexAddressSchema;
|
|
13177
13203
|
/**
|
|
13178
13204
|
* Zod schema for a non-negative uint256-like value.
|
|
13179
13205
|
*
|
|
@@ -13460,14 +13486,12 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
13460
13486
|
*
|
|
13461
13487
|
* The bridge prepare path returns one item per collected source fee (e.g.
|
|
13462
13488
|
* `PRE_FINALITY` for the fast-burn fee and `FORWARD` for the destination
|
|
13463
|
-
* forward fee), all denominated in the shared `feeToken`.
|
|
13464
|
-
*
|
|
13465
|
-
* guard parses these to recompute the expected value. Each item's `amount` is
|
|
13466
|
-
* a base-unit decimal string in `feeToken` units.
|
|
13489
|
+
* forward fee), all denominated in the shared `feeToken`. Each item's `amount`
|
|
13490
|
+
* is a base-unit decimal string in `feeToken` units.
|
|
13467
13491
|
*
|
|
13468
13492
|
* @internal
|
|
13469
13493
|
*/ const bridgeFeeQuoteSchema = zod.z.object({
|
|
13470
|
-
feeToken:
|
|
13494
|
+
feeToken: bridgeFeeTokenSchema,
|
|
13471
13495
|
items: zod.z.array(zod.z.object({
|
|
13472
13496
|
type: zod.z.string(),
|
|
13473
13497
|
amount: uint256LikeSchema
|
|
@@ -13483,6 +13507,12 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
13483
13507
|
erc3009TypedData: erc3009TypedDataSchema,
|
|
13484
13508
|
feeQuote: bridgeFeeQuoteSchema
|
|
13485
13509
|
}).passthrough();
|
|
13510
|
+
const bridgeDepositPrepareReviewSchema = zod.z.object({
|
|
13511
|
+
sourceChain: zod.z.string(),
|
|
13512
|
+
destinationChain: zod.z.string(),
|
|
13513
|
+
amount: amountJsonSchema,
|
|
13514
|
+
vaultAddress: hexAddressSchema
|
|
13515
|
+
}).passthrough();
|
|
13486
13516
|
/**
|
|
13487
13517
|
* Zod schema for the bridge deposit prepare payload.
|
|
13488
13518
|
*
|
|
@@ -13494,7 +13524,7 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
|
|
|
13494
13524
|
execId: bridgeDepositExecIdSchema,
|
|
13495
13525
|
erc3009TypedData: bridgeDepositPreparedBundleSchema,
|
|
13496
13526
|
expiresAt: zod.z.string().datetime(),
|
|
13497
|
-
review:
|
|
13527
|
+
review: bridgeDepositPrepareReviewSchema
|
|
13498
13528
|
});
|
|
13499
13529
|
/**
|
|
13500
13530
|
* Zod schema for the `POST /v1/earnKit/bridge/deposit/prepare` API response.
|
|
@@ -14222,7 +14252,8 @@ function toPositionInfo(data) {
|
|
|
14222
14252
|
return {
|
|
14223
14253
|
execId: response.data.execId,
|
|
14224
14254
|
preparedBundle,
|
|
14225
|
-
expiresAt: response.data.expiresAt
|
|
14255
|
+
expiresAt: response.data.expiresAt,
|
|
14256
|
+
review: response.data.review
|
|
14226
14257
|
};
|
|
14227
14258
|
} catch (error) {
|
|
14228
14259
|
throw parseEarnApiError(error, {
|
|
@@ -14333,47 +14364,34 @@ const VALID_AFTER_CLOCK_SKEW_SECONDS = 60n;
|
|
|
14333
14364
|
* @throws If any typed-data field does not match the expected bridge deposit intent.
|
|
14334
14365
|
* @internal
|
|
14335
14366
|
*/ function assertBridgeDepositTypedDataMatchesIntent(intent) {
|
|
14336
|
-
const { preparedBundle, sourceChain, sourceAddress, sourceTokenAddress, authorizationRecipient, amount, maxFee, tokenDecimals, nowSeconds = BigInt(Math.floor(Date.now() / 1000)) } = intent;
|
|
14367
|
+
const { preparedBundle, sourceChain, sourceAddress, sourceTokenAddress, expectedDomain, authorizationRecipient, amount, maxFee, tokenDecimals, amountRaw, nowSeconds = BigInt(Math.floor(Date.now() / 1000)) } = intent;
|
|
14337
14368
|
const { domain, message } = preparedBundle.erc3009TypedData;
|
|
14338
|
-
// USDC deployments differ on the EIP-712 domain name per chain, so the
|
|
14339
|
-
// expected domain is looked up for the source chain being signed on.
|
|
14340
|
-
const expectedDomain = EARN_BRIDGE_ERC3009_DOMAINS[sourceChain.chain];
|
|
14341
|
-
if (expectedDomain === undefined) {
|
|
14342
|
-
throw createValidationFailedError('sourceChain.chain', sourceChain.chain, 'no expected ERC-3009 domain is configured for the source chain');
|
|
14343
|
-
}
|
|
14344
|
-
if (domain.name !== expectedDomain.name) {
|
|
14345
|
-
throw createValidationFailedError('preparedBundle.erc3009TypedData.domain.name', domain.name, `domain name must be ${expectedDomain.name}`);
|
|
14346
|
-
}
|
|
14347
|
-
if (domain.version !== expectedDomain.version) {
|
|
14348
|
-
throw createValidationFailedError('preparedBundle.erc3009TypedData.domain.version', domain.version, `domain version must be ${expectedDomain.version}`);
|
|
14349
|
-
}
|
|
14350
14369
|
assertSameAddress('preparedBundle.erc3009TypedData.domain.verifyingContract', domain.verifyingContract, sourceTokenAddress, 'verifyingContract must match the source token contract');
|
|
14351
14370
|
assertSameBigInt('preparedBundle.erc3009TypedData.domain.chainId', domain.chainId, BigInt(sourceChain.chainId), 'chainId must match the source chain');
|
|
14371
|
+
assertSameString('preparedBundle.erc3009TypedData.domain.name', domain.name, expectedDomain.name, 'domain name must match the source token');
|
|
14372
|
+
assertSameString('preparedBundle.erc3009TypedData.domain.version', domain.version, expectedDomain.version, 'domain version must match the source token');
|
|
14352
14373
|
assertSameAddress('preparedBundle.erc3009TypedData.message.from', message.from, sourceAddress, 'from must match the source wallet');
|
|
14353
14374
|
assertSameAddress('preparedBundle.erc3009TypedData.message.to', message.to, authorizationRecipient, 'to must match the configured authorization recipient');
|
|
14354
|
-
//
|
|
14355
|
-
//
|
|
14356
|
-
|
|
14357
|
-
|
|
14358
|
-
// token to the source token contract before summing.
|
|
14359
|
-
assertSameAddress('preparedBundle.feeQuote.feeToken', preparedBundle.feeQuote.feeToken, sourceTokenAddress, 'feeQuote.feeToken must match the source token contract');
|
|
14360
|
-
// Recompute the expected value from locally trusted inputs (the caller
|
|
14361
|
-
// principal) plus the bundle's own fee items so the guard accepts fee-bearing
|
|
14362
|
-
// deposits while still rejecting any other value tampering. The fee quote is
|
|
14363
|
-
// service-supplied, so bound it to the caller-accepted quote-derived cap
|
|
14364
|
-
// before including it in the signed value.
|
|
14365
|
-
const principal = Amount.parse(amount, {
|
|
14375
|
+
// Recompute the expected ERC-3009 value from locally trusted inputs. Bridge
|
|
14376
|
+
// source fees are paid in the source ERC-20 and are part of the authorization.
|
|
14377
|
+
assertDecimalPlaces('amount', amount, tokenDecimals, `amount must have at most ${String(tokenDecimals)} decimal places for the cross-chain Earn deposit source token`);
|
|
14378
|
+
const parsedAmount = Amount.parse(amount, {
|
|
14366
14379
|
decimals: tokenDecimals
|
|
14367
14380
|
}).raw;
|
|
14381
|
+
const principal = amountRaw === undefined ? parsedAmount : parseReviewedAmount(amountRaw, parsedAmount);
|
|
14382
|
+
const feeQuote = preparedBundle.feeQuote;
|
|
14368
14383
|
const feeTotal = sumBridgeFeeItems(preparedBundle.feeQuote);
|
|
14384
|
+
assertFeeTokenMatchesSourceToken(feeQuote.feeToken, sourceTokenAddress);
|
|
14385
|
+
const feeDecimals = tokenDecimals;
|
|
14386
|
+
assertDecimalPlaces('maxFee', maxFee, feeDecimals, `maxFee must have at most ${String(feeDecimals)} decimal places for the cross-chain Earn deposit fee token`);
|
|
14369
14387
|
const maxFeeRaw = Amount.parse(maxFee, {
|
|
14370
|
-
decimals:
|
|
14388
|
+
decimals: feeDecimals
|
|
14371
14389
|
}).raw;
|
|
14372
14390
|
if (feeTotal > maxFeeRaw) {
|
|
14373
14391
|
throw createValidationFailedError('preparedBundle.feeQuote.items', feeTotal.toString(), 'fee total must not exceed maxFee in base units');
|
|
14374
14392
|
}
|
|
14375
14393
|
const expectedValue = principal + feeTotal;
|
|
14376
|
-
assertSameBigInt('preparedBundle.erc3009TypedData.message.value', message.value, expectedValue, 'value must match the requested amount plus
|
|
14394
|
+
assertSameBigInt('preparedBundle.erc3009TypedData.message.value', message.value, expectedValue, 'value must match the requested amount plus source-token fees in base units');
|
|
14377
14395
|
const window = checkErc3009ValidityWindow({
|
|
14378
14396
|
validAfter: BigInt(message.validAfter),
|
|
14379
14397
|
validBefore: BigInt(message.validBefore),
|
|
@@ -14447,14 +14465,19 @@ const VALID_AFTER_CLOCK_SKEW_SECONDS = 60n;
|
|
|
14447
14465
|
/**
|
|
14448
14466
|
* Sum the source-collected fee items in a prepared bundle's fee quote.
|
|
14449
14467
|
*
|
|
14450
|
-
* Each item amount is a base-unit value in the shared `feeToken
|
|
14451
|
-
* what the prepare path adds to the principal when building `message.value`.
|
|
14468
|
+
* Each item amount is a base-unit value in the shared `feeToken`.
|
|
14452
14469
|
*
|
|
14453
14470
|
* @param feeQuote - Fee quote parsed from the prepared bundle.
|
|
14454
|
-
* @returns Total fee in
|
|
14471
|
+
* @returns Total fee in fee-token base units (0n when there are no items).
|
|
14455
14472
|
*/ function sumBridgeFeeItems(feeQuote) {
|
|
14456
14473
|
return feeQuote.items.reduce((total, item)=>total + BigInt(item.amount), 0n);
|
|
14457
14474
|
}
|
|
14475
|
+
function assertFeeTokenMatchesSourceToken(feeToken, sourceTokenAddress) {
|
|
14476
|
+
if (isSameAddress(feeToken, sourceTokenAddress)) {
|
|
14477
|
+
return;
|
|
14478
|
+
}
|
|
14479
|
+
throw createValidationFailedError('preparedBundle.feeQuote.feeToken', feeToken, 'fee token must match the cross-chain Earn deposit source token');
|
|
14480
|
+
}
|
|
14458
14481
|
function assertNever(value) {
|
|
14459
14482
|
throw new KitError({
|
|
14460
14483
|
...EarnError.INTERNAL_ERROR,
|
|
@@ -14462,8 +14485,23 @@ function assertNever(value) {
|
|
|
14462
14485
|
message: `Unexpected validity window reason: ${String(value)}`
|
|
14463
14486
|
});
|
|
14464
14487
|
}
|
|
14488
|
+
function parseReviewedAmount(amountRaw, parsedAmount) {
|
|
14489
|
+
let reviewedAmount;
|
|
14490
|
+
try {
|
|
14491
|
+
reviewedAmount = BigInt(amountRaw);
|
|
14492
|
+
} catch {
|
|
14493
|
+
throw createValidationFailedError('prepared.review.amount.raw', amountRaw, 'amount must be a non-negative integer string');
|
|
14494
|
+
}
|
|
14495
|
+
if (reviewedAmount < 0n) {
|
|
14496
|
+
throw createValidationFailedError('prepared.review.amount.raw', amountRaw, 'amount must be a non-negative integer string');
|
|
14497
|
+
}
|
|
14498
|
+
if (reviewedAmount !== parsedAmount) {
|
|
14499
|
+
throw createValidationFailedError('prepared.review.amount.raw', amountRaw, 'review amount must match the requested amount');
|
|
14500
|
+
}
|
|
14501
|
+
return reviewedAmount;
|
|
14502
|
+
}
|
|
14465
14503
|
function assertSameAddress(field, actual, expected, reason) {
|
|
14466
|
-
if (actual
|
|
14504
|
+
if (!isSameAddress(actual, expected)) {
|
|
14467
14505
|
throw createValidationFailedError(field, actual, reason);
|
|
14468
14506
|
}
|
|
14469
14507
|
}
|
|
@@ -14472,6 +14510,11 @@ function assertSameBigInt(field, actual, expected, reason) {
|
|
|
14472
14510
|
throw createValidationFailedError(field, actual, reason);
|
|
14473
14511
|
}
|
|
14474
14512
|
}
|
|
14513
|
+
function assertSameString(field, actual, expected, reason) {
|
|
14514
|
+
if (actual !== expected) {
|
|
14515
|
+
throw createValidationFailedError(field, actual, reason);
|
|
14516
|
+
}
|
|
14517
|
+
}
|
|
14475
14518
|
|
|
14476
14519
|
/**
|
|
14477
14520
|
* Build signed withdrawal instructions via the Earn Service API.
|
|
@@ -14829,6 +14872,9 @@ function toWithdrawalQuoteInfo(data) {
|
|
|
14829
14872
|
'CONFIRMED',
|
|
14830
14873
|
'COMPLETE'
|
|
14831
14874
|
]);
|
|
14875
|
+
// Intentionally built-ins-only: Earn bridge support is limited to SDK-known
|
|
14876
|
+
// token contracts plus the explicit ERC-3009 domain allowlist below.
|
|
14877
|
+
const TOKEN_REGISTRY = createTokenRegistry();
|
|
14832
14878
|
/**
|
|
14833
14879
|
* Build the typed error raised when a cross-chain wait is cancelled via its
|
|
14834
14880
|
* `AbortSignal`. Mirrors `@core/adapter-base`'s `createAbortError` (same
|
|
@@ -15148,24 +15194,20 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
15148
15194
|
}
|
|
15149
15195
|
const { address, chain: apiChain, chainDefinition: chain } = await resolveAdapterContext(params.from);
|
|
15150
15196
|
const adapterContractAddress = requireAdapterContract(chain);
|
|
15151
|
-
const rawUsdcAddress = chain.usdcAddress;
|
|
15152
|
-
if (rawUsdcAddress === null) {
|
|
15153
|
-
throw createUnsupportedTokenError('USDC', chain.name);
|
|
15154
|
-
}
|
|
15155
|
-
const usdcAddress = assertHexAddress('chain.usdcAddress', rawUsdcAddress, `USDC address for chain ${chain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
15156
15197
|
const { adapter } = params.from;
|
|
15198
|
+
const vaultAddress = assertHexAddress('vaultAddress', params.vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
15157
15199
|
const { executionParams, signature } = await this.runPhase(ctx, 'deposit', 'fetchParams', async ()=>fetchDeposit({
|
|
15158
|
-
vaultAddress
|
|
15200
|
+
vaultAddress,
|
|
15159
15201
|
amount: params.amount,
|
|
15160
15202
|
address,
|
|
15161
15203
|
chain: apiChain,
|
|
15162
15204
|
config
|
|
15163
15205
|
}), ()=>undefined);
|
|
15164
15206
|
validateExecutionDeadline(executionParams);
|
|
15165
|
-
const
|
|
15166
|
-
const
|
|
15207
|
+
const approvalToken = resolveEarnApprovalToken(executionParams);
|
|
15208
|
+
const tokenInputs = approvalToken === undefined ? [] : buildEarnTokenInputs(executionParams, approvalToken);
|
|
15167
15209
|
const requiredAllowance = sumTokenInputAmounts(tokenInputs);
|
|
15168
|
-
if (!options.skipApprove && approvalToken !== undefined) {
|
|
15210
|
+
if (!options.skipApprove && approvalToken !== undefined && requiredAllowance > 0n) {
|
|
15169
15211
|
await this.runPhase(ctx, 'approve', 'approve', async ()=>approveAllowanceIfNeeded({
|
|
15170
15212
|
adapter,
|
|
15171
15213
|
chain,
|
|
@@ -15173,7 +15215,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
15173
15215
|
delegate: adapterContractAddress,
|
|
15174
15216
|
address,
|
|
15175
15217
|
requiredAllowance,
|
|
15176
|
-
revertMessage: '
|
|
15218
|
+
revertMessage: 'Earn deposit token approval reverted on-chain'
|
|
15177
15219
|
}), (txHash)=>txHash);
|
|
15178
15220
|
}
|
|
15179
15221
|
const { txHash, explorerUrl } = await this.runPhase(ctx, 'deposit', 'execute', async ()=>executeEarnAction({
|
|
@@ -15192,7 +15234,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
15192
15234
|
kind: 'same-chain',
|
|
15193
15235
|
txHash,
|
|
15194
15236
|
explorerUrl,
|
|
15195
|
-
vaultAddress
|
|
15237
|
+
vaultAddress,
|
|
15196
15238
|
amount: params.amount
|
|
15197
15239
|
};
|
|
15198
15240
|
} catch (error) {
|
|
@@ -15214,13 +15256,6 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
15214
15256
|
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.`);
|
|
15215
15257
|
const destinationAddress = assertHexAddress('to.recipientAddress', params.to.recipientAddress, `Destination address for chain ${destinationChain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
15216
15258
|
const vaultAddress = assertHexAddress('vaultAddress', params.vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
15217
|
-
// The signed ERC-3009 value is denominated in USDC's 6 decimals. More
|
|
15218
|
-
// precise inputs would be silently truncated by Amount.parse, so the
|
|
15219
|
-
// result would claim a larger deposit than was actually authorized.
|
|
15220
|
-
const amountDecimals = params.amount.split('.')[1]?.length ?? 0;
|
|
15221
|
-
if (amountDecimals > TOKEN_DECIMALS_6) {
|
|
15222
|
-
throw createValidationFailedError('amount', params.amount, `amount must have at most ${String(TOKEN_DECIMALS_6)} decimal places for cross-chain Earn deposits`);
|
|
15223
|
-
}
|
|
15224
15259
|
const execId = params.idempotencyKey;
|
|
15225
15260
|
const prepared = await this.runPhase(ctx, 'crossChainDeposit', 'prepare', async ()=>fetchBridgeDepositPrepare({
|
|
15226
15261
|
execId,
|
|
@@ -15240,17 +15275,19 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
15240
15275
|
// the error trace, instead of ending the event stream unannounced
|
|
15241
15276
|
// after the prepare success event.
|
|
15242
15277
|
assertPreparedBundleMatchesTypedDataNonce(prepared.preparedBundle);
|
|
15278
|
+
const sourceToken = resolvePreparedBridgeSourceToken(prepared.preparedBundle, route.sourceChain);
|
|
15279
|
+
validatePreparedReviewAmountDecimals(prepared.review.amount.decimals, sourceToken.decimals);
|
|
15243
15280
|
assertBridgeDepositTypedDataMatchesIntent({
|
|
15244
15281
|
preparedBundle: prepared.preparedBundle,
|
|
15245
15282
|
sourceChain: route.sourceChain,
|
|
15246
15283
|
sourceAddress,
|
|
15247
|
-
sourceTokenAddress:
|
|
15284
|
+
sourceTokenAddress: sourceToken.address,
|
|
15285
|
+
expectedDomain: sourceToken.erc3009Domain,
|
|
15248
15286
|
authorizationRecipient: route.authorizationRecipient,
|
|
15249
15287
|
amount: params.amount,
|
|
15250
15288
|
maxFee: params.maxFee,
|
|
15251
|
-
|
|
15252
|
-
|
|
15253
|
-
tokenDecimals: TOKEN_DECIMALS_6
|
|
15289
|
+
tokenDecimals: sourceToken.decimals,
|
|
15290
|
+
amountRaw: prepared.review.amount.raw
|
|
15254
15291
|
});
|
|
15255
15292
|
return signBridgeDepositAuthorization({
|
|
15256
15293
|
adapter: bridgeSigningAdapter,
|
|
@@ -15473,21 +15510,17 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
15473
15510
|
const { adapter, chain, apiChain, address, vaultAddress, amount, config } = params;
|
|
15474
15511
|
try {
|
|
15475
15512
|
const adapterContractAddress = requireAdapterContract(chain);
|
|
15476
|
-
const
|
|
15477
|
-
if (rawUsdcAddress === null) {
|
|
15478
|
-
throw createUnsupportedTokenError('USDC', chain.name);
|
|
15479
|
-
}
|
|
15480
|
-
const usdcAddress = assertHexAddress('chain.usdcAddress', rawUsdcAddress, `USDC address for chain ${chain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
15513
|
+
const normalizedVaultAddress = assertHexAddress('vaultAddress', vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
15481
15514
|
const { executionParams, signature } = await fetchDeposit({
|
|
15482
|
-
vaultAddress,
|
|
15515
|
+
vaultAddress: normalizedVaultAddress,
|
|
15483
15516
|
amount,
|
|
15484
15517
|
address,
|
|
15485
15518
|
chain: apiChain,
|
|
15486
15519
|
config
|
|
15487
15520
|
});
|
|
15488
15521
|
validateExecutionDeadline(executionParams);
|
|
15489
|
-
const
|
|
15490
|
-
const
|
|
15522
|
+
const approvalToken = resolveEarnApprovalToken(executionParams);
|
|
15523
|
+
const tokenInputs = approvalToken === undefined ? [] : buildEarnTokenInputs(executionParams, approvalToken);
|
|
15491
15524
|
const requiredAllowance = sumTokenInputAmounts(tokenInputs);
|
|
15492
15525
|
return await estimateEarnQuoteGasFees({
|
|
15493
15526
|
adapter,
|
|
@@ -15500,7 +15533,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
15500
15533
|
tokenInputs,
|
|
15501
15534
|
signature
|
|
15502
15535
|
},
|
|
15503
|
-
approval: approvalToken !== undefined ? {
|
|
15536
|
+
approval: approvalToken !== undefined && requiredAllowance > 0n ? {
|
|
15504
15537
|
token: approvalToken,
|
|
15505
15538
|
delegate: adapterContractAddress,
|
|
15506
15539
|
requiredAllowance
|
|
@@ -15744,11 +15777,6 @@ function validateCrossChainDepositRoute(sourceChain, destinationChain) {
|
|
|
15744
15777
|
if (destinationApiChain === undefined) {
|
|
15745
15778
|
throw createUnsupportedCrossChainDepositError(destinationChain, 'destination chain is not enabled for cross-chain Earn deposits');
|
|
15746
15779
|
}
|
|
15747
|
-
const sourceTokenAddressRaw = sourceChain.usdcAddress;
|
|
15748
|
-
if (sourceTokenAddressRaw === null) {
|
|
15749
|
-
throw createUnsupportedTokenError('USDC', sourceChain.name);
|
|
15750
|
-
}
|
|
15751
|
-
const sourceTokenAddress = assertHexAddress('sourceChain.usdcAddress', sourceTokenAddressRaw, `USDC address for chain ${sourceChain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
15752
15780
|
if (sourceChain.cctp == null) {
|
|
15753
15781
|
throw createBridgeRouteNotConfiguredError(sourceChain, 'source chain does not support CCTP');
|
|
15754
15782
|
}
|
|
@@ -15772,10 +15800,50 @@ function validateCrossChainDepositRoute(sourceChain, destinationChain) {
|
|
|
15772
15800
|
destinationApiChain,
|
|
15773
15801
|
destinationDomain: destinationChain.cctp.domain,
|
|
15774
15802
|
sourceChain,
|
|
15775
|
-
sourceTokenAddress,
|
|
15776
15803
|
authorizationRecipient
|
|
15777
15804
|
};
|
|
15778
15805
|
}
|
|
15806
|
+
function resolvePreparedBridgeSourceToken(preparedBundle, sourceChain) {
|
|
15807
|
+
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.`);
|
|
15808
|
+
let sourceToken;
|
|
15809
|
+
try {
|
|
15810
|
+
sourceToken = TOKEN_REGISTRY.resolveByAddress(sourceTokenAddress, sourceChain.chain);
|
|
15811
|
+
} catch {
|
|
15812
|
+
throw createValidationFailedError('preparedBundle.erc3009TypedData.domain.verifyingContract', sourceTokenAddress, `source token ${sourceTokenAddress} is not configured on source chain ${sourceChain.name} for cross-chain Earn deposits`);
|
|
15813
|
+
}
|
|
15814
|
+
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.`);
|
|
15815
|
+
if (!isSameAddress(sourceTokenAddress, registryAddress)) {
|
|
15816
|
+
throw createValidationFailedError('preparedBundle.erc3009TypedData.domain.verifyingContract', sourceTokenAddress, `source token must match the configured ${sourceToken.symbol ?? 'token'} address on source chain ${sourceChain.name}`);
|
|
15817
|
+
}
|
|
15818
|
+
const { symbol } = sourceToken;
|
|
15819
|
+
if (symbol === undefined) {
|
|
15820
|
+
throw createValidationFailedError('preparedBundle.erc3009TypedData.domain.verifyingContract', sourceTokenAddress, `source token ${sourceTokenAddress} is not a registered Earn bridge token on source chain ${sourceChain.name}`);
|
|
15821
|
+
}
|
|
15822
|
+
return {
|
|
15823
|
+
address: sourceTokenAddress,
|
|
15824
|
+
erc3009Domain: resolveBridgeErc3009Domain(symbol, sourceChain),
|
|
15825
|
+
decimals: sourceToken.decimals
|
|
15826
|
+
};
|
|
15827
|
+
}
|
|
15828
|
+
function validatePreparedReviewAmountDecimals(reviewedDecimals, tokenDecimals) {
|
|
15829
|
+
if (reviewedDecimals !== tokenDecimals) {
|
|
15830
|
+
throw createValidationFailedError('prepared.review.amount.decimals', reviewedDecimals, `review amount decimals must match the cross-chain Earn deposit source token decimals ${String(tokenDecimals)}`);
|
|
15831
|
+
}
|
|
15832
|
+
}
|
|
15833
|
+
function resolveBridgeErc3009Domain(tokenSymbol, sourceChain) {
|
|
15834
|
+
if (!isEarnBridgeErc3009TokenSymbol(tokenSymbol)) {
|
|
15835
|
+
throw createValidationFailedError('sourceToken.symbol', tokenSymbol, `ERC-3009 domain is not configured for ${tokenSymbol} on source chain ${sourceChain.name}`);
|
|
15836
|
+
}
|
|
15837
|
+
const domainsByChain = EARN_BRIDGE_ERC3009_DOMAINS[tokenSymbol];
|
|
15838
|
+
const domain = domainsByChain[sourceChain.chain];
|
|
15839
|
+
if (domain === undefined) {
|
|
15840
|
+
throw createValidationFailedError('sourceToken.symbol', tokenSymbol, `ERC-3009 domain is not configured for ${tokenSymbol} on source chain ${sourceChain.name}`);
|
|
15841
|
+
}
|
|
15842
|
+
return domain;
|
|
15843
|
+
}
|
|
15844
|
+
function isEarnBridgeErc3009TokenSymbol(tokenSymbol) {
|
|
15845
|
+
return EARN_BRIDGE_ERC3009_TOKEN_SYMBOLS.includes(tokenSymbol);
|
|
15846
|
+
}
|
|
15779
15847
|
function createUnsupportedCrossChainDepositError(chain, reason) {
|
|
15780
15848
|
return new KitError({
|
|
15781
15849
|
...EarnError.UNSUPPORTED_CHAIN,
|
|
@@ -15848,7 +15916,6 @@ function createUnsupportedCrossChainDepositError(chain, reason) {
|
|
|
15848
15916
|
// Untyped callers can pass anything here; without these guards a null
|
|
15849
15917
|
// config crashes with a raw TypeError on `config.providers` instead of
|
|
15850
15918
|
// the structured validation error every other public surface throws.
|
|
15851
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- JS consumers may pass null or a non-object config
|
|
15852
15919
|
if (typeof config !== 'object' || config === null || Array.isArray(config)) {
|
|
15853
15920
|
throw createValidationFailedError('config', config, 'config must be a plain object when provided');
|
|
15854
15921
|
}
|
|
@@ -16215,10 +16282,9 @@ const sourceAdapterContextSchema = zod.z.object({
|
|
|
16215
16282
|
* Schema for validating human-readable decimal amount strings.
|
|
16216
16283
|
*
|
|
16217
16284
|
* Accept positive decimal strings like '100', '100.50', '0.001'. Reject
|
|
16218
|
-
* zero, negative, non-numeric, and non-canonical strings.
|
|
16219
|
-
*
|
|
16220
|
-
*
|
|
16221
|
-
* 7+ decimal-place inputs to the server.
|
|
16285
|
+
* zero, negative, non-numeric, and non-canonical strings. Same-chain
|
|
16286
|
+
* EarnKit vaults can use different asset precisions, so the service enforces
|
|
16287
|
+
* the vault-specific decimal cap after resolving the vault asset.
|
|
16222
16288
|
*
|
|
16223
16289
|
* @internal
|
|
16224
16290
|
*/ const amountSchema = zod.z.string({
|
|
@@ -16227,32 +16293,15 @@ const sourceAdapterContextSchema = zod.z.object({
|
|
|
16227
16293
|
allowZero: false,
|
|
16228
16294
|
regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
|
|
16229
16295
|
attributeName: 'amount',
|
|
16230
|
-
maxDecimals:
|
|
16231
|
-
})(zod.z.string())).refine((value)=>CANONICAL_AMOUNT_REGEX.test(value), AMOUNT_FORMAT_ERROR_MESSAGE);
|
|
16232
|
-
/**
|
|
16233
|
-
* Schema for cross-chain deposit amount strings.
|
|
16234
|
-
*
|
|
16235
|
-
* Cross-chain Earn deposits are USDC-only, and the signed ERC-3009 value is
|
|
16236
|
-
* denominated in USDC's 6 decimals. Reject more precise inputs up front so
|
|
16237
|
-
* the signed value always equals the requested amount instead of a silent
|
|
16238
|
-
* truncation. Non-canonical forms (leading dot, leading zeros) are rejected
|
|
16239
|
-
* too, matching the Earn Service.
|
|
16240
|
-
*
|
|
16241
|
-
* @internal
|
|
16242
|
-
*/ const crossChainAmountSchema = zod.z.string({
|
|
16243
|
-
required_error: 'amount is required'
|
|
16244
|
-
}).min(1, 'amount is required').pipe(createDecimalStringValidator({
|
|
16245
|
-
allowZero: false,
|
|
16246
|
-
regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
|
|
16247
|
-
attributeName: 'amount',
|
|
16248
|
-
maxDecimals: 6
|
|
16296
|
+
maxDecimals: 18
|
|
16249
16297
|
})(zod.z.string())).refine((value)=>CANONICAL_AMOUNT_REGEX.test(value), AMOUNT_FORMAT_ERROR_MESSAGE);
|
|
16250
16298
|
/**
|
|
16251
16299
|
* Schema for cross-chain source fee caps.
|
|
16252
16300
|
*
|
|
16253
|
-
* Cross-chain source fees are denominated in
|
|
16254
|
-
* when the quote contains no source-collected fees; otherwise the cap
|
|
16255
|
-
* the caller-accepted total from a recent quote.
|
|
16301
|
+
* Cross-chain source fees are denominated in the quote's fee token. Zero is
|
|
16302
|
+
* valid when the quote contains no source-collected fees; otherwise the cap
|
|
16303
|
+
* should be the caller-accepted total from a recent quote. The provider
|
|
16304
|
+
* enforces the prepared bundle's fee-token precision before signing.
|
|
16256
16305
|
*
|
|
16257
16306
|
* @internal
|
|
16258
16307
|
*/ const crossChainMaxFeeSchema = zod.z.string({
|
|
@@ -16261,7 +16310,7 @@ const sourceAdapterContextSchema = zod.z.object({
|
|
|
16261
16310
|
allowZero: true,
|
|
16262
16311
|
regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
|
|
16263
16312
|
attributeName: 'maxFee',
|
|
16264
|
-
maxDecimals:
|
|
16313
|
+
maxDecimals: 18
|
|
16265
16314
|
})(zod.z.string())).refine((value)=>CANONICAL_AMOUNT_REGEX.test(value), AMOUNT_FORMAT_ERROR_MESSAGE);
|
|
16266
16315
|
/**
|
|
16267
16316
|
* Build a schema for an optional non-negative decimal filter string.
|
|
@@ -16422,7 +16471,7 @@ const crossChainDepositDestinationSchema = zod.z.object({
|
|
|
16422
16471
|
from: sourceAdapterContextSchema,
|
|
16423
16472
|
to: crossChainDepositDestinationSchema,
|
|
16424
16473
|
vaultAddress: vaultAddressSchema,
|
|
16425
|
-
amount:
|
|
16474
|
+
amount: amountSchema,
|
|
16426
16475
|
maxFee: crossChainMaxFeeSchema,
|
|
16427
16476
|
transferSpeed: zod.z.enum([
|
|
16428
16477
|
'FAST',
|