@circle-fin/app-kit 1.16.0 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +79 -0
- package/borrow.cjs +150 -32
- package/borrow.d.cts +38 -9
- package/borrow.d.mts +38 -9
- package/borrow.d.ts +38 -9
- package/borrow.mjs +150 -32
- package/bridge.cjs +454 -53
- package/bridge.d.cts +38 -9
- package/bridge.d.mts +38 -9
- package/bridge.d.ts +38 -9
- package/bridge.mjs +454 -53
- package/chains.cjs +10 -6
- package/chains.d.cts +1 -1
- package/chains.d.mts +1 -1
- package/chains.d.ts +1 -1
- package/chains.mjs +10 -6
- package/context.d.cts +38 -9
- package/context.d.mts +38 -9
- package/context.d.ts +38 -9
- package/earn.cjs +830 -43
- package/earn.d.cts +38 -9
- package/earn.d.mts +38 -9
- package/earn.d.ts +38 -9
- package/earn.mjs +830 -43
- package/estimateBridge.cjs +454 -53
- package/estimateBridge.d.cts +38 -9
- package/estimateBridge.d.mts +38 -9
- package/estimateBridge.d.ts +38 -9
- package/estimateBridge.mjs +454 -53
- package/estimateSwap.cjs +265 -45
- package/estimateSwap.d.cts +86 -9
- package/estimateSwap.d.mts +86 -9
- package/estimateSwap.d.ts +86 -9
- package/estimateSwap.mjs +265 -45
- package/index.cjs +1019 -73
- package/index.d.cts +373 -210
- package/index.d.mts +373 -210
- package/index.d.ts +373 -210
- package/index.mjs +1019 -73
- package/package.json +8 -8
- package/server.cjs +35 -9
- package/server.d.cts +29 -10
- package/server.d.mts +29 -10
- package/server.d.ts +29 -10
- package/server.mjs +35 -9
- package/swap.cjs +265 -45
- package/swap.d.cts +86 -9
- package/swap.d.mts +86 -9
- package/swap.d.ts +86 -9
- package/swap.mjs +265 -45
- package/unifiedBalance.cjs +139 -20
- package/unifiedBalance.mjs +139 -20
package/index.cjs
CHANGED
|
@@ -2811,22 +2811,96 @@ let activeRedactor = defaultMessageRedactor;
|
|
|
2811
2811
|
'0x9147d463': 'Final balance is less than the initial balance',
|
|
2812
2812
|
'0xf4b3b1bc': 'Native token transfer to the beneficiary failed'
|
|
2813
2813
|
};
|
|
2814
|
+
/**
|
|
2815
|
+
* Matches revert data: a 4-byte selector followed by whole ABI-encoded bytes.
|
|
2816
|
+
*/ const REVERT_DATA_PATTERN = /^0x[0-9a-f]{8}(?:[0-9a-f]{2})*$/i;
|
|
2817
|
+
/**
|
|
2818
|
+
* Returns the revert data held by one error in a `cause` chain.
|
|
2819
|
+
*
|
|
2820
|
+
* Browser wallets (EIP-1193) wrap the node's error, so the revert data sits
|
|
2821
|
+
* one level down as `data.data`. viem's `getContractError` reads it the same
|
|
2822
|
+
* way, then keeps it on `ContractFunctionRevertedError.raw` and drops the
|
|
2823
|
+
* node's error from the chain.
|
|
2824
|
+
*
|
|
2825
|
+
* @param link - One error in the `cause` chain
|
|
2826
|
+
* @returns The `raw` or `data` string, or undefined if there is none
|
|
2827
|
+
*/ function getRevertData(link) {
|
|
2828
|
+
if ('raw' in link && typeof link.raw === 'string' && REVERT_DATA_PATTERN.test(link.raw)) {
|
|
2829
|
+
return link.raw;
|
|
2830
|
+
}
|
|
2831
|
+
if (!('data' in link)) {
|
|
2832
|
+
return undefined;
|
|
2833
|
+
}
|
|
2834
|
+
const { data } = link;
|
|
2835
|
+
if (typeof data === 'string') {
|
|
2836
|
+
return data;
|
|
2837
|
+
}
|
|
2838
|
+
if (data !== null && typeof data === 'object' && 'data' in data && typeof data.data === 'string') {
|
|
2839
|
+
return data.data;
|
|
2840
|
+
}
|
|
2841
|
+
return undefined;
|
|
2842
|
+
}
|
|
2843
|
+
/**
|
|
2844
|
+
* Finds the first revert data on the error or in its `cause` chain that
|
|
2845
|
+
* `accept` takes.
|
|
2846
|
+
*
|
|
2847
|
+
* viem keeps the node's revert data on an inner `RpcRequestError`, not on
|
|
2848
|
+
* the error it throws. The `seen` set stops a self-referencing `cause` chain.
|
|
2849
|
+
*
|
|
2850
|
+
* @param error - The error object
|
|
2851
|
+
* @param accept - Returns true for the data shape the caller wants
|
|
2852
|
+
* @returns The data as written on the error, or undefined if not found
|
|
2853
|
+
*/ function findRevertData(error, accept) {
|
|
2854
|
+
const seen = new Set();
|
|
2855
|
+
let current = error;
|
|
2856
|
+
while(current !== null && typeof current === 'object' && !seen.has(current)){
|
|
2857
|
+
seen.add(current);
|
|
2858
|
+
const data = getRevertData(current);
|
|
2859
|
+
if (data !== undefined && accept(data)) {
|
|
2860
|
+
return data;
|
|
2861
|
+
}
|
|
2862
|
+
current = 'cause' in current ? current.cause : undefined;
|
|
2863
|
+
}
|
|
2864
|
+
return undefined;
|
|
2865
|
+
}
|
|
2866
|
+
/**
|
|
2867
|
+
* Finds the raw revert data on an error or anywhere in its `cause` chain.
|
|
2868
|
+
*
|
|
2869
|
+
* Reads `data`, the nested `data.data` from browser wallets, and viem's
|
|
2870
|
+
* `raw`. Only hex of at least 4 bytes counts, so decoded `data` objects and
|
|
2871
|
+
* empty `0x` data are skipped.
|
|
2872
|
+
*
|
|
2873
|
+
* Revert data is public: it is what the contract returned. Keep it when the
|
|
2874
|
+
* rest of a raw error is redacted, so callers can still decode the revert.
|
|
2875
|
+
*
|
|
2876
|
+
* @param error - The raw error from the blockchain library
|
|
2877
|
+
* @returns The revert data in lowercase, or undefined if there is none
|
|
2878
|
+
*
|
|
2879
|
+
* @example
|
|
2880
|
+
* ```typescript
|
|
2881
|
+
* import { extractRevertData } from '@core/errors'
|
|
2882
|
+
*
|
|
2883
|
+
* extractRevertData({ cause: { data: { data: '0xF64059DB' } } })
|
|
2884
|
+
* // Returns: '0xf64059db'
|
|
2885
|
+
* ```
|
|
2886
|
+
*/ function extractRevertData(error) {
|
|
2887
|
+
return findRevertData(error, (data)=>REVERT_DATA_PATTERN.test(data))?.toLowerCase();
|
|
2888
|
+
}
|
|
2814
2889
|
/**
|
|
2815
2890
|
* Attempts to extract an error selector from an error object or message.
|
|
2816
2891
|
*
|
|
2817
2892
|
* Checks multiple sources in priority order:
|
|
2818
|
-
* 1. `
|
|
2893
|
+
* 1. `raw` or `data` field (or nested `data.data`) on the error or anywhere
|
|
2894
|
+
* in its `cause` chain
|
|
2819
2895
|
* 2. Message pattern: "custom error 0x8baa579f"
|
|
2820
2896
|
*
|
|
2821
2897
|
* @param msg - The error message
|
|
2822
2898
|
* @param error - The error object (optional)
|
|
2823
2899
|
* @returns The 4 bytes selector , or undefined if not found
|
|
2824
2900
|
*/ function extractErrorSelector(msg, error) {
|
|
2825
|
-
|
|
2826
|
-
if (
|
|
2827
|
-
|
|
2828
|
-
return error.data.toLowerCase();
|
|
2829
|
-
}
|
|
2901
|
+
const data = findRevertData(error, (value)=>/^0x[0-9a-f]{8}$/i.test(value));
|
|
2902
|
+
if (data !== undefined) {
|
|
2903
|
+
return data.toLowerCase();
|
|
2830
2904
|
}
|
|
2831
2905
|
// Check for "custom error 0x8baa579f" in the message
|
|
2832
2906
|
const match = /custom error (0x[0-9a-f]{8})\b/i.exec(msg);
|
|
@@ -4363,7 +4437,7 @@ let activeRedactor = defaultMessageRedactor;
|
|
|
4363
4437
|
* between input errors (fix your request) and service errors (retry later).
|
|
4364
4438
|
*
|
|
4365
4439
|
* Error code ranges:
|
|
4366
|
-
* - 1100-
|
|
4440
|
+
* - 1100-1107: INPUT errors — invalid, unsupported, or stale request state
|
|
4367
4441
|
* - 8100-8105: SERVICE errors — retryable backend/provider failures
|
|
4368
4442
|
*
|
|
4369
4443
|
* @example
|
|
@@ -4424,6 +4498,15 @@ let activeRedactor = defaultMessageRedactor;
|
|
|
4424
4498
|
name: 'EARN_BRIDGE_QUOTE_EXPIRED',
|
|
4425
4499
|
type: 'INPUT'
|
|
4426
4500
|
},
|
|
4501
|
+
/**
|
|
4502
|
+
* The wallet's vault shares are inside the vault's share lock period, which
|
|
4503
|
+
* starts at deposit. Surfaced as FATAL rather than RETRYABLE because the
|
|
4504
|
+
* lock can last hours, so an immediate retry fails the same way.
|
|
4505
|
+
*/ SHARES_LOCKED: {
|
|
4506
|
+
code: 1107,
|
|
4507
|
+
name: 'EARN_SHARES_LOCKED',
|
|
4508
|
+
type: 'INPUT'
|
|
4509
|
+
},
|
|
4427
4510
|
/** The proxy signing call failed — retryable. */ SIGNING_FAILED: {
|
|
4428
4511
|
code: 8100,
|
|
4429
4512
|
name: 'EARN_SIGNING_FAILED',
|
|
@@ -4665,6 +4748,15 @@ let activeRedactor = defaultMessageRedactor;
|
|
|
4665
4748
|
recoverability: 'RETRYABLE'
|
|
4666
4749
|
}
|
|
4667
4750
|
],
|
|
4751
|
+
// DEPOSIT_INTENT_INVALID: the Veda deposit intent is missing, expired, or
|
|
4752
|
+
// not signed by the depositing wallet.
|
|
4753
|
+
[
|
|
4754
|
+
380418,
|
|
4755
|
+
{
|
|
4756
|
+
errorDef: EarnError.SIGNATURE_REJECTED,
|
|
4757
|
+
recoverability: 'FATAL'
|
|
4758
|
+
}
|
|
4759
|
+
],
|
|
4668
4760
|
// Bridge (380_5XX)
|
|
4669
4761
|
[
|
|
4670
4762
|
380500,
|
|
@@ -5570,6 +5662,8 @@ exports.SwapChain = void 0;
|
|
|
5570
5662
|
SwapChain["HyperEVM"] = "HyperEVM";
|
|
5571
5663
|
SwapChain["Monad"] = "Monad";
|
|
5572
5664
|
SwapChain["Arc"] = "Arc";
|
|
5665
|
+
SwapChain["Cronos"] = "Cronos";
|
|
5666
|
+
SwapChain["Morph"] = "Morph";
|
|
5573
5667
|
// Testnet chains with swap support
|
|
5574
5668
|
SwapChain["Arc_Testnet"] = "Arc_Testnet";
|
|
5575
5669
|
})(exports.SwapChain || (exports.SwapChain = {}));
|
|
@@ -6271,14 +6365,14 @@ exports.BorrowChain = void 0;
|
|
|
6271
6365
|
symbol: 'USDC',
|
|
6272
6366
|
// Arc uses native USDC with 18 decimals for gas payments (EVM standard).
|
|
6273
6367
|
// Note: The ERC-20 USDC contract at usdcAddress uses 6 decimals.
|
|
6274
|
-
// See: https://docs.arc.
|
|
6368
|
+
// See: https://docs.arc.io/arc/references/contract-addresses
|
|
6275
6369
|
decimals: 18
|
|
6276
6370
|
},
|
|
6277
6371
|
chainId: 5042002,
|
|
6278
6372
|
isTestnet: true,
|
|
6279
6373
|
explorerUrl: 'https://testnet.arcscan.app/tx/{hash}',
|
|
6280
6374
|
rpcEndpoints: [
|
|
6281
|
-
'https://rpc.testnet.arc.
|
|
6375
|
+
'https://rpc.testnet.arc.io'
|
|
6282
6376
|
],
|
|
6283
6377
|
eurcAddress: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
|
|
6284
6378
|
usdcAddress: '0x3600000000000000000000000000000000000000',
|
|
@@ -6923,7 +7017,8 @@ exports.BorrowChain = void 0;
|
|
|
6923
7017
|
}
|
|
6924
7018
|
},
|
|
6925
7019
|
kitContracts: {
|
|
6926
|
-
bridge: BRIDGE_CONTRACT_EVM_MAINNET
|
|
7020
|
+
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
7021
|
+
adapter: ADAPTER_CONTRACT_EVM_MAINNET
|
|
6927
7022
|
}
|
|
6928
7023
|
});
|
|
6929
7024
|
|
|
@@ -7792,7 +7887,8 @@ exports.BorrowChain = void 0;
|
|
|
7792
7887
|
}
|
|
7793
7888
|
},
|
|
7794
7889
|
kitContracts: {
|
|
7795
|
-
bridge: BRIDGE_CONTRACT_EVM_MAINNET
|
|
7890
|
+
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
7891
|
+
adapter: ADAPTER_CONTRACT_EVM_MAINNET
|
|
7796
7892
|
}
|
|
7797
7893
|
});
|
|
7798
7894
|
|
|
@@ -10163,8 +10259,8 @@ var Chains = {
|
|
|
10163
10259
|
* Zod schema for validating swap-specific chain identifiers.
|
|
10164
10260
|
*
|
|
10165
10261
|
* Validates chains based on:
|
|
10166
|
-
* -
|
|
10167
|
-
* - Mainnet
|
|
10262
|
+
* - Membership in the SwapChain enum
|
|
10263
|
+
* - Mainnet or explicitly allowlisted testnet
|
|
10168
10264
|
* - At least one supported token available
|
|
10169
10265
|
*
|
|
10170
10266
|
*/ const swapChainIdentifierSchema = zod.z.union([
|
|
@@ -10806,7 +10902,7 @@ const swapTokenEnumSchema = zod.z.enum([
|
|
|
10806
10902
|
*
|
|
10807
10903
|
* A chain supports swaps if ALL conditions are met:
|
|
10808
10904
|
* 1. Is a member of the {@link SwapChain} enum
|
|
10809
|
-
* 2. Has
|
|
10905
|
+
* 2. Has an Adapter contract configured directly or through CCTP v2
|
|
10810
10906
|
*
|
|
10811
10907
|
* Testnets are allowed only when explicitly listed in the
|
|
10812
10908
|
* {@link SwapChain} enum (e.g., Arc_Testnet).
|
|
@@ -10827,14 +10923,14 @@ const swapTokenEnumSchema = zod.z.enum([
|
|
|
10827
10923
|
* @remarks
|
|
10828
10924
|
* Chains automatically become swap-enabled when:
|
|
10829
10925
|
* - Added to the {@link SwapChain} enum
|
|
10830
|
-
* - Adapter contract is deployed
|
|
10926
|
+
* - An Adapter contract is deployed and configured
|
|
10831
10927
|
*
|
|
10832
10928
|
* No code changes needed when new chains are added!
|
|
10833
10929
|
*/ function isSwapSupportedChain(chain) {
|
|
10834
10930
|
if (!(chain.chain in exports.SwapChain)) {
|
|
10835
10931
|
return false;
|
|
10836
10932
|
}
|
|
10837
|
-
if (!isCCTPV2Supported(chain)) {
|
|
10933
|
+
if (!isCCTPV2Supported(chain) && !hasCustomContractSupport(chain, 'adapter')) {
|
|
10838
10934
|
return false;
|
|
10839
10935
|
}
|
|
10840
10936
|
return true;
|
|
@@ -10845,7 +10941,7 @@ const swapTokenEnumSchema = zod.z.enum([
|
|
|
10845
10941
|
*
|
|
10846
10942
|
* Dynamically filter chain definitions to include only those that:
|
|
10847
10943
|
* - Are members of the {@link SwapChain} enum
|
|
10848
|
-
* - Have
|
|
10944
|
+
* - Have an Adapter contract configured directly or through CCTP v2
|
|
10849
10945
|
*
|
|
10850
10946
|
* This function is pure and accepts chains as parameter to avoid
|
|
10851
10947
|
* circular dependencies.
|
|
@@ -12370,11 +12466,13 @@ function parseOrThrow(value, schema, context) {
|
|
|
12370
12466
|
[exports.Blockchain.Arbitrum]: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
|
|
12371
12467
|
[exports.Blockchain.Avalanche]: '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7',
|
|
12372
12468
|
[exports.Blockchain.Celo]: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e',
|
|
12469
|
+
[exports.Blockchain.Cronos]: '0x66e428c3f67a68878562e79A0234c1F83c208770',
|
|
12373
12470
|
[exports.Blockchain.Ethereum]: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
12374
12471
|
[exports.Blockchain.HyperEVM]: '0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb',
|
|
12375
12472
|
[exports.Blockchain.Ink]: '0x0200C29006150606B650577BBE7B6248F58470c1',
|
|
12376
12473
|
[exports.Blockchain.Linea]: '0xA219439258ca9da29E9Cc4cE5596924745e12B93',
|
|
12377
12474
|
[exports.Blockchain.Monad]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
|
|
12475
|
+
[exports.Blockchain.Morph]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
|
|
12378
12476
|
[exports.Blockchain.NEAR]: 'usdt.tether-token.near',
|
|
12379
12477
|
[exports.Blockchain.Optimism]: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
|
|
12380
12478
|
[exports.Blockchain.Polkadot_Asset_Hub]: '1984',
|
|
@@ -12433,6 +12531,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
12433
12531
|
[exports.Blockchain.Arbitrum]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
|
|
12434
12532
|
[exports.Blockchain.Avalanche]: '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70',
|
|
12435
12533
|
[exports.Blockchain.Base]: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
|
|
12534
|
+
[exports.Blockchain.Cronos]: '0xF2001B145b43032AAF5Ee2884e456CCd805F677D',
|
|
12436
12535
|
[exports.Blockchain.Ethereum]: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
|
|
12437
12536
|
[exports.Blockchain.Linea]: '0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5',
|
|
12438
12537
|
[exports.Blockchain.Optimism]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
|
|
@@ -12495,7 +12594,9 @@ function parseOrThrow(value, schema, context) {
|
|
|
12495
12594
|
[exports.Blockchain.Arbitrum]: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
|
|
12496
12595
|
[exports.Blockchain.Avalanche]: '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB',
|
|
12497
12596
|
[exports.Blockchain.Base]: '0x4200000000000000000000000000000000000006',
|
|
12597
|
+
[exports.Blockchain.Cronos]: '0xe44Fd7fCb2b1581822D0c862B68222998a0c299a',
|
|
12498
12598
|
[exports.Blockchain.Ethereum]: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
|
|
12599
|
+
[exports.Blockchain.Morph]: '0x5300000000000000000000000000000000000011',
|
|
12499
12600
|
[exports.Blockchain.Optimism]: '0x4200000000000000000000000000000000000006',
|
|
12500
12601
|
[exports.Blockchain.Polygon]: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
|
|
12501
12602
|
[exports.Blockchain.Solana]: '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs'
|
|
@@ -12514,6 +12615,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
12514
12615
|
locators: {
|
|
12515
12616
|
[exports.Blockchain.Ethereum]: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
|
|
12516
12617
|
[exports.Blockchain.Arbitrum]: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f',
|
|
12618
|
+
[exports.Blockchain.Cronos]: '0x062E66477Faf219F25D27dCED647BF57C3107d52',
|
|
12517
12619
|
[exports.Blockchain.Avalanche]: '0x50b7545627a5162F82A992c33b87aDc75187B218'
|
|
12518
12620
|
}
|
|
12519
12621
|
};
|
|
@@ -14588,7 +14690,7 @@ function resolveOptions(options) {
|
|
|
14588
14690
|
}
|
|
14589
14691
|
|
|
14590
14692
|
var name$5 = "@circle-fin/bridge-kit";
|
|
14591
|
-
var version$6 = "1.
|
|
14693
|
+
var version$6 = "1.16.0";
|
|
14592
14694
|
var pkg$6 = {
|
|
14593
14695
|
name: name$5,
|
|
14594
14696
|
version: version$6};
|
|
@@ -16310,6 +16412,53 @@ const EURC_MAINNET_TOKEN_ID = '0x6ca9e29fa53becc29becaf4a90b9ca7a995ad4d2234880d
|
|
|
16310
16412
|
}
|
|
16311
16413
|
};
|
|
16312
16414
|
|
|
16415
|
+
/**
|
|
16416
|
+
* Map a CCTPx bytes32 token id back to its known display symbol.
|
|
16417
|
+
*
|
|
16418
|
+
* The inverse of the provider's internal symbol -> id resolution: a bridge
|
|
16419
|
+
* resolves a symbol such as `'cirBTC'` to its canonical bytes32 id and reports
|
|
16420
|
+
* that id on `result.token`, dropping the symbol. This helper recovers the
|
|
16421
|
+
* symbol for display. It is scoped to {@link KNOWN_TOKEN_SYMBOLS} — the same
|
|
16422
|
+
* known set the forward resolution covers — and is keyed by network, because a
|
|
16423
|
+
* symbol names a separate registration on each network, whose ids are
|
|
16424
|
+
* unrelated.
|
|
16425
|
+
*
|
|
16426
|
+
* The lookup is best-effort by design: an arbitrary registry token, a
|
|
16427
|
+
* non-canonical bridge of a known symbol, or any unrecognized id has no known
|
|
16428
|
+
* symbol and yields `undefined`. The id is matched case-insensitively (bytes32
|
|
16429
|
+
* ids have no checksum casing).
|
|
16430
|
+
*
|
|
16431
|
+
* @param id - The bytes32 token id to decode (e.g. a `result.token` value).
|
|
16432
|
+
* @param network - The network the id belongs to (`'testnet'` or `'mainnet'`);
|
|
16433
|
+
* derive it from the source chain, e.g. `chain.isTestnet ? 'testnet' :
|
|
16434
|
+
* 'mainnet'`.
|
|
16435
|
+
* @returns The matching {@link KnownTokenSymbol}, or `undefined` when `id` is
|
|
16436
|
+
* not one of the known canonical ids for `network`.
|
|
16437
|
+
* @throws A `KitError` (`INPUT_VALIDATION_FAILED`, FATAL) when `id` is not a
|
|
16438
|
+
* string or `network` is not a recognized network.
|
|
16439
|
+
*
|
|
16440
|
+
* @example
|
|
16441
|
+
* ```typescript
|
|
16442
|
+
* import { resolveToKnownTokenSymbol } from '@circle-fin/provider-cctpx'
|
|
16443
|
+
*
|
|
16444
|
+
* const result = await provider.bridge(params) // params.token === 'cirBTC'
|
|
16445
|
+
* const network = result.source.chain.isTestnet ? 'testnet' : 'mainnet'
|
|
16446
|
+
*
|
|
16447
|
+
* resolveToKnownTokenSymbol(result.token, network) // 'cirBTC'
|
|
16448
|
+
* resolveToKnownTokenSymbol('0xdeadbeef…', network) // undefined (not known)
|
|
16449
|
+
* ```
|
|
16450
|
+
*/ function resolveToKnownTokenSymbol(id, network) {
|
|
16451
|
+
if (typeof id !== 'string') {
|
|
16452
|
+
throw createValidationFailedError$1('id', id, 'must be a string');
|
|
16453
|
+
}
|
|
16454
|
+
if (network !== 'testnet' && network !== 'mainnet') {
|
|
16455
|
+
throw createValidationFailedError$1('network', network, "must be 'testnet' or 'mainnet'");
|
|
16456
|
+
}
|
|
16457
|
+
const row = KNOWN_TOKEN_IDS_BY_NETWORK[network];
|
|
16458
|
+
const target = id.toLowerCase();
|
|
16459
|
+
return KNOWN_TOKEN_SYMBOLS.find((symbol)=>row[symbol].toLowerCase() === target);
|
|
16460
|
+
}
|
|
16461
|
+
|
|
16313
16462
|
/**
|
|
16314
16463
|
* Default clock implementation.
|
|
16315
16464
|
*
|
|
@@ -17653,7 +17802,11 @@ function createNoopLogger() {
|
|
|
17653
17802
|
tokenId: bytes32Hex,
|
|
17654
17803
|
domainId: nonNegativeInteger,
|
|
17655
17804
|
tokenAddress: zod.z.string(),
|
|
17656
|
-
tokenManagerAddress: zod.z.string()
|
|
17805
|
+
tokenManagerAddress: zod.z.string(),
|
|
17806
|
+
// Operator-controlled per-chain kill switch (CCTPE-3979). Optional for
|
|
17807
|
+
// back-compat with older IRIS versions that do not surface it. Downstream
|
|
17808
|
+
// consumers in `findCCTPXDeployment` treat a missing value as `true`.
|
|
17809
|
+
enabled: zod.z.boolean().optional()
|
|
17657
17810
|
});
|
|
17658
17811
|
/**
|
|
17659
17812
|
* Schema for one token entry in the token registry response.
|
|
@@ -18137,17 +18290,23 @@ function createNoopLogger() {
|
|
|
18137
18290
|
/**
|
|
18138
18291
|
* Find a token's deployment on the given CCTP domain.
|
|
18139
18292
|
*
|
|
18293
|
+
* A deployment row with `enabled === false` is treated as missing: an IRIS
|
|
18294
|
+
* operator has flipped the per-chain kill switch (CCTPE-3979), so the SDK must
|
|
18295
|
+
* not route through it even though the row still exists in the registry. A
|
|
18296
|
+
* missing or `true` `enabled` value remains routable — the field is optional on
|
|
18297
|
+
* older IRIS versions and absence is read as "allowed".
|
|
18298
|
+
*
|
|
18140
18299
|
* @param token - The registry token whose deployments are searched.
|
|
18141
18300
|
* @param domain - The CCTP domain id to match.
|
|
18142
18301
|
* @returns The matching {@link CCTPXTokenDeployment}, or `undefined` when the
|
|
18143
|
-
* token has no deployment on that domain.
|
|
18302
|
+
* token has no deployment on that domain or the deployment is disabled.
|
|
18144
18303
|
*
|
|
18145
18304
|
* @example
|
|
18146
18305
|
* ```typescript
|
|
18147
18306
|
* const deployment = findCCTPXDeployment(token, 0)
|
|
18148
18307
|
* ```
|
|
18149
18308
|
*/ function findCCTPXDeployment(token, domain) {
|
|
18150
|
-
return token.deployments.find((deployment)=>deployment.domainId === domain);
|
|
18309
|
+
return token.deployments.find((deployment)=>deployment.domainId === domain && deployment.enabled !== false);
|
|
18151
18310
|
}
|
|
18152
18311
|
|
|
18153
18312
|
/**
|
|
@@ -18249,6 +18408,24 @@ function createNoopLogger() {
|
|
|
18249
18408
|
}
|
|
18250
18409
|
}
|
|
18251
18410
|
})
|
|
18411
|
+
],
|
|
18412
|
+
// The token is not deployed on one of the route chains, or an operator has
|
|
18413
|
+
// flipped the per-chain `enabled` kill switch on the IRIS deployment row.
|
|
18414
|
+
// Either way the route is permanently unavailable until the operator intervenes;
|
|
18415
|
+
// a retry at the same source/destination will keep returning this error.
|
|
18416
|
+
[
|
|
18417
|
+
'TOKEN_NOT_SUPPORTED_ON_ROUTE',
|
|
18418
|
+
({ sourceDomain, destinationDomain })=>new KitError({
|
|
18419
|
+
...InputError.UNSUPPORTED_ROUTE,
|
|
18420
|
+
recoverability: 'FATAL',
|
|
18421
|
+
message: 'CCTPx fee quote rejected the route: the token is not supported on ' + 'the source or destination chain (missing deployment or operator-disabled).',
|
|
18422
|
+
cause: {
|
|
18423
|
+
trace: {
|
|
18424
|
+
sourceDomain,
|
|
18425
|
+
destinationDomain
|
|
18426
|
+
}
|
|
18427
|
+
}
|
|
18428
|
+
})
|
|
18252
18429
|
]
|
|
18253
18430
|
]);
|
|
18254
18431
|
/**
|
|
@@ -23505,6 +23682,24 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
|
|
|
23505
23682
|
}
|
|
23506
23683
|
};
|
|
23507
23684
|
|
|
23685
|
+
/**
|
|
23686
|
+
* Check whether a forwarded burn to the given destination asks Circle's
|
|
23687
|
+
* Forwarding Service to set up the recipient's token account before minting.
|
|
23688
|
+
*
|
|
23689
|
+
* Must return `true` exactly for the destinations where
|
|
23690
|
+
* {@link getForwarderHookData} emits the `createAta` frame, so the forwarding
|
|
23691
|
+
* fee quote includes the setup rent whenever the burn requests it.
|
|
23692
|
+
*
|
|
23693
|
+
* @internal
|
|
23694
|
+
* @param chainType - The destination blockchain type.
|
|
23695
|
+
* @returns `true` for Solana destinations, `false` otherwise.
|
|
23696
|
+
*
|
|
23697
|
+
* @example
|
|
23698
|
+
* ```typescript
|
|
23699
|
+
* requiresRecipientSetup('solana') // true
|
|
23700
|
+
* requiresRecipientSetup('evm') // false
|
|
23701
|
+
* ```
|
|
23702
|
+
*/ const requiresRecipientSetup = (chainType)=>chainType === 'solana';
|
|
23508
23703
|
/**
|
|
23509
23704
|
* Build the forwarding `hookData` for a forwarded (Orbit-relayed) CCTP v2 burn,
|
|
23510
23705
|
* tailored to the destination chain.
|
|
@@ -23855,10 +24050,12 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
|
|
|
23855
24050
|
* @param sourceDomain - The source domain
|
|
23856
24051
|
* @param destinationDomain - The destination domain
|
|
23857
24052
|
* @param isTestnet - Whether the request is for a testnet chain
|
|
24053
|
+
* @param includeRecipientSetup - Whether the quote includes recipient account setup rent
|
|
23858
24054
|
* @returns The complete API URL with forward=true query parameter
|
|
23859
|
-
*/ function buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet) {
|
|
24055
|
+
*/ function buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet, includeRecipientSetup) {
|
|
23860
24056
|
const baseUrl = isTestnet ? IRIS_API_SANDBOX_BASE_URL$1 : IRIS_API_BASE_URL$1;
|
|
23861
|
-
|
|
24057
|
+
const recipientSetupQuery = includeRecipientSetup ? '&includeRecipientSetup=true' : '';
|
|
24058
|
+
return `${baseUrl}/v2/burn/USDC/fees/${sourceDomain.toString()}/${destinationDomain.toString()}?forward=true${recipientSetupQuery}`;
|
|
23862
24059
|
}
|
|
23863
24060
|
/**
|
|
23864
24061
|
* Fetches the forwarding fee for a USDC transfer operation with retry and timeout handling.
|
|
@@ -23875,6 +24072,10 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
|
|
|
23875
24072
|
*
|
|
23876
24073
|
* The high fee tier is always used to provide buffer for destination gas fluctuations.
|
|
23877
24074
|
*
|
|
24075
|
+
* Pass `includeRecipientSetup: true` when the burn asks the Forwarding Service to create
|
|
24076
|
+
* the recipient's token account (Solana destinations). The quote then includes the
|
|
24077
|
+
* account rent, which the quote without this flag leaves out.
|
|
24078
|
+
*
|
|
23878
24079
|
* Implements timeout and retry logic to handle network issues and temporary API failures.
|
|
23879
24080
|
* Each HTTP request is given a maximum of 2 seconds before it is aborted.
|
|
23880
24081
|
* We retry up to 10 times, waiting 200ms between attempts.
|
|
@@ -23883,6 +24084,8 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
|
|
|
23883
24084
|
* @param destinationDomain - The destination domain
|
|
23884
24085
|
* @param isTestnet - Whether the request is for a testnet chain
|
|
23885
24086
|
* @param finalityThreshold - The finality threshold to select the fee tier (1000 for FAST, 2000 for SLOW)
|
|
24087
|
+
* @param includeRecipientSetup - Whether to include recipient account setup rent in the
|
|
24088
|
+
* quote. Defaults to `false`.
|
|
23886
24089
|
* @returns The high-tier forwarding fee in USDC minor units (6 decimals)
|
|
23887
24090
|
* @throws Error if the input domains are invalid, the API request fails, returns invalid data, or network errors occur
|
|
23888
24091
|
*
|
|
@@ -23894,8 +24097,11 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
|
|
|
23894
24097
|
*
|
|
23895
24098
|
* // SLOW transfer - use 2000ms finality threshold
|
|
23896
24099
|
* const slowForwardingFee = await fetchForwardingFee(0, 6, false, 2000)
|
|
24100
|
+
*
|
|
24101
|
+
* // Ethereum -> Solana: include the recipient token account rent
|
|
24102
|
+
* const solanaForwardingFee = await fetchForwardingFee(0, 5, false, 1000, true)
|
|
23897
24103
|
* ```
|
|
23898
|
-
*/ async function fetchForwardingFee(sourceDomain, destinationDomain, isTestnet, finalityThreshold) {
|
|
24104
|
+
*/ async function fetchForwardingFee(sourceDomain, destinationDomain, isTestnet, finalityThreshold, includeRecipientSetup = false) {
|
|
23899
24105
|
// Validate source domain
|
|
23900
24106
|
if (!Number.isInteger(sourceDomain) || sourceDomain < 0) {
|
|
23901
24107
|
throw new KitError({
|
|
@@ -23923,7 +24129,7 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
|
|
|
23923
24129
|
});
|
|
23924
24130
|
}
|
|
23925
24131
|
// Fetch the fee tiers from the API with forward=true
|
|
23926
|
-
const url = buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet);
|
|
24132
|
+
const url = buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet, includeRecipientSetup);
|
|
23927
24133
|
const response = await pollApiGet(url, isForwardingFeeResponse, DEFAULT_CONFIG$4);
|
|
23928
24134
|
// Validate that we have at least one fee tier
|
|
23929
24135
|
if (response.length === 0) {
|
|
@@ -26259,7 +26465,7 @@ const mockAttestationMessage = {
|
|
|
26259
26465
|
return step;
|
|
26260
26466
|
}
|
|
26261
26467
|
|
|
26262
|
-
var version$5 = "1.14.
|
|
26468
|
+
var version$5 = "1.14.3";
|
|
26263
26469
|
var pkg$5 = {
|
|
26264
26470
|
version: version$5};
|
|
26265
26471
|
|
|
@@ -27178,9 +27384,34 @@ function assertCCTPV2Config(config) {
|
|
|
27178
27384
|
tokenAddress: source.chain.usdcAddress,
|
|
27179
27385
|
operationContext: sourceOperationContext
|
|
27180
27386
|
});
|
|
27387
|
+
await this.assertAmountCoversRecipientSetupFee(params);
|
|
27181
27388
|
return bridge$1(params, this);
|
|
27182
27389
|
}
|
|
27183
27390
|
/**
|
|
27391
|
+
* Reject a forwarded transfer to Solana whose amount does not exceed the fee.
|
|
27392
|
+
*
|
|
27393
|
+
* CCTP v2 reverts a burn whose `maxFee` is not less than its amount, and the Solana
|
|
27394
|
+
* forwarding fee includes the recipient token account rent. Checking before the
|
|
27395
|
+
* approval avoids sending a transaction that can never be followed by a burn.
|
|
27396
|
+
* Skipped when the caller provides `config.maxFee`.
|
|
27397
|
+
*
|
|
27398
|
+
* @param params - The validated CCTP v2 bridge parameters.
|
|
27399
|
+
* @returns A promise that resolves when the amount covers the fee.
|
|
27400
|
+
* @throws {KitError} INPUT_INVALID_AMOUNT (FATAL) when the fee is greater than or equal
|
|
27401
|
+
* to the amount.
|
|
27402
|
+
*/ async assertAmountCoversRecipientSetupFee(params) {
|
|
27403
|
+
const { destination, amount, config } = params;
|
|
27404
|
+
if (destination.useForwarder !== true || config.maxFee !== undefined || !requiresRecipientSetup(destination.chain.type)) {
|
|
27405
|
+
return;
|
|
27406
|
+
}
|
|
27407
|
+
const { providerFee, forwarderFee } = await this.getMaxFee(params);
|
|
27408
|
+
const maxFee = providerFee + forwarderFee;
|
|
27409
|
+
if (maxFee >= BigInt(amount)) {
|
|
27410
|
+
// Amounts and fees are in USDC minor units (6 decimals)
|
|
27411
|
+
throw createInvalidAmountError(formatUnits(amount, 6), `Amount must be greater than the ${formatUnits(String(maxFee), 6)} USDC fee for forwarded transfers to ${destination.chain.name}, which includes creating the recipient's token account`);
|
|
27412
|
+
}
|
|
27413
|
+
}
|
|
27414
|
+
/**
|
|
27184
27415
|
* Retry a failed or incomplete CCTP v2 bridge operation.
|
|
27185
27416
|
*
|
|
27186
27417
|
* Analyzes the bridge result to determine where to continue and executes
|
|
@@ -27897,6 +28128,9 @@ function assertCCTPV2Config(config) {
|
|
|
27897
28128
|
* | FAST | true | No | maxFee = calculatedBurnFee + forwardingFee |
|
|
27898
28129
|
* | FAST | true | Yes | maxFee = config.maxFee |
|
|
27899
28130
|
*
|
|
28131
|
+
* For forwarded burns to Solana, the forwarding fee includes the rent for creating the
|
|
28132
|
+
* recipient's token account. A caller-provided `maxFee` must cover it too.
|
|
28133
|
+
*
|
|
27900
28134
|
* @param params - The bridge parameters object containing:
|
|
27901
28135
|
* - `source`: The source wallet context with chain definition and adapter
|
|
27902
28136
|
* - `destination`: The destination wallet context with chain definition and adapter
|
|
@@ -27935,6 +28169,7 @@ function assertCCTPV2Config(config) {
|
|
|
27935
28169
|
}
|
|
27936
28170
|
// Get the min finality threshold based on the transfer speed
|
|
27937
28171
|
const minFinalityThreshold = CCTPv2MinFinalityThreshold[transferSpeed];
|
|
28172
|
+
const includeRecipientSetup = requiresRecipientSetup(destination.chain.type);
|
|
27938
28173
|
// SLOW transfer without forwarder: no fee
|
|
27939
28174
|
if (transferSpeed === exports.TransferSpeed.SLOW && !useForwarder) {
|
|
27940
28175
|
return {
|
|
@@ -27944,7 +28179,7 @@ function assertCCTPV2Config(config) {
|
|
|
27944
28179
|
}
|
|
27945
28180
|
// SLOW transfer with forwarder: only forwarding fee
|
|
27946
28181
|
if (transferSpeed === exports.TransferSpeed.SLOW && useForwarder) {
|
|
27947
|
-
const forwarderFee = await fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold);
|
|
28182
|
+
const forwarderFee = await fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold, includeRecipientSetup);
|
|
27948
28183
|
return {
|
|
27949
28184
|
providerFee: 0n,
|
|
27950
28185
|
forwarderFee
|
|
@@ -27954,7 +28189,7 @@ function assertCCTPV2Config(config) {
|
|
|
27954
28189
|
// Fetch fees in parallel when forwarder is enabled
|
|
27955
28190
|
const [scaledBps, forwarderFee] = await Promise.all([
|
|
27956
28191
|
fetchUsdcFastBurnFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet),
|
|
27957
|
-
useForwarder ? fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold) : Promise.resolve(0n)
|
|
28192
|
+
useForwarder ? fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold, includeRecipientSetup) : Promise.resolve(0n)
|
|
27958
28193
|
]);
|
|
27959
28194
|
// Calculate fee proportional to the transfer amount
|
|
27960
28195
|
const amountInMinorUnits = BigInt(amount);
|
|
@@ -28504,6 +28739,21 @@ function assertCCTPV2Config(config) {
|
|
|
28504
28739
|
'bridge_forward'
|
|
28505
28740
|
]
|
|
28506
28741
|
];
|
|
28742
|
+
/**
|
|
28743
|
+
* Provider step names whose `txHash` is the source-chain transaction that
|
|
28744
|
+
* moves funds into the bridge protocol.
|
|
28745
|
+
*
|
|
28746
|
+
* @remarks
|
|
28747
|
+
* The success event carries this hash so the backend can join it to the
|
|
28748
|
+
* on-chain CCTP / CCTPx event for the same transfer. `burn` is the CCTP v2
|
|
28749
|
+
* step (including batched and source-fee flows); `transfer` is the CCTPx
|
|
28750
|
+
* `CrossChainTokenService` step.
|
|
28751
|
+
*
|
|
28752
|
+
* @internal
|
|
28753
|
+
*/ const BRIDGE_SOURCE_TX_STEP_NAMES = [
|
|
28754
|
+
'burn',
|
|
28755
|
+
'transfer'
|
|
28756
|
+
];
|
|
28507
28757
|
|
|
28508
28758
|
/**
|
|
28509
28759
|
* Base URL for Circle's Quote API (hosted in Iris) on mainnet/production.
|
|
@@ -29599,6 +29849,85 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
|
|
|
29599
29849
|
}
|
|
29600
29850
|
return undefined;
|
|
29601
29851
|
}
|
|
29852
|
+
/**
|
|
29853
|
+
* Pick the source-chain transaction hash from a bridge result.
|
|
29854
|
+
*
|
|
29855
|
+
* Walk the steps from the end and return the hash of the most recent
|
|
29856
|
+
* successful source step (`burn` for CCTP v2, `transfer` for CCTPx). Walking
|
|
29857
|
+
* backwards selects the attempt that landed when a retry appended a new
|
|
29858
|
+
* source step after a failed one.
|
|
29859
|
+
*
|
|
29860
|
+
* @param steps - All steps from the bridge result.
|
|
29861
|
+
* @returns The source transaction hash, or `undefined` if no source step recorded one.
|
|
29862
|
+
*
|
|
29863
|
+
* @internal
|
|
29864
|
+
*/ function pickSourceTxHash(steps) {
|
|
29865
|
+
for(let i = steps.length - 1; i >= 0; i--){
|
|
29866
|
+
const step = steps[i];
|
|
29867
|
+
if (step?.txHash != null && step.state === 'success' && BRIDGE_SOURCE_TX_STEP_NAMES.includes(step.name)) {
|
|
29868
|
+
return step.txHash;
|
|
29869
|
+
}
|
|
29870
|
+
}
|
|
29871
|
+
return undefined;
|
|
29872
|
+
}
|
|
29873
|
+
/**
|
|
29874
|
+
* Resolve the token value reported in telemetry.
|
|
29875
|
+
*
|
|
29876
|
+
* CCTPx reports `result.token` as a bytes32 token id, while the backend's
|
|
29877
|
+
* canonical tables key tokens by symbol. Map the canonical id of a known CCTPx
|
|
29878
|
+
* token back to its symbol and pass any other value (USDC, or a registry token
|
|
29879
|
+
* without a known symbol) through unchanged. Registry symbols are not used
|
|
29880
|
+
* because they are not unique across CCTPx tokens. Never throws.
|
|
29881
|
+
*
|
|
29882
|
+
* @param token - The token from a bridge result.
|
|
29883
|
+
* @param sourceChain - The result's source chain; selects testnet or mainnet ids.
|
|
29884
|
+
* @returns The known symbol, or `token` unchanged.
|
|
29885
|
+
*
|
|
29886
|
+
* @internal
|
|
29887
|
+
*/ function toTelemetryToken(token, sourceChain) {
|
|
29888
|
+
try {
|
|
29889
|
+
const network = sourceChain.isTestnet ? 'testnet' : 'mainnet';
|
|
29890
|
+
return resolveToKnownTokenSymbol(token, network) ?? token;
|
|
29891
|
+
} catch {
|
|
29892
|
+
return token;
|
|
29893
|
+
}
|
|
29894
|
+
}
|
|
29895
|
+
/**
|
|
29896
|
+
* Emit the `bridge_bridge` success event for a completed transfer.
|
|
29897
|
+
*
|
|
29898
|
+
* No-ops unless `result.state` is `'success'`. Failed results are already
|
|
29899
|
+
* reported by error telemetry. Never throws: a malformed result (for example
|
|
29900
|
+
* from a custom provider) is reported as a telemetry drop warning and the
|
|
29901
|
+
* caller's result is returned unchanged.
|
|
29902
|
+
*
|
|
29903
|
+
* @param result - The human-readable bridge result returned to the caller.
|
|
29904
|
+
* @param config - Analytics telemetry identity, gated by `disableAnalytics`.
|
|
29905
|
+
*
|
|
29906
|
+
* @internal
|
|
29907
|
+
*/ function emitBridgeSuccessTelemetry(result, config) {
|
|
29908
|
+
if (config.disabled || result.state !== 'success') {
|
|
29909
|
+
return;
|
|
29910
|
+
}
|
|
29911
|
+
try {
|
|
29912
|
+
const txHash = pickSourceTxHash(result.steps);
|
|
29913
|
+
// `JSON.stringify` turns a non-finite number into `null`, which the logs
|
|
29914
|
+
// endpoint's strict schema rejects along with the whole event.
|
|
29915
|
+
const amountIn = Number.parseFloat(result.amount);
|
|
29916
|
+
emitSuccessTelemetry(BRIDGE_EVENT_TYPES.BRIDGE, config, {
|
|
29917
|
+
sourceChain: result.source.chain.chain,
|
|
29918
|
+
destinationChain: result.destination.chain.chain,
|
|
29919
|
+
tokenIn: toTelemetryToken(result.token, result.source.chain),
|
|
29920
|
+
...Number.isFinite(amountIn) && {
|
|
29921
|
+
amountIn
|
|
29922
|
+
},
|
|
29923
|
+
...txHash != null && {
|
|
29924
|
+
txHash
|
|
29925
|
+
}
|
|
29926
|
+
});
|
|
29927
|
+
} catch (telemetryError) {
|
|
29928
|
+
warnTelemetryDrop(BRIDGE_EVENT_TYPES.BRIDGE, telemetryError);
|
|
29929
|
+
}
|
|
29930
|
+
}
|
|
29602
29931
|
/**
|
|
29603
29932
|
* BridgeKit caller component for retry and estimate operations.
|
|
29604
29933
|
*/ const BRIDGE_KIT_CALLER = {
|
|
@@ -29729,6 +30058,10 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
|
|
|
29729
30058
|
/** Whether error telemetry is disabled. */ disableErrorReporting;
|
|
29730
30059
|
/** Per-kit telemetry identity for shared helpers. */ telemetryConfig;
|
|
29731
30060
|
/**
|
|
30061
|
+
* Per-kit telemetry identity for success events. Gated by `disableAnalytics`
|
|
30062
|
+
* independently of `disableErrorReporting`.
|
|
30063
|
+
*/ analyticsTelemetryConfig;
|
|
30064
|
+
/**
|
|
29732
30065
|
* Create a new BridgeKit instance.
|
|
29733
30066
|
*
|
|
29734
30067
|
* @param config - The configuration containing the CCTPv2 provider
|
|
@@ -29755,6 +30088,11 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
|
|
|
29755
30088
|
sdkVersion: pkg$6.version,
|
|
29756
30089
|
disabled: this.disableErrorReporting
|
|
29757
30090
|
};
|
|
30091
|
+
this.analyticsTelemetryConfig = {
|
|
30092
|
+
sdkName: SDK_NAME$4,
|
|
30093
|
+
sdkVersion: pkg$6.version,
|
|
30094
|
+
disabled: config.disableAnalytics === true
|
|
30095
|
+
};
|
|
29758
30096
|
for (const provider of this.providers){
|
|
29759
30097
|
provider.registerDispatcher(this.actionDispatcher);
|
|
29760
30098
|
}
|
|
@@ -29768,7 +30106,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
|
|
|
29768
30106
|
this.actionDispatcher.off(actionOrWildCard, handler);
|
|
29769
30107
|
}
|
|
29770
30108
|
async bridge(params) {
|
|
29771
|
-
|
|
30109
|
+
const bridgeResult = await withErrorTelemetry(async ()=>{
|
|
29772
30110
|
// First validate the parameters
|
|
29773
30111
|
assertBridgeParams(params, bridgeParamsWithChainIdentifierSchema);
|
|
29774
30112
|
// Then resolve chain definitions (includes adapter chain support validation)
|
|
@@ -29805,7 +30143,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
|
|
|
29805
30143
|
emitResultStepErrorTelemetry(failedStep, BRIDGE_STEP_EVENT_MAP, BRIDGE_EVENT_TYPES.BRIDGE, this.telemetryConfig, {
|
|
29806
30144
|
sourceChain: resolveChainName(params.from.chain),
|
|
29807
30145
|
destinationChain: resolveChainName(params.to.chain),
|
|
29808
|
-
tokenIn:
|
|
30146
|
+
tokenIn: toTelemetryToken(result.token, result.source.chain),
|
|
29809
30147
|
...txHash != null && {
|
|
29810
30148
|
txHash
|
|
29811
30149
|
}
|
|
@@ -29817,6 +30155,8 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
|
|
|
29817
30155
|
destinationChain: resolveChainName(params.to.chain),
|
|
29818
30156
|
tokenIn: params.token
|
|
29819
30157
|
});
|
|
30158
|
+
emitBridgeSuccessTelemetry(bridgeResult, this.analyticsTelemetryConfig);
|
|
30159
|
+
return bridgeResult;
|
|
29820
30160
|
}
|
|
29821
30161
|
/**
|
|
29822
30162
|
* Retry a failed or incomplete cross-chain USDC bridge operation.
|
|
@@ -29887,7 +30227,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
|
|
|
29887
30227
|
* )
|
|
29888
30228
|
* ```
|
|
29889
30229
|
*/ async retry(result, context, invocationMeta) {
|
|
29890
|
-
|
|
30230
|
+
const retriedResult = await withErrorTelemetry(async ()=>{
|
|
29891
30231
|
// Cast the providers list to the wide view here. The tuple
|
|
29892
30232
|
// type on `this.providers` retains concrete provider types
|
|
29893
30233
|
// (CCTPv2 narrows `TToken` to `'USDC'`); calling `.retry`
|
|
@@ -29915,7 +30255,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
|
|
|
29915
30255
|
emitResultStepErrorTelemetry(failedStep, BRIDGE_STEP_EVENT_MAP, BRIDGE_EVENT_TYPES.RETRY, this.telemetryConfig, {
|
|
29916
30256
|
sourceChain: result.source.chain.chain,
|
|
29917
30257
|
destinationChain: result.destination.chain.chain,
|
|
29918
|
-
tokenIn: result.token,
|
|
30258
|
+
tokenIn: toTelemetryToken(result.token, result.source.chain),
|
|
29919
30259
|
...txHash != null && {
|
|
29920
30260
|
txHash
|
|
29921
30261
|
}
|
|
@@ -29929,8 +30269,16 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
|
|
|
29929
30269
|
}, BRIDGE_EVENT_TYPES.RETRY, this.telemetryConfig, {
|
|
29930
30270
|
sourceChain: result.source.chain.chain,
|
|
29931
30271
|
destinationChain: result.destination.chain.chain,
|
|
29932
|
-
tokenIn: result.token
|
|
30272
|
+
tokenIn: toTelemetryToken(result.token, result.source.chain)
|
|
29933
30273
|
});
|
|
30274
|
+
// A retry that completes the transfer is the transfer's only success
|
|
30275
|
+
// event, so it reports the same `bridge_bridge` type as `bridge()`. An
|
|
30276
|
+
// input that already succeeded was reported by the call that completed it
|
|
30277
|
+
// (CCTP v2 returns such a result unchanged), so it is not reported again.
|
|
30278
|
+
if (result.state !== 'success') {
|
|
30279
|
+
emitBridgeSuccessTelemetry(retriedResult, this.analyticsTelemetryConfig);
|
|
30280
|
+
}
|
|
30281
|
+
return retriedResult;
|
|
29934
30282
|
}
|
|
29935
30283
|
async estimate(params) {
|
|
29936
30284
|
return withErrorTelemetry(async ()=>{
|
|
@@ -30479,7 +30827,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
|
|
|
30479
30827
|
registerKit(`${pkg$6.name}/${pkg$6.version}`);
|
|
30480
30828
|
|
|
30481
30829
|
var name$4 = "@circle-fin/swap-kit";
|
|
30482
|
-
var version$4 = "1.
|
|
30830
|
+
var version$4 = "1.8.1";
|
|
30483
30831
|
var pkg$4 = {
|
|
30484
30832
|
name: name$4,
|
|
30485
30833
|
version: version$4};
|
|
@@ -30939,6 +31287,64 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
|
|
|
30939
31287
|
}, {
|
|
30940
31288
|
message: 'fee amount must be non-negative (>= 0)'
|
|
30941
31289
|
});
|
|
31290
|
+
/**
|
|
31291
|
+
* Parses a display-only field leniently: a value `schema` rejects is replaced
|
|
31292
|
+
* with `fallback` instead of failing the response that carries it.
|
|
31293
|
+
* Implemented with `preprocess` rather than Zod's `.catch()` because static
|
|
31294
|
+
* analysis misreads `.catch` on the schema chain as an unhandled Promise
|
|
31295
|
+
* (S7785).
|
|
31296
|
+
*/ const withFallback = (schema, fallback)=>zod.z.preprocess((value)=>schema.safeParse(value).success ? value : fallback, schema);
|
|
31297
|
+
const swapRouteTokenSchema = zod.z.object({
|
|
31298
|
+
symbol: zod.z.string(),
|
|
31299
|
+
address: zod.z.string(),
|
|
31300
|
+
decimals: zod.z.number()
|
|
31301
|
+
});
|
|
31302
|
+
const swapRouteMetadataSchema = zod.z.record(zod.z.unknown()).optional();
|
|
31303
|
+
const swapRouteStepSchema = zod.z.object({
|
|
31304
|
+
type: zod.z.string(),
|
|
31305
|
+
tool: zod.z.string(),
|
|
31306
|
+
toolName: zod.z.string(),
|
|
31307
|
+
fromToken: swapRouteTokenSchema,
|
|
31308
|
+
toToken: swapRouteTokenSchema,
|
|
31309
|
+
estimate: zod.z.object({
|
|
31310
|
+
fromAmount: zod.z.string(),
|
|
31311
|
+
toAmount: zod.z.string(),
|
|
31312
|
+
gasCostUSD: zod.z.string().optional()
|
|
31313
|
+
}),
|
|
31314
|
+
metadata: swapRouteMetadataSchema
|
|
31315
|
+
});
|
|
31316
|
+
/**
|
|
31317
|
+
* Strict schema for a `SwapRoute`. Module-private: service responses
|
|
31318
|
+
* parse routes leniently through {@link responseSwapRouteSchema}.
|
|
31319
|
+
*/ const swapRouteSchema = zod.z.object({
|
|
31320
|
+
provider: zod.z.string().min(1, 'provider must be a non-empty string'),
|
|
31321
|
+
steps: zod.z.array(swapRouteStepSchema),
|
|
31322
|
+
metadata: swapRouteMetadataSchema
|
|
31323
|
+
});
|
|
31324
|
+
/**
|
|
31325
|
+
* Route details are display-only, so service responses parse them leniently:
|
|
31326
|
+
* steps or metadata in a shape the SDK does not know are dropped rather than
|
|
31327
|
+
* failing the quote or swap they describe. Steps are all-or-nothing: if any
|
|
31328
|
+
* step is malformed, `steps` is empty, so a route never silently loses a hop.
|
|
31329
|
+
*/ const responseSwapRouteSchema = swapRouteSchema.extend({
|
|
31330
|
+
steps: withFallback(swapRouteSchema.shape.steps, []),
|
|
31331
|
+
metadata: withFallback(swapRouteMetadataSchema, undefined)
|
|
31332
|
+
});
|
|
31333
|
+
const createSwapFeeItemSchema = zod.z.object({
|
|
31334
|
+
token: zod.z.string({
|
|
31335
|
+
required_error: 'fee token is required',
|
|
31336
|
+
invalid_type_error: 'fee token must be a string'
|
|
31337
|
+
}).min(1, 'fee token must be a non-empty string'),
|
|
31338
|
+
amount: feeAmountSchema,
|
|
31339
|
+
decimals: zod.z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
|
|
31340
|
+
symbol: zod.z.string({
|
|
31341
|
+
invalid_type_error: 'fee token symbol must be a string'
|
|
31342
|
+
}).min(1, 'fee token symbol must be a non-empty string').optional(),
|
|
31343
|
+
// Descriptive metadata: a value the SDK cannot use is dropped rather than
|
|
31344
|
+
// failing the response that carries the fee.
|
|
31345
|
+
type: withFallback(zod.z.string().min(1).optional(), undefined),
|
|
31346
|
+
chain: withFallback(zod.z.string().min(1).optional(), undefined)
|
|
31347
|
+
});
|
|
30942
31348
|
/**
|
|
30943
31349
|
* Zod schema for validating API keys.
|
|
30944
31350
|
*
|
|
@@ -31024,10 +31430,7 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
|
|
|
31024
31430
|
/** Quote information with estimated amounts and route */ quote: zod.z.object({
|
|
31025
31431
|
/** Estimated output amount (base units) */ estimatedAmount: zod.z.string().min(1, 'estimatedAmount must be a non-empty string').regex(/^\d+$/, 'estimatedAmount must be a numeric string'),
|
|
31026
31432
|
/** Guaranteed minimum output amount (base units) */ minAmount: zod.z.string().min(1, 'minAmount must be a non-empty string').regex(/^\d+$/, 'minAmount must be a numeric string'),
|
|
31027
|
-
/** Route information from DEX aggregator */ route:
|
|
31028
|
-
provider: zod.z.string().min(1, 'provider must be a non-empty string'),
|
|
31029
|
-
steps: zod.z.array(zod.z.unknown())
|
|
31030
|
-
})
|
|
31433
|
+
/** Route information from DEX aggregator */ route: responseSwapRouteSchema
|
|
31031
31434
|
}),
|
|
31032
31435
|
/** Fee context indicating where fees should be collected */ feeContext: quoteFeeContextSchema
|
|
31033
31436
|
});
|
|
@@ -31243,19 +31646,6 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
31243
31646
|
}).min(1, 'toChain must be a non-empty string if provided').optional(),
|
|
31244
31647
|
apiKey: apiKeySchema.optional()
|
|
31245
31648
|
});
|
|
31246
|
-
/**
|
|
31247
|
-
* Zod schema for validating CreateSwapResponse payloads.
|
|
31248
|
-
*/ const createSwapFeeItemSchema = zod.z.object({
|
|
31249
|
-
token: zod.z.string({
|
|
31250
|
-
required_error: 'fee token is required',
|
|
31251
|
-
invalid_type_error: 'fee token must be a string'
|
|
31252
|
-
}).min(1, 'fee token must be a non-empty string'),
|
|
31253
|
-
amount: feeAmountSchema,
|
|
31254
|
-
decimals: zod.z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
|
|
31255
|
-
symbol: zod.z.string({
|
|
31256
|
-
invalid_type_error: 'fee token symbol must be a string'
|
|
31257
|
-
}).min(1, 'fee token symbol must be a non-empty string').optional()
|
|
31258
|
-
});
|
|
31259
31649
|
/**
|
|
31260
31650
|
* Developer fee item schema with basis field.
|
|
31261
31651
|
*/ const createSwapDeveloperFeeItemSchema = createSwapFeeItemSchema.extend({
|
|
@@ -31393,6 +31783,8 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
31393
31783
|
correlationId: zod.z.preprocess((value)=>zod.z.string().uuid().safeParse(value).success ? value : undefined, zod.z.string().optional()),
|
|
31394
31784
|
config: createSwapRequestBaseSchema.shape.config.optional(),
|
|
31395
31785
|
fees: createSwapFeesSchema.optional(),
|
|
31786
|
+
// A route the SDK cannot parse is dropped instead of failing the swap.
|
|
31787
|
+
route: withFallback(responseSwapRouteSchema.optional(), undefined),
|
|
31396
31788
|
transaction: createSwapTransactionSchema
|
|
31397
31789
|
});
|
|
31398
31790
|
|
|
@@ -31420,6 +31812,25 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
31420
31812
|
* }
|
|
31421
31813
|
* ```
|
|
31422
31814
|
*/ const isGetQuoteResponse = (obj)=>getQuoteResponseSchema.safeParse(obj).success;
|
|
31815
|
+
/**
|
|
31816
|
+
* Parse and transform a GetQuoteResponse object.
|
|
31817
|
+
*
|
|
31818
|
+
* Applies the schema's lenient route and fee parsing: entries the SDK does
|
|
31819
|
+
* not recognise are dropped and undeclared fields are stripped, rather than
|
|
31820
|
+
* passed through to callers as the type guard alone would.
|
|
31821
|
+
*
|
|
31822
|
+
* @param obj - The raw API response object to parse.
|
|
31823
|
+
* @returns The validated and transformed GetQuoteResponse.
|
|
31824
|
+
* @throws If validation fails.
|
|
31825
|
+
*
|
|
31826
|
+
* @example
|
|
31827
|
+
* ```typescript
|
|
31828
|
+
* import { parseGetQuoteResponse } from '@core/service-client'
|
|
31829
|
+
*
|
|
31830
|
+
* const quote = parseGetQuoteResponse(await response.json())
|
|
31831
|
+
* console.log(quote.quote.route.steps.length)
|
|
31832
|
+
* ```
|
|
31833
|
+
*/ const parseGetQuoteResponse = (obj)=>getQuoteResponseSchema.parse(obj);
|
|
31423
31834
|
/**
|
|
31424
31835
|
* Type guard to validate CreateSwapResponse objects.
|
|
31425
31836
|
*
|
|
@@ -31825,7 +32236,9 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
31825
32236
|
}
|
|
31826
32237
|
}
|
|
31827
32238
|
};
|
|
31828
|
-
|
|
32239
|
+
// Use type guard for validation in polling, then parse for transformation
|
|
32240
|
+
const rawResponse = await pollApiGet(url, isGetQuoteResponse, effectiveConfig);
|
|
32241
|
+
return parseGetQuoteResponse(rawResponse);
|
|
31829
32242
|
};
|
|
31830
32243
|
|
|
31831
32244
|
/**
|
|
@@ -40749,6 +41162,16 @@ function resolveFeePayoutChain(tokenIn, tokenOut, sourceChain, destinationChain)
|
|
|
40749
41162
|
name: 'USDC',
|
|
40750
41163
|
version: '2'
|
|
40751
41164
|
},
|
|
41165
|
+
Cronos: {
|
|
41166
|
+
chainId: Cronos.chainId,
|
|
41167
|
+
name: 'USDC',
|
|
41168
|
+
version: '2'
|
|
41169
|
+
},
|
|
41170
|
+
Morph: {
|
|
41171
|
+
chainId: Morph.chainId,
|
|
41172
|
+
name: 'USDC',
|
|
41173
|
+
version: '2'
|
|
41174
|
+
},
|
|
40752
41175
|
Arc_Testnet: {
|
|
40753
41176
|
chainId: ArcTestnet.chainId,
|
|
40754
41177
|
name: 'USDC',
|
|
@@ -42317,6 +42740,12 @@ const TOKEN_REGISTRY$2 = createTokenRegistry();
|
|
|
42317
42740
|
* i.e. 1 + 0.2 + 0.1 = 1.3. Use the greater of (local estimate × this multiplier)
|
|
42318
42741
|
* or the proxy's gasLimit.
|
|
42319
42742
|
*/ const GAS_SAFETY_MULTIPLIER = 1.3;
|
|
42743
|
+
/**
|
|
42744
|
+
* Gas units used to estimate the displayed gas fee when the service returns an
|
|
42745
|
+
* EVM route without a gasLimit, as direct Circle CCTP plans do. Sized from
|
|
42746
|
+
* observed Adapter executions of those plans. Display-only: it never becomes an
|
|
42747
|
+
* execution gas limit.
|
|
42748
|
+
*/ const DEFAULT_EVM_SWAP_GAS_LIMIT = 500_000n;
|
|
42320
42749
|
const TOKEN_REGISTRY$1 = createTokenRegistry();
|
|
42321
42750
|
/**
|
|
42322
42751
|
* Enhances a KitError with transaction details (txHash and explorerUrl).
|
|
@@ -42833,9 +43262,9 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
42833
43262
|
// Calculate estimated gas/transaction fee based on chain type
|
|
42834
43263
|
let estimatedGasFee = '0';
|
|
42835
43264
|
const evmGasLimit = 'gasLimit' in serviceResponse.transaction ? serviceResponse.transaction.gasLimit : undefined;
|
|
42836
|
-
if (chain.type === 'evm'
|
|
43265
|
+
if (chain.type === 'evm') {
|
|
42837
43266
|
// EVM: gasLimit (units) × gasPrice (wei/unit) = estimated gas fee (wei)
|
|
42838
|
-
const gasLimitUnits = BigInt(evmGasLimit);
|
|
43267
|
+
const gasLimitUnits = evmGasLimit === undefined ? DEFAULT_EVM_SWAP_GAS_LIMIT : BigInt(evmGasLimit);
|
|
42839
43268
|
// Use calculateTransactionFee which fetches current gas price and applies buffer
|
|
42840
43269
|
const gasEstimate = await params.from.adapter.calculateTransactionFee(gasLimitUnits, undefined, chain);
|
|
42841
43270
|
// Format the fee from wei to human-readable native currency amount
|
|
@@ -42879,7 +43308,10 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
42879
43308
|
const estimateResult = {
|
|
42880
43309
|
stopLimit: formattedStopLimit,
|
|
42881
43310
|
estimatedOutput: formattedEstimatedOutput,
|
|
42882
|
-
fees
|
|
43311
|
+
fees,
|
|
43312
|
+
...serviceResponse.route !== undefined && {
|
|
43313
|
+
route: serviceResponse.route
|
|
43314
|
+
}
|
|
42883
43315
|
};
|
|
42884
43316
|
return estimateResult;
|
|
42885
43317
|
}
|
|
@@ -43264,7 +43696,7 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
43264
43696
|
* }
|
|
43265
43697
|
* ```
|
|
43266
43698
|
*/ async swap(params) {
|
|
43267
|
-
|
|
43699
|
+
validateServiceSwapParams(params);
|
|
43268
43700
|
const { from, tokenIn, tokenOut, config } = params;
|
|
43269
43701
|
const { adapter, chain } = from;
|
|
43270
43702
|
const destinationChain = params.toChain ?? chain;
|
|
@@ -43401,6 +43833,9 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
43401
43833
|
...swapResultFees !== undefined && {
|
|
43402
43834
|
fees: swapResultFees
|
|
43403
43835
|
},
|
|
43836
|
+
...serviceResponse.route !== undefined && {
|
|
43837
|
+
route: serviceResponse.route
|
|
43838
|
+
},
|
|
43404
43839
|
progress: statusSnapshot.progress,
|
|
43405
43840
|
...statusSnapshot.amountOut !== undefined && {
|
|
43406
43841
|
amountOut: statusSnapshot.amountOut
|
|
@@ -43601,7 +44036,13 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
43601
44036
|
return {
|
|
43602
44037
|
token: formatted.token,
|
|
43603
44038
|
amount: formatted.amount,
|
|
43604
|
-
type
|
|
44039
|
+
type,
|
|
44040
|
+
...fee.type !== undefined && {
|
|
44041
|
+
component: fee.type
|
|
44042
|
+
},
|
|
44043
|
+
...fee.chain !== undefined && {
|
|
44044
|
+
chain: fee.chain
|
|
44045
|
+
}
|
|
43605
44046
|
};
|
|
43606
44047
|
}));
|
|
43607
44048
|
}
|
|
@@ -43622,7 +44063,13 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
|
|
|
43622
44063
|
token: formatted.token,
|
|
43623
44064
|
amount: formatted.amount,
|
|
43624
44065
|
type: 'developer',
|
|
43625
|
-
recipientAddress
|
|
44066
|
+
recipientAddress,
|
|
44067
|
+
...fee.type !== undefined && {
|
|
44068
|
+
component: fee.type
|
|
44069
|
+
},
|
|
44070
|
+
...fee.chain !== undefined && {
|
|
44071
|
+
chain: fee.chain
|
|
44072
|
+
}
|
|
43626
44073
|
};
|
|
43627
44074
|
}));
|
|
43628
44075
|
}
|
|
@@ -43679,6 +44126,7 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
|
|
|
43679
44126
|
* - customFee: Optional fee configuration
|
|
43680
44127
|
* - apiKey: Optional credential string
|
|
43681
44128
|
* - kitKey: Optional credential string (deprecated alias for apiKey)
|
|
44129
|
+
* - batchTransactions: Optional boolean to opt out of atomic batching
|
|
43682
44130
|
*/ const swapConfigSchema = zod.z.object({
|
|
43683
44131
|
allowanceStrategy: allowanceStrategySchema$1.optional(),
|
|
43684
44132
|
slippageBps: zod.z.number().int().min(0).optional(),
|
|
@@ -43689,7 +44137,10 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
|
|
|
43689
44137
|
})(zod.z.string())).optional(),
|
|
43690
44138
|
customFee: swapCustomFeeSchema.optional(),
|
|
43691
44139
|
apiKey: zod.z.string().optional(),
|
|
43692
|
-
kitKey: zod.z.string().optional()
|
|
44140
|
+
kitKey: zod.z.string().optional(),
|
|
44141
|
+
batchTransactions: zod.z.boolean({
|
|
44142
|
+
invalid_type_error: 'batchTransactions must be a boolean'
|
|
44143
|
+
}).optional()
|
|
43693
44144
|
});
|
|
43694
44145
|
const swapDestinationSchema = zod.z.object({
|
|
43695
44146
|
chain: optionalSwapChainIdentifierField,
|
|
@@ -44664,6 +45115,9 @@ const transformAmount = (value, direction, decimals)=>formatUnits(value, decimal
|
|
|
44664
45115
|
...formattedFees && {
|
|
44665
45116
|
fees: formattedFees
|
|
44666
45117
|
},
|
|
45118
|
+
...result.route !== undefined && {
|
|
45119
|
+
route: result.route
|
|
45120
|
+
},
|
|
44667
45121
|
...formattedConfig && {
|
|
44668
45122
|
config: formattedConfig
|
|
44669
45123
|
},
|
|
@@ -44925,6 +45379,9 @@ const transformAmount = (value, direction, decimals)=>formatUnits(value, decimal
|
|
|
44925
45379
|
estimatedOutput: providerEstimate.estimatedOutput,
|
|
44926
45380
|
...providerEstimate.fees !== undefined && {
|
|
44927
45381
|
fees: providerEstimate.fees
|
|
45382
|
+
},
|
|
45383
|
+
...providerEstimate.route !== undefined && {
|
|
45384
|
+
route: providerEstimate.route
|
|
44928
45385
|
}
|
|
44929
45386
|
};
|
|
44930
45387
|
}
|
|
@@ -46604,6 +47061,9 @@ registerKit(`${pkg$4.name}/${pkg$4.version}`);
|
|
|
46604
47061
|
...context.disableErrorReporting != null && {
|
|
46605
47062
|
disableErrorReporting: context.disableErrorReporting
|
|
46606
47063
|
},
|
|
47064
|
+
...context.disableAnalytics != null && {
|
|
47065
|
+
disableAnalytics: context.disableAnalytics
|
|
47066
|
+
},
|
|
46607
47067
|
...context.headers != null && {
|
|
46608
47068
|
headers: context.headers
|
|
46609
47069
|
}
|
|
@@ -46724,7 +47184,7 @@ registerKit(`${pkg$4.name}/${pkg$4.version}`);
|
|
|
46724
47184
|
};
|
|
46725
47185
|
|
|
46726
47186
|
var name$3 = "@circle-fin/earn-kit";
|
|
46727
|
-
var version$3 = "1.
|
|
47187
|
+
var version$3 = "1.10.0";
|
|
46728
47188
|
var pkg$3 = {
|
|
46729
47189
|
name: name$3,
|
|
46730
47190
|
version: version$3};
|
|
@@ -46831,6 +47291,61 @@ const EARN_BRIDGE_USDC_ERC3009_DOMAINS = {
|
|
|
46831
47291
|
*
|
|
46832
47292
|
* @internal
|
|
46833
47293
|
*/ const EARN_BRIDGE_MAX_VALIDITY_SECONDS = 86_400n;
|
|
47294
|
+
/**
|
|
47295
|
+
* Static fields of the EIP-712 domain for the `DepositIntent` typed-data
|
|
47296
|
+
* intent. `chainId` is excluded here — it must come from the deposit's
|
|
47297
|
+
* actual chain (see {@link buildDepositIntentTypedData}), since a mismatched
|
|
47298
|
+
* `chainId` in the domain separator would make every intent signature
|
|
47299
|
+
* invalid.
|
|
47300
|
+
*
|
|
47301
|
+
* `verifyingContract` is intentionally omitted — the intent is verified
|
|
47302
|
+
* off-chain by the Earn Service, not by a deployed contract.
|
|
47303
|
+
*
|
|
47304
|
+
* @internal
|
|
47305
|
+
*/ const DEPOSIT_INTENT_DOMAIN = {
|
|
47306
|
+
name: 'Circle EarnKit',
|
|
47307
|
+
version: '1'
|
|
47308
|
+
};
|
|
47309
|
+
/**
|
|
47310
|
+
* EIP-712 type definition for the `DepositIntent` struct.
|
|
47311
|
+
*
|
|
47312
|
+
* @internal
|
|
47313
|
+
*/ const DEPOSIT_INTENT_TYPES = {
|
|
47314
|
+
DepositIntent: [
|
|
47315
|
+
{
|
|
47316
|
+
name: 'wallet',
|
|
47317
|
+
type: 'address'
|
|
47318
|
+
},
|
|
47319
|
+
{
|
|
47320
|
+
name: 'vault',
|
|
47321
|
+
type: 'address'
|
|
47322
|
+
},
|
|
47323
|
+
{
|
|
47324
|
+
name: 'amount',
|
|
47325
|
+
type: 'uint256'
|
|
47326
|
+
},
|
|
47327
|
+
{
|
|
47328
|
+
name: 'deadline',
|
|
47329
|
+
type: 'uint256'
|
|
47330
|
+
}
|
|
47331
|
+
]
|
|
47332
|
+
};
|
|
47333
|
+
/**
|
|
47334
|
+
* Lifetime of a `DepositIntent` signature, in seconds.
|
|
47335
|
+
*
|
|
47336
|
+
* The Earn Service rejects a deadline more than 300 seconds ahead of its own
|
|
47337
|
+
* clock. 240 seconds leaves room for clock skew between the client and the
|
|
47338
|
+
* service while still covering the deposit request that follows the
|
|
47339
|
+
* signature.
|
|
47340
|
+
*
|
|
47341
|
+
* @internal
|
|
47342
|
+
*/ const DEPOSIT_INTENT_TTL_SECONDS = 240;
|
|
47343
|
+
/**
|
|
47344
|
+
* Protocol value the Earn Service reports for Veda vaults. A deposit into a
|
|
47345
|
+
* Veda vault needs a signed `DepositIntent`.
|
|
47346
|
+
*
|
|
47347
|
+
* @internal
|
|
47348
|
+
*/ const VEDA_PROTOCOL = 'veda';
|
|
46834
47349
|
/**
|
|
46835
47350
|
* Display symbol for vault share tokens.
|
|
46836
47351
|
*
|
|
@@ -48512,6 +49027,187 @@ function isCrossChainDepositStatusStep(step) {
|
|
|
48512
49027
|
return step.name === 'status';
|
|
48513
49028
|
}
|
|
48514
49029
|
|
|
49030
|
+
/**
|
|
49031
|
+
* Selector of `TellerWithMultiAssetSupport__SharesAreLocked()`.
|
|
49032
|
+
*/ const SHARES_LOCKED_SELECTOR = '0xf64059db';
|
|
49033
|
+
/**
|
|
49034
|
+
* Readable reasons for Veda BoringVault Teller and Accountant reverts.
|
|
49035
|
+
*/ const VEDA_REVERT_REASONS = {
|
|
49036
|
+
'0xe0f9e71d': 'The vault is paused',
|
|
49037
|
+
'0x3b3132f6': 'The vault exchange rate is paused',
|
|
49038
|
+
'0xc8bfa33e': 'The vault does not support this asset for this operation',
|
|
49039
|
+
'0x4b37cd2a': 'The asset amount must be greater than zero',
|
|
49040
|
+
'0x3a8c54c4': 'The share amount must be greater than zero',
|
|
49041
|
+
'0x4bd956a8': 'The deposit would mint fewer vault shares than the signed minimum',
|
|
49042
|
+
'0x9883d840': 'The withdrawal would pay out fewer assets than the signed minimum',
|
|
49043
|
+
'0xed32f3bb': 'The deposit would exceed the vault deposit cap',
|
|
49044
|
+
'0x1b8851f0': 'The permit failed and the token allowance is too low',
|
|
49045
|
+
'0x2821264f': 'The vault denied the share transfer'
|
|
49046
|
+
};
|
|
49047
|
+
/**
|
|
49048
|
+
* Matches a 4-byte selector in a message. The lookarounds stop a longer hex
|
|
49049
|
+
* value, such as an address or a hash, from matching.
|
|
49050
|
+
*/ const SELECTOR_IN_TEXT = /(?<![0-9a-fx])0x[0-9a-f]{8}(?![0-9a-f])/gi;
|
|
49051
|
+
function toRecord(value) {
|
|
49052
|
+
return typeof value === 'object' && value !== null ? value : {};
|
|
49053
|
+
}
|
|
49054
|
+
function isVedaSelector(selector) {
|
|
49055
|
+
return selector === SHARES_LOCKED_SELECTOR || selector in VEDA_REVERT_REASONS;
|
|
49056
|
+
}
|
|
49057
|
+
/**
|
|
49058
|
+
* Reads the selector from the revert data on a trace. The `/next` adapters
|
|
49059
|
+
* keep it on `revertData`. The default adapters keep the whole raw error on
|
|
49060
|
+
* `rawError`.
|
|
49061
|
+
*/ function selectorFromData(trace) {
|
|
49062
|
+
const data = typeof trace['revertData'] === 'string' ? trace['revertData'] : extractRevertData(trace['rawError']);
|
|
49063
|
+
const selector = data?.slice(0, 10).toLowerCase();
|
|
49064
|
+
return selector !== undefined && isVedaSelector(selector) ? selector : undefined;
|
|
49065
|
+
}
|
|
49066
|
+
/**
|
|
49067
|
+
* Reads the selector from error text, for wallets that return no revert
|
|
49068
|
+
* data and put the selector or the decoded error name in the message.
|
|
49069
|
+
*/ function selectorFromText(texts) {
|
|
49070
|
+
for (const text of texts){
|
|
49071
|
+
if (typeof text !== 'string') {
|
|
49072
|
+
continue;
|
|
49073
|
+
}
|
|
49074
|
+
if (/SharesAreLocked/i.test(text)) {
|
|
49075
|
+
return SHARES_LOCKED_SELECTOR;
|
|
49076
|
+
}
|
|
49077
|
+
const selector = text.match(SELECTOR_IN_TEXT)?.map((match)=>match.toLowerCase()).find(isVedaSelector);
|
|
49078
|
+
if (selector !== undefined) {
|
|
49079
|
+
return selector;
|
|
49080
|
+
}
|
|
49081
|
+
}
|
|
49082
|
+
return undefined;
|
|
49083
|
+
}
|
|
49084
|
+
/**
|
|
49085
|
+
* Reads a Veda revert from one error, without looking at nested errors.
|
|
49086
|
+
*/ function readVedaRevert(error) {
|
|
49087
|
+
const trace = toRecord(error.cause?.trace);
|
|
49088
|
+
const selector = selectorFromData(trace) ?? selectorFromText([
|
|
49089
|
+
error.message,
|
|
49090
|
+
trace['reason']
|
|
49091
|
+
]);
|
|
49092
|
+
if (selector === undefined) {
|
|
49093
|
+
return undefined;
|
|
49094
|
+
}
|
|
49095
|
+
const chain = trace['chain'];
|
|
49096
|
+
return {
|
|
49097
|
+
selector,
|
|
49098
|
+
chain: typeof chain === 'string' ? chain : undefined
|
|
49099
|
+
};
|
|
49100
|
+
}
|
|
49101
|
+
/**
|
|
49102
|
+
* Returns the errors that a failed batch nests under `trace.cause` or
|
|
49103
|
+
* `trace.error`.
|
|
49104
|
+
*/ function nestedErrors(error) {
|
|
49105
|
+
const trace = toRecord(error.cause?.trace);
|
|
49106
|
+
return [
|
|
49107
|
+
trace['cause'],
|
|
49108
|
+
trace['error']
|
|
49109
|
+
].filter(isKitError);
|
|
49110
|
+
}
|
|
49111
|
+
/**
|
|
49112
|
+
* Finds a Veda revert on the error or on an error nested in its trace.
|
|
49113
|
+
*/ function findVedaRevert(error) {
|
|
49114
|
+
const seen = new Set();
|
|
49115
|
+
const pending = [
|
|
49116
|
+
error
|
|
49117
|
+
];
|
|
49118
|
+
let current = pending.pop();
|
|
49119
|
+
while(current !== undefined){
|
|
49120
|
+
if (!seen.has(current)) {
|
|
49121
|
+
seen.add(current);
|
|
49122
|
+
const revert = readVedaRevert(current);
|
|
49123
|
+
if (revert !== undefined) {
|
|
49124
|
+
return {
|
|
49125
|
+
...revert,
|
|
49126
|
+
nested: current !== error
|
|
49127
|
+
};
|
|
49128
|
+
}
|
|
49129
|
+
pending.push(...nestedErrors(current));
|
|
49130
|
+
}
|
|
49131
|
+
current = pending.pop();
|
|
49132
|
+
}
|
|
49133
|
+
return undefined;
|
|
49134
|
+
}
|
|
49135
|
+
/**
|
|
49136
|
+
* Map a Veda vault revert to an Earn error.
|
|
49137
|
+
*
|
|
49138
|
+
* A Teller `SharesAreLocked()` revert becomes `EARN_SHARES_LOCKED`. Other
|
|
49139
|
+
* known Veda reverts keep their simulation failed or transaction reverted
|
|
49140
|
+
* code and get a readable reason. A failed batch that wraps a Veda revert
|
|
49141
|
+
* keeps its code, and the reason is added to its message. A timed out batch
|
|
49142
|
+
* is returned unchanged, because its outcome is unknown. Any other error is
|
|
49143
|
+
* returned unchanged.
|
|
49144
|
+
*
|
|
49145
|
+
* @param error - The error caught from an earn operation.
|
|
49146
|
+
* @returns The mapped error, or `error` when it is not a Veda revert.
|
|
49147
|
+
*
|
|
49148
|
+
* @example
|
|
49149
|
+
* ```typescript
|
|
49150
|
+
* import { mapVedaRevert } from './vedaRevert'
|
|
49151
|
+
*
|
|
49152
|
+
* const mapped = mapVedaRevert(error)
|
|
49153
|
+
* console.log(mapped.name) // 'EARN_SHARES_LOCKED' for a share lock revert
|
|
49154
|
+
* ```
|
|
49155
|
+
*
|
|
49156
|
+
* @internal
|
|
49157
|
+
*/ function mapVedaRevert(error) {
|
|
49158
|
+
if (error.name === NetworkError.TIMEOUT.name) {
|
|
49159
|
+
return error;
|
|
49160
|
+
}
|
|
49161
|
+
const revert = findVedaRevert(error);
|
|
49162
|
+
if (revert === undefined) {
|
|
49163
|
+
return error;
|
|
49164
|
+
}
|
|
49165
|
+
const trace = toRecord(error.cause?.trace);
|
|
49166
|
+
const chain = typeof trace['chain'] === 'string' ? trace['chain'] : revert.chain ?? 'EVM';
|
|
49167
|
+
if (revert.selector === SHARES_LOCKED_SELECTOR) {
|
|
49168
|
+
return new KitError({
|
|
49169
|
+
...EarnError.SHARES_LOCKED,
|
|
49170
|
+
// The lock can last hours, so an immediate retry fails the same way.
|
|
49171
|
+
recoverability: 'FATAL',
|
|
49172
|
+
message: `Vault shares on ${chain} are still locked after a recent deposit. Wait for the vault's share lock period to end, then try again.`,
|
|
49173
|
+
cause: {
|
|
49174
|
+
trace: {
|
|
49175
|
+
...trace,
|
|
49176
|
+
selector: revert.selector,
|
|
49177
|
+
chain
|
|
49178
|
+
}
|
|
49179
|
+
}
|
|
49180
|
+
});
|
|
49181
|
+
}
|
|
49182
|
+
const readable = VEDA_REVERT_REASONS[revert.selector];
|
|
49183
|
+
if (readable === undefined) {
|
|
49184
|
+
return error;
|
|
49185
|
+
}
|
|
49186
|
+
const reason = `${readable} (${revert.selector})`;
|
|
49187
|
+
if (error.name === OnchainError.SIMULATION_FAILED.name) {
|
|
49188
|
+
return createSimulationFailedError(chain, reason, trace);
|
|
49189
|
+
}
|
|
49190
|
+
if (error.name === OnchainError.TRANSACTION_REVERTED.name) {
|
|
49191
|
+
return createTransactionRevertedError(chain, reason, trace);
|
|
49192
|
+
}
|
|
49193
|
+
if (!revert.nested) {
|
|
49194
|
+
return error;
|
|
49195
|
+
}
|
|
49196
|
+
return new KitError({
|
|
49197
|
+
code: error.code,
|
|
49198
|
+
name: error.name,
|
|
49199
|
+
type: error.type,
|
|
49200
|
+
recoverability: error.recoverability,
|
|
49201
|
+
message: `${error.message} Reason: ${reason}.`,
|
|
49202
|
+
cause: {
|
|
49203
|
+
trace: {
|
|
49204
|
+
...trace,
|
|
49205
|
+
reason
|
|
49206
|
+
}
|
|
49207
|
+
}
|
|
49208
|
+
});
|
|
49209
|
+
}
|
|
49210
|
+
|
|
48515
49211
|
const EARN_OPERATIONS = new Set([
|
|
48516
49212
|
'deposit',
|
|
48517
49213
|
'withdraw',
|
|
@@ -48557,6 +49253,10 @@ const EARN_OPERATIONS = new Set([
|
|
|
48557
49253
|
* `RESUMABLE` so callers know `retry()` can resume from the failed phase. A
|
|
48558
49254
|
* fatal underlying error stays fatal — it cannot be resumed.
|
|
48559
49255
|
*
|
|
49256
|
+
* A Veda vault revert is mapped first (see {@link mapVedaRevert}): a share
|
|
49257
|
+
* lock becomes `EARN_SHARES_LOCKED`, and other known Veda reverts get a
|
|
49258
|
+
* readable reason.
|
|
49259
|
+
*
|
|
48560
49260
|
* Non-{@link KitError} inputs (which should not occur on provider code paths)
|
|
48561
49261
|
* are normalized to a retryable {@link ServiceError.INTERNAL_ERROR}.
|
|
48562
49262
|
*
|
|
@@ -48578,7 +49278,7 @@ const EARN_OPERATIONS = new Set([
|
|
|
48578
49278
|
* }
|
|
48579
49279
|
* ```
|
|
48580
49280
|
*/ function augmentEarnError(error, context) {
|
|
48581
|
-
const base = isKitError(error) ? error : new KitError({
|
|
49281
|
+
const base = isKitError(error) ? mapVedaRevert(error) : new KitError({
|
|
48582
49282
|
...ServiceError.INTERNAL_ERROR,
|
|
48583
49283
|
recoverability: 'RETRYABLE',
|
|
48584
49284
|
message: getErrorMessage(error)
|
|
@@ -49521,7 +50221,7 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
|
|
|
49521
50221
|
}
|
|
49522
50222
|
|
|
49523
50223
|
var name$2 = "@circle-fin/provider-earn-service";
|
|
49524
|
-
var version$2 = "1.
|
|
50224
|
+
var version$2 = "1.8.0";
|
|
49525
50225
|
var pkg$2 = {
|
|
49526
50226
|
name: name$2,
|
|
49527
50227
|
version: version$2};
|
|
@@ -49873,8 +50573,9 @@ function toPositionInfo(data) {
|
|
|
49873
50573
|
* Build signed deposit instructions via the Earn Service API.
|
|
49874
50574
|
*
|
|
49875
50575
|
* Call `POST /v1/earnKit/deposit` with the vault address, amount,
|
|
49876
|
-
* wallet address, and chain
|
|
49877
|
-
* parameters for on-chain
|
|
50576
|
+
* wallet address, and chain, plus the signed `DepositIntent` when one is
|
|
50577
|
+
* given. Return EIP-712 signed execution parameters for on-chain
|
|
50578
|
+
* submission.
|
|
49878
50579
|
*
|
|
49879
50580
|
* @param params - Deposit parameters
|
|
49880
50581
|
* @returns Signed execution parameters for the deposit
|
|
@@ -49888,7 +50589,8 @@ function toPositionInfo(data) {
|
|
|
49888
50589
|
vaultAddress: params.vaultAddress,
|
|
49889
50590
|
amount: params.amount,
|
|
49890
50591
|
address: params.address,
|
|
49891
|
-
chain: params.chain
|
|
50592
|
+
chain: params.chain,
|
|
50593
|
+
...params.intent
|
|
49892
50594
|
};
|
|
49893
50595
|
try {
|
|
49894
50596
|
const response = await pollApiPost(url.toString(), requestBody, isDepositResponse, pollingConfig);
|
|
@@ -49900,6 +50602,232 @@ function toPositionInfo(data) {
|
|
|
49900
50602
|
}
|
|
49901
50603
|
}
|
|
49902
50604
|
|
|
50605
|
+
/**
|
|
50606
|
+
* Build the version-1 EarnKit `DepositIntent` EIP-712 typed data.
|
|
50607
|
+
*
|
|
50608
|
+
* The signed struct binds the depositing `wallet`, the target `vault`, the
|
|
50609
|
+
* deposit `amount` in the asset's smallest unit, and a signature `deadline`.
|
|
50610
|
+
* The Earn Service rebuilds the same struct from the deposit request and
|
|
50611
|
+
* rejects the deposit unless `wallet` signed it. The domain intentionally
|
|
50612
|
+
* omits `verifyingContract` because the service verifies the signature
|
|
50613
|
+
* off-chain.
|
|
50614
|
+
*
|
|
50615
|
+
* `domain.chainId` comes from the deposit's chain rather than a constant: the
|
|
50616
|
+
* domain separator must match what the service rebuilds, and a wrong
|
|
50617
|
+
* `chainId` would make every intent signature invalid.
|
|
50618
|
+
*
|
|
50619
|
+
* This function returns the fully-typed {@link DepositIntentTypedData} with
|
|
50620
|
+
* no assertion. {@link resolveDepositIntent} casts it to the adapter's
|
|
50621
|
+
* generic `EIP712Domain` shape only where it calls `adapter.signTypedData`,
|
|
50622
|
+
* since that shared interface always requires `verifyingContract`. The
|
|
50623
|
+
* adapter's runtime `eip712DomainSchema` treats `verifyingContract` as
|
|
50624
|
+
* optional, so the cast is safe at runtime. BorrowKit's
|
|
50625
|
+
* `buildWebhookRegistrationTypedData` works around the same gap.
|
|
50626
|
+
*
|
|
50627
|
+
* @param params - Wallet, vault, base-unit amount, deadline, and chain id.
|
|
50628
|
+
* @returns EIP-712 typed data ready to be cast for `adapter.signTypedData`.
|
|
50629
|
+
*
|
|
50630
|
+
* @example
|
|
50631
|
+
* ```typescript
|
|
50632
|
+
* const typedData = buildDepositIntentTypedData({
|
|
50633
|
+
* wallet: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
|
|
50634
|
+
* vault: '0x00000000000000000000000000000000000000a1',
|
|
50635
|
+
* amount: 100_000_000n,
|
|
50636
|
+
* deadline: 1_700_000_000n,
|
|
50637
|
+
* chainId: 5_042_002,
|
|
50638
|
+
* })
|
|
50639
|
+
* ```
|
|
50640
|
+
*
|
|
50641
|
+
* @internal
|
|
50642
|
+
*/ function buildDepositIntentTypedData(params) {
|
|
50643
|
+
return {
|
|
50644
|
+
domain: {
|
|
50645
|
+
...DEPOSIT_INTENT_DOMAIN,
|
|
50646
|
+
chainId: params.chainId
|
|
50647
|
+
},
|
|
50648
|
+
types: DEPOSIT_INTENT_TYPES,
|
|
50649
|
+
primaryType: 'DepositIntent',
|
|
50650
|
+
message: {
|
|
50651
|
+
wallet: params.wallet,
|
|
50652
|
+
vault: params.vault,
|
|
50653
|
+
amount: params.amount,
|
|
50654
|
+
deadline: params.deadline
|
|
50655
|
+
}
|
|
50656
|
+
};
|
|
50657
|
+
}
|
|
50658
|
+
/**
|
|
50659
|
+
* Earn Service error code for a missing, expired, or wrongly signed
|
|
50660
|
+
* `DepositIntent` (`DEPOSIT_INTENT_INVALID`).
|
|
50661
|
+
*/ const DEPOSIT_INTENT_INVALID_CODE = 380418;
|
|
50662
|
+
/**
|
|
50663
|
+
* Sign a `DepositIntent` when the target vault needs one.
|
|
50664
|
+
*
|
|
50665
|
+
* A Veda vault mints shares straight to the depositing wallet, so the Earn
|
|
50666
|
+
* Service only builds a Veda deposit for a wallet that signed a
|
|
50667
|
+
* `DepositIntent` for the same vault and amount. The vault lookup exists only
|
|
50668
|
+
* to find out whether the vault is a Veda vault (and its asset decimals, to
|
|
50669
|
+
* sign the amount). Other vaults ignore the intent, so this returns no intent
|
|
50670
|
+
* for them and the wallet sees no extra signature prompt. A vault the service
|
|
50671
|
+
* does not return is also treated as not needing an intent; the deposit call
|
|
50672
|
+
* then reports why.
|
|
50673
|
+
*
|
|
50674
|
+
* A failed vault lookup does not stop the deposit: this returns the lookup
|
|
50675
|
+
* error with no intent, so a non-Veda deposit works as before. A Veda deposit
|
|
50676
|
+
* then gets `DEPOSIT_INTENT_INVALID` from the service, and
|
|
50677
|
+
* {@link resolveDepositError} reports the lookup error instead. The lookup
|
|
50678
|
+
* fails either by throwing or by returning the vault in its `errors` list (for
|
|
50679
|
+
* example `VAULT_REFRESH_BUSY`); both are kept the same way.
|
|
50680
|
+
*
|
|
50681
|
+
* @param params - Adapter, resolved chain, wallet, vault, human-readable
|
|
50682
|
+
* amount, and service config.
|
|
50683
|
+
* @returns The signed intent when the vault needs one, and the lookup error
|
|
50684
|
+
* when the vault lookup failed.
|
|
50685
|
+
* @throws {@link KitError} `InputError.VALIDATION_FAILED` when the vault needs
|
|
50686
|
+
* an intent but the chain is not EVM, the adapter cannot sign typed data, or
|
|
50687
|
+
* the amount has more decimal places than the vault asset.
|
|
50688
|
+
*
|
|
50689
|
+
* @example
|
|
50690
|
+
* ```typescript
|
|
50691
|
+
* const { intent } = await resolveDepositIntent({
|
|
50692
|
+
* adapter,
|
|
50693
|
+
* chain: ArcTestnet,
|
|
50694
|
+
* address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
|
|
50695
|
+
* vaultAddress: '0x00000000000000000000000000000000000000a1',
|
|
50696
|
+
* amount: '100',
|
|
50697
|
+
* config,
|
|
50698
|
+
* })
|
|
50699
|
+
* // intent -> { intentSignature: '0x…', intentDeadline: 1700000240 }
|
|
50700
|
+
* ```
|
|
50701
|
+
*
|
|
50702
|
+
* @internal
|
|
50703
|
+
*/ async function resolveDepositIntent(params) {
|
|
50704
|
+
let lookup;
|
|
50705
|
+
try {
|
|
50706
|
+
lookup = await fetchVaults({
|
|
50707
|
+
// `resolveAdapterContext` already rejected chains the Earn Service does
|
|
50708
|
+
// not support, so the chain is an Earn chain here.
|
|
50709
|
+
vaults: [
|
|
50710
|
+
{
|
|
50711
|
+
chain: params.chain,
|
|
50712
|
+
vaultAddress: params.vaultAddress
|
|
50713
|
+
}
|
|
50714
|
+
],
|
|
50715
|
+
config: params.config
|
|
50716
|
+
});
|
|
50717
|
+
} catch (lookupError) {
|
|
50718
|
+
return {
|
|
50719
|
+
lookupError
|
|
50720
|
+
};
|
|
50721
|
+
}
|
|
50722
|
+
const vaultError = lookup.errors.find((candidate)=>isSameAddress(candidate.vaultAddress, params.vaultAddress));
|
|
50723
|
+
if (vaultError !== undefined) {
|
|
50724
|
+
// Same handling as a thrown lookup error, mapped with the Earn Service
|
|
50725
|
+
// error table so it keeps its recoverability (VAULT_REFRESH_BUSY retries).
|
|
50726
|
+
return {
|
|
50727
|
+
lookupError: parseEarnApiError({
|
|
50728
|
+
responseBody: {
|
|
50729
|
+
code: vaultError.code,
|
|
50730
|
+
message: vaultError.message
|
|
50731
|
+
}
|
|
50732
|
+
}, {
|
|
50733
|
+
operation: 'getVaults'
|
|
50734
|
+
})
|
|
50735
|
+
};
|
|
50736
|
+
}
|
|
50737
|
+
const vault = lookup.vaults.find((candidate)=>isSameAddress(candidate.address, params.vaultAddress));
|
|
50738
|
+
if (vault?.protocol.toLowerCase() !== VEDA_PROTOCOL) {
|
|
50739
|
+
return {};
|
|
50740
|
+
}
|
|
50741
|
+
if (params.chain.type !== 'evm') {
|
|
50742
|
+
throw createValidationFailedError$1('from.chain', params.chain.chain, 'Veda vault deposits need an EVM chain to sign the deposit intent');
|
|
50743
|
+
}
|
|
50744
|
+
if (!canSignTypedData(params.adapter)) {
|
|
50745
|
+
throw createValidationFailedError$1('from.adapter', params.adapter, 'adapter must support signTypedData for Veda vault deposits');
|
|
50746
|
+
}
|
|
50747
|
+
const wallet = assertHexAddress('from.address', params.address, 'Depositor address must be a 0x-prefixed 20-byte hex address.');
|
|
50748
|
+
const assetDecimals = vault.liquidityProfile.totalDeposits.decimals;
|
|
50749
|
+
// Amount.parse truncates extra digits, but the service rejects them. Fail
|
|
50750
|
+
// before the wallet prompt instead of asking to sign a deposit that cannot
|
|
50751
|
+
// go through.
|
|
50752
|
+
assertDecimalPlaces('amount', params.amount, assetDecimals, `amount must have at most ${String(assetDecimals)} decimal places for the vault asset`);
|
|
50753
|
+
const deadline = Math.floor(Date.now() / 1000) + DEPOSIT_INTENT_TTL_SECONDS;
|
|
50754
|
+
const typedData = buildDepositIntentTypedData({
|
|
50755
|
+
wallet,
|
|
50756
|
+
vault: params.vaultAddress,
|
|
50757
|
+
amount: Amount.parse(params.amount, {
|
|
50758
|
+
decimals: assetDecimals
|
|
50759
|
+
}).raw,
|
|
50760
|
+
deadline: BigInt(deadline),
|
|
50761
|
+
chainId: params.chain.chainId
|
|
50762
|
+
});
|
|
50763
|
+
// `typedData.domain` intentionally omits `verifyingContract` (the Earn
|
|
50764
|
+
// Service verifies off-chain), but the shared `signTypedData` signature
|
|
50765
|
+
// requires the standard `EIP712Domain` shape. The adapter's runtime
|
|
50766
|
+
// `eip712DomainSchema` treats `verifyingContract` as optional, so this cast
|
|
50767
|
+
// is safe at runtime.
|
|
50768
|
+
const intentSignature = await params.adapter.signTypedData(typedData, {
|
|
50769
|
+
chain: params.chain,
|
|
50770
|
+
address: wallet
|
|
50771
|
+
});
|
|
50772
|
+
return {
|
|
50773
|
+
intent: {
|
|
50774
|
+
intentSignature,
|
|
50775
|
+
intentDeadline: deadline
|
|
50776
|
+
}
|
|
50777
|
+
};
|
|
50778
|
+
}
|
|
50779
|
+
/**
|
|
50780
|
+
* Pick the error to report for a failed deposit request.
|
|
50781
|
+
*
|
|
50782
|
+
* When the vault lookup failed, the SDK sent the deposit without an intent.
|
|
50783
|
+
* If the service then answers `DEPOSIT_INTENT_INVALID`, the vault is a Veda
|
|
50784
|
+
* vault and the real cause is the failed lookup, not a bad signature. Report
|
|
50785
|
+
* the lookup error, which keeps its own recoverability (often retryable),
|
|
50786
|
+
* instead of a fatal signature rejection.
|
|
50787
|
+
*
|
|
50788
|
+
* When the SDK did send an intent and the service still answers
|
|
50789
|
+
* `DEPOSIT_INTENT_INVALID`, the error stays a fatal signature rejection, but
|
|
50790
|
+
* its message adds that the intent may have expired: the deadline starts
|
|
50791
|
+
* before the wallet prompt, so a slow signer can submit an expired intent.
|
|
50792
|
+
* Calling `deposit()` again signs a new one. Every other error is reported as
|
|
50793
|
+
* is.
|
|
50794
|
+
*
|
|
50795
|
+
* @param resolution - The result of {@link resolveDepositIntent}.
|
|
50796
|
+
* @param error - The error from the deposit request.
|
|
50797
|
+
* @returns The error to throw.
|
|
50798
|
+
*
|
|
50799
|
+
* @example
|
|
50800
|
+
* ```typescript
|
|
50801
|
+
* try {
|
|
50802
|
+
* return await fetchDeposit({ ...params, intent: resolution.intent })
|
|
50803
|
+
* } catch (error) {
|
|
50804
|
+
* throw resolveDepositError(resolution, error)
|
|
50805
|
+
* }
|
|
50806
|
+
* ```
|
|
50807
|
+
*
|
|
50808
|
+
* @internal
|
|
50809
|
+
*/ function resolveDepositError(resolution, error) {
|
|
50810
|
+
if (resolution.lookupError !== undefined && isKitError(error) && isEarnServiceCode(error.cause?.trace, DEPOSIT_INTENT_INVALID_CODE)) {
|
|
50811
|
+
return resolution.lookupError;
|
|
50812
|
+
}
|
|
50813
|
+
if (resolution.intent !== undefined && isKitError(error) && isEarnServiceCode(error.cause?.trace, DEPOSIT_INTENT_INVALID_CODE)) {
|
|
50814
|
+
return new KitError({
|
|
50815
|
+
code: error.code,
|
|
50816
|
+
name: error.name,
|
|
50817
|
+
type: error.type,
|
|
50818
|
+
recoverability: error.recoverability,
|
|
50819
|
+
message: `${error.message} The deposit intent may have expired before the deposit was submitted (it is valid for ${String(DEPOSIT_INTENT_TTL_SECONDS)} seconds from when the SDK asks the wallet to sign). Call deposit() again to sign a new intent.`,
|
|
50820
|
+
...error.cause !== undefined && {
|
|
50821
|
+
cause: error.cause
|
|
50822
|
+
}
|
|
50823
|
+
});
|
|
50824
|
+
}
|
|
50825
|
+
return error;
|
|
50826
|
+
}
|
|
50827
|
+
function isEarnServiceCode(trace, code) {
|
|
50828
|
+
return typeof trace === 'object' && trace !== null && 'earnServiceCode' in trace && trace.earnServiceCode === code;
|
|
50829
|
+
}
|
|
50830
|
+
|
|
49903
50831
|
/**
|
|
49904
50832
|
* Build cross-chain deposit instructions via the bridge API.
|
|
49905
50833
|
*
|
|
@@ -50782,7 +51710,7 @@ function reportTransactionFailure(reportContext, action, error) {
|
|
|
50782
51710
|
}
|
|
50783
51711
|
function transactionReportErrorCode(error) {
|
|
50784
51712
|
if (isKitError(error)) {
|
|
50785
|
-
return error.name;
|
|
51713
|
+
return mapVedaRevert(error).name;
|
|
50786
51714
|
}
|
|
50787
51715
|
const message = getErrorMessage(error);
|
|
50788
51716
|
if (/user (rejected|denied)|rejected by user/i.test(message)) {
|
|
@@ -51138,13 +52066,31 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
51138
52066
|
const adapterContractAddress = requireAdapterContract(chain);
|
|
51139
52067
|
const { adapter } = params.from;
|
|
51140
52068
|
const vaultAddress = assertHexAddress('vaultAddress', params.vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
51141
|
-
const { execId, executionParams, signature } = await this.runPhase(ctx, 'deposit', 'fetchParams', async ()=>
|
|
52069
|
+
const { execId, executionParams, signature } = await this.runPhase(ctx, 'deposit', 'fetchParams', async ()=>{
|
|
52070
|
+
// Veda vaults mint shares straight to `address`, so the service
|
|
52071
|
+
// needs proof that `address` asked for this deposit. Sign it here,
|
|
52072
|
+
// inside fetchParams, so a rejected prompt fails this phase.
|
|
52073
|
+
const resolution = await resolveDepositIntent({
|
|
52074
|
+
adapter,
|
|
52075
|
+
chain,
|
|
52076
|
+
address,
|
|
51142
52077
|
vaultAddress,
|
|
51143
52078
|
amount: params.amount,
|
|
51144
|
-
address,
|
|
51145
|
-
chain: apiChain,
|
|
51146
52079
|
config
|
|
51147
|
-
})
|
|
52080
|
+
});
|
|
52081
|
+
try {
|
|
52082
|
+
return await fetchDeposit({
|
|
52083
|
+
vaultAddress,
|
|
52084
|
+
amount: params.amount,
|
|
52085
|
+
address,
|
|
52086
|
+
chain: apiChain,
|
|
52087
|
+
intent: resolution.intent,
|
|
52088
|
+
config
|
|
52089
|
+
});
|
|
52090
|
+
} catch (error) {
|
|
52091
|
+
throw resolveDepositError(resolution, error);
|
|
52092
|
+
}
|
|
52093
|
+
}, ()=>undefined);
|
|
51148
52094
|
validateExecutionDeadline$1(executionParams);
|
|
51149
52095
|
const transactionReportContext = {
|
|
51150
52096
|
execId,
|
|
@@ -54151,7 +55097,7 @@ registerKit(`${pkg$3.name}/${pkg$3.version}`);
|
|
|
54151
55097
|
};
|
|
54152
55098
|
|
|
54153
55099
|
var name$1 = "@circle-fin/borrow-kit";
|
|
54154
|
-
var version$1 = "1.
|
|
55100
|
+
var version$1 = "1.1.1";
|
|
54155
55101
|
var pkg$1 = {
|
|
54156
55102
|
name: name$1,
|
|
54157
55103
|
version: version$1};
|
|
@@ -64875,7 +65821,7 @@ function normaliseLoadTimeout(value) {
|
|
|
64875
65821
|
currency: zod.z.string().min(1).optional(),
|
|
64876
65822
|
email: zod.z.string().email().optional(),
|
|
64877
65823
|
assets: onrampAssetSelectionSchema.optional(),
|
|
64878
|
-
metadata: zod.z.record(zod.z.string(), zod.z.
|
|
65824
|
+
metadata: zod.z.record(zod.z.string(), zod.z.string()).optional()
|
|
64879
65825
|
});
|
|
64880
65826
|
|
|
64881
65827
|
/**
|
|
@@ -67060,7 +68006,7 @@ async function deposit$2(context, params) {
|
|
|
67060
68006
|
}
|
|
67061
68007
|
|
|
67062
68008
|
var name = "@circle-fin/unified-balance-kit";
|
|
67063
|
-
var version = "1.8.
|
|
68009
|
+
var version = "1.8.2";
|
|
67064
68010
|
var pkg = {
|
|
67065
68011
|
name: name,
|
|
67066
68012
|
version: version};
|