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