@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@circle-fin/app-kit",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "A one-stop Circle SDK solution for building stablecoin (e.g. USDC) applications, with bridging, swapping, and more on-chain operations",
5
5
  "keywords": [
6
6
  "circle",
@@ -36,11 +36,11 @@
36
36
  "module": "./index.mjs",
37
37
  "types": "./index.d.cts",
38
38
  "dependencies": {
39
- "@circle-fin/unified-balance-kit": "1.2.0",
40
- "@circle-fin/provider-gateway-v1": "1.1.0",
41
- "@circle-fin/earn-kit": "1.2.0",
42
- "@circle-fin/bridge-kit": "1.11.0",
43
- "@circle-fin/swap-kit": "1.3.0",
39
+ "@circle-fin/unified-balance-kit": "1.2.1",
40
+ "@circle-fin/provider-gateway-v1": "1.1.1",
41
+ "@circle-fin/earn-kit": "1.2.1",
42
+ "@circle-fin/bridge-kit": "1.11.1",
43
+ "@circle-fin/swap-kit": "1.3.1",
44
44
  "zod": "3.25.67",
45
45
  "@ethersproject/address": "^5.8.0",
46
46
  "@ethersproject/bytes": "^5.8.0",
package/swap.cjs CHANGED
@@ -8563,13 +8563,20 @@ const swapTokenEnumSchema = zod.z.enum([
8563
8563
  symbol: 'EURC',
8564
8564
  decimals: 6,
8565
8565
  locators: {
8566
+ // =========================================================================
8567
+ // Mainnets
8568
+ // =========================================================================
8566
8569
  [Blockchain.Avalanche]: '0xc891EB4cbdEFf6e073e859e987815Ed1505c2ACD',
8567
8570
  [Blockchain.Base]: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42',
8568
8571
  [Blockchain.Ethereum]: '0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c',
8569
8572
  [Blockchain.Solana]: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr',
8570
8573
  [Blockchain.World_Chain]: '0x1C60ba0A0eD1019e8Eb035E6daF4155A5cE2380B',
8574
+ // =========================================================================
8571
8575
  // Testnets
8572
- [Blockchain.Arc_Testnet]: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a'
8576
+ // =========================================================================
8577
+ [Blockchain.Arc_Testnet]: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
8578
+ [Blockchain.Base_Sepolia]: '0x808456652fdb597867f38412077A9182bf77359F',
8579
+ [Blockchain.Ethereum_Sepolia]: '0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4'
8573
8580
  }
8574
8581
  };
8575
8582
 
@@ -8779,8 +8786,7 @@ const swapTokenEnumSchema = zod.z.enum([
8779
8786
  * cirBTC (Circle Bitcoin) token definition with addresses and metadata.
8780
8787
  *
8781
8788
  * @remarks
8782
- * Built-in cirBTC definition for the TokenRegistry. Currently deployed
8783
- * on Arc Testnet.
8789
+ * Built-in cirBTC definition for the TokenRegistry.
8784
8790
  *
8785
8791
  * @example
8786
8792
  * ```typescript
@@ -8796,7 +8802,15 @@ const swapTokenEnumSchema = zod.z.enum([
8796
8802
  symbol: 'cirBTC',
8797
8803
  decimals: 8,
8798
8804
  locators: {
8799
- [Blockchain.Arc_Testnet]: '0xf0C4a4CE82A5746AbAAd9425360Ab04fbBA432BF'
8805
+ // =========================================================================
8806
+ // Mainnets
8807
+ // =========================================================================
8808
+ [Blockchain.Ethereum]: '0x72DFB2E44f59C5AD2bAFE84314E5b99a7cd5075E',
8809
+ // =========================================================================
8810
+ // Testnets
8811
+ // =========================================================================
8812
+ [Blockchain.Arc_Testnet]: '0xf0C4a4CE82A5746AbAAd9425360Ab04fbBA432BF',
8813
+ [Blockchain.Ethereum_Sepolia]: '0x3a3fe695F684Bf9b9e43CF43C2b895Ea5e392bB3'
8800
8814
  }
8801
8815
  };
8802
8816
 
@@ -9748,7 +9762,7 @@ const swapTokenEnumSchema = zod.z.enum([
9748
9762
  }
9749
9763
 
9750
9764
  var name$2 = "@circle-fin/bridge-kit";
9751
- var version$2 = "1.11.0";
9765
+ var version$2 = "1.11.1";
9752
9766
  var pkg$2 = {
9753
9767
  name: name$2,
9754
9768
  version: version$2};
@@ -10638,7 +10652,7 @@ var TransferSpeed;
10638
10652
  registerKit(`${pkg$2.name}/${pkg$2.version}`);
10639
10653
 
10640
10654
  var name$1 = "@circle-fin/swap-kit";
10641
- var version$1 = "1.3.0";
10655
+ var version$1 = "1.3.1";
10642
10656
  var pkg$1 = {
10643
10657
  name: name$1,
10644
10658
  version: version$1};
@@ -15044,6 +15058,19 @@ const TOKEN_REGISTRY = createTokenRegistry();
15044
15058
  *
15045
15059
  * @internal
15046
15060
  */ const SUPPORTED_CHAINS = getSwapSupportedChains(Chains);
15061
+ /**
15062
+ * Inter-poll delays (ms) for the same-chain `amountOut` enrichment loop.
15063
+ *
15064
+ * A same-chain swap is already terminal `DONE` once its source transaction
15065
+ * confirms; this short, escalating budget (~5s total across up to four polls)
15066
+ * gives the service a brief window to resolve the output amount before
15067
+ * `swap()` returns. The loop exits the instant `amountOut` is available.
15068
+ */ const SAME_CHAIN_AMOUNT_OUT_POLL_DELAYS_MS = [
15069
+ 1_000,
15070
+ 2_000,
15071
+ 2_000
15072
+ ];
15073
+ const sleep$2 = async (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
15047
15074
  function buildSwapProgress(statusResult) {
15048
15075
  return {
15049
15076
  status: statusResult.status,
@@ -15063,29 +15090,46 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
15063
15090
  }
15064
15091
  };
15065
15092
  }
15066
- try {
15067
- const statusResult = await getSwapStatus$1({
15068
- txHash,
15069
- chain: chain.chain,
15070
- apiKey
15071
- });
15072
- const progress = buildSwapProgress(statusResult);
15073
- if (statusResult.status === 'DONE' && statusResult.amountOut !== undefined) {
15093
+ // A same-chain swap completes atomically in the source transaction, which
15094
+ // has already been confirmed (and receipt-checked for revert) by the time we
15095
+ // get here. Its terminal status is therefore `DONE`. We briefly poll the
15096
+ // status endpoint to enrich `amountOut`, which the service usually resolves
15097
+ // within a few seconds. A non-DONE/erroneous peek never downgrades the
15098
+ // status — once the short budget is exhausted we return `DONE` without
15099
+ // `amountOut`, and the caller can fetch it later via `getSwapStatus`.
15100
+ for(let attempt = 0;; attempt += 1){
15101
+ try {
15102
+ const statusResult = await getSwapStatus$1({
15103
+ txHash,
15104
+ chain: chain.chain,
15105
+ apiKey
15106
+ });
15107
+ if (statusResult.status === 'DONE' && statusResult.amountOut !== undefined) {
15108
+ return {
15109
+ progress: buildSwapProgress(statusResult),
15110
+ amountOut: statusResult.amountOut
15111
+ };
15112
+ }
15113
+ } catch {
15114
+ // Reachability/parse failure during enrichment — non-fatal; the swap
15115
+ // already succeeded on-chain. `getSwapStatus` already retried transient
15116
+ // network errors internally, so further polling is unlikely to help.
15074
15117
  return {
15075
- progress,
15076
- amountOut: statusResult.amountOut
15118
+ progress: {
15119
+ status: 'DONE'
15120
+ }
15077
15121
  };
15078
15122
  }
15079
- return {
15080
- progress
15081
- };
15082
- } catch {
15083
- // Non-fatal — the swap already succeeded on-chain. status stays PENDING.
15084
- return {
15085
- progress: {
15086
- status: 'PENDING'
15087
- }
15088
- };
15123
+ const delayMs = SAME_CHAIN_AMOUNT_OUT_POLL_DELAYS_MS[attempt];
15124
+ if (delayMs === undefined) {
15125
+ // Budget exhausted — terminal `DONE`, `amountOut` not yet available.
15126
+ return {
15127
+ progress: {
15128
+ status: 'DONE'
15129
+ }
15130
+ };
15131
+ }
15132
+ await sleep$2(delayMs);
15089
15133
  }
15090
15134
  }
15091
15135
  /**
@@ -15963,14 +16007,14 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
15963
16007
  }
15964
16008
  // Build swap fees with adapter-backed formatting for unregistered tokens
15965
16009
  const swapResultFees = serviceResponse.fees ? await this.buildFormattedFees(serviceResponse.fees, chain, destinationChain, adapter, config?.customFee?.recipientAddress) : undefined;
15966
- // Best-effort enrichment: fetch amountOut and provider-reported status.
15967
- // Only performed for same-chain swaps they typically reach DONE within
15968
- // a few seconds of source-tx confirmation, so the peek is useful UX and
15969
- // adds minimal latency. Cross-chain swaps are skipped because the
15970
- // destination mint takes minutes (CCTPv2 attestation), meaning the peek
15971
- // would always return PENDING/WAIT_DESTINATION_TRANSACTION while still
15972
- // burning up to 30s of the load balancer's server-side wait. Callers
15973
- // should use `kit.getSwapStatus()` to poll cross-chain swaps to DONE.
16010
+ // Best-effort enrichment: briefly poll for amountOut on same-chain swaps.
16011
+ // They complete atomically in the (already-confirmed) source tx, so the
16012
+ // result is terminal `DONE` regardless; the short poll only adds amountOut
16013
+ // once the service has resolved it. Cross-chain swaps are skipped because
16014
+ // the destination mint takes minutes (CCTPv2 attestation), so polling would
16015
+ // always return PENDING/WAIT_DESTINATION_TRANSACTION while burning the
16016
+ // budget. Callers should use `kit.getSwapStatus()` to poll cross-chain
16017
+ // swaps to DONE.
15974
16018
  const statusSnapshot = await fetchSameChainStatusSnapshot({
15975
16019
  isCrossChainSwap,
15976
16020
  txHash,
@@ -20272,7 +20316,7 @@ registerKit(`${pkg$1.name}/${pkg$1.version}`);
20272
20316
  };
20273
20317
 
20274
20318
  var name = "@circle-fin/earn-kit";
20275
- var version = "1.2.0";
20319
+ var version = "1.2.1";
20276
20320
  var pkg = {
20277
20321
  name: name,
20278
20322
  version: version};
@@ -20311,20 +20355,7 @@ var pkg = {
20311
20355
  */ ({
20312
20356
  [Blockchain.Arc_Testnet]: CHAIN_TO_API[Blockchain.Arc_Testnet]
20313
20357
  });
20314
- /**
20315
- * Expected EIP-712 domain of the ERC-3009 authorization token per source
20316
- * chain.
20317
- *
20318
- * Cross-chain Earn deposits are USDC-only, but USDC deployments differ on the
20319
- * EIP-712 domain name: newer deployments use `USDC` while older ones (for
20320
- * example Arbitrum Sepolia) kept `USD Coin` from the FiatToken V2 upgrade.
20321
- * These values mirror the bridge service's per-chain domain table and the
20322
- * on-chain `DOMAIN_SEPARATOR` inputs. Asserted before signing so a tampered
20323
- * prepare response cannot point the signature at another token. The
20324
- * `satisfies` check forces a domain entry whenever a source chain is added.
20325
- *
20326
- * @internal
20327
- */ ({
20358
+ ({
20328
20359
  [Blockchain.Arbitrum_Sepolia]: {
20329
20360
  },
20330
20361
  [Blockchain.Base_Sepolia]: {
@@ -20360,6 +20391,7 @@ const hexSignatureSchema = evmSignatureSchema;
20360
20391
  const hexAddressSchema = evmAddressSchema;
20361
20392
  // '0x' prefix + 32 bytes * 2 hex chars.
20362
20393
  const BYTES32_HEX_LENGTH = 66;
20394
+ const bridgeFeeTokenSchema = hexAddressSchema;
20363
20395
  /**
20364
20396
  * Zod schema for a non-negative uint256-like value.
20365
20397
  *
@@ -20646,14 +20678,12 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
20646
20678
  *
20647
20679
  * The bridge prepare path returns one item per collected source fee (e.g.
20648
20680
  * `PRE_FINALITY` for the fast-burn fee and `FORWARD` for the destination
20649
- * forward fee), all denominated in the shared `feeToken`. The signed
20650
- * authorization `value` is `principal + sum(items[].amount)`, so the pre-sign
20651
- * guard parses these to recompute the expected value. Each item's `amount` is
20652
- * a base-unit decimal string in `feeToken` units.
20681
+ * forward fee), all denominated in the shared `feeToken`. Each item's `amount`
20682
+ * is a base-unit decimal string in `feeToken` units.
20653
20683
  *
20654
20684
  * @internal
20655
20685
  */ const bridgeFeeQuoteSchema = zod.z.object({
20656
- feeToken: hexAddressSchema,
20686
+ feeToken: bridgeFeeTokenSchema,
20657
20687
  items: zod.z.array(zod.z.object({
20658
20688
  type: zod.z.string(),
20659
20689
  amount: uint256LikeSchema
@@ -20669,6 +20699,12 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
20669
20699
  erc3009TypedData: erc3009TypedDataSchema,
20670
20700
  feeQuote: bridgeFeeQuoteSchema
20671
20701
  }).passthrough();
20702
+ const bridgeDepositPrepareReviewSchema = zod.z.object({
20703
+ sourceChain: zod.z.string(),
20704
+ destinationChain: zod.z.string(),
20705
+ amount: amountJsonSchema,
20706
+ vaultAddress: hexAddressSchema
20707
+ }).passthrough();
20672
20708
  /**
20673
20709
  * Zod schema for the bridge deposit prepare payload.
20674
20710
  *
@@ -20680,7 +20716,7 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
20680
20716
  execId: bridgeDepositExecIdSchema,
20681
20717
  erc3009TypedData: bridgeDepositPreparedBundleSchema,
20682
20718
  expiresAt: zod.z.string().datetime(),
20683
- review: zod.z.unknown().optional()
20719
+ review: bridgeDepositPrepareReviewSchema
20684
20720
  });
20685
20721
  /**
20686
20722
  * Zod schema for the `POST /v1/earnKit/bridge/deposit/prepare` API response.
@@ -20959,6 +20995,10 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
20959
20995
  data: exploreVaultsPayloadSchema
20960
20996
  });
20961
20997
 
20998
+ // Intentionally built-ins-only: Earn bridge support is limited to SDK-known
20999
+ // token contracts plus the explicit ERC-3009 domain allowlist below.
21000
+ createTokenRegistry();
21001
+
20962
21002
  /**
20963
21003
  * Shared UTF-8 encoder for building the keccak input in
20964
21004
  * {@link isValidEip55Checksum}, hoisted to module scope to avoid allocating a
@@ -21081,10 +21121,9 @@ const sourceAdapterContextSchema = zod.z.object({
21081
21121
  * Schema for validating human-readable decimal amount strings.
21082
21122
  *
21083
21123
  * Accept positive decimal strings like '100', '100.50', '0.001'. Reject
21084
- * zero, negative, non-numeric, and non-canonical strings. EarnKit
21085
- * same-chain amounts are USDC/EURC (6 decimals), so precision is capped at
21086
- * 6 to match the Earn Service and fail fast locally instead of leaking
21087
- * 7+ decimal-place inputs to the server.
21124
+ * zero, negative, non-numeric, and non-canonical strings. Same-chain
21125
+ * EarnKit vaults can use different asset precisions, so the service enforces
21126
+ * the vault-specific decimal cap after resolving the vault asset.
21088
21127
  *
21089
21128
  * @internal
21090
21129
  */ const amountSchema = zod.z.string({
@@ -21093,32 +21132,15 @@ const sourceAdapterContextSchema = zod.z.object({
21093
21132
  allowZero: false,
21094
21133
  regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
21095
21134
  attributeName: 'amount',
21096
- maxDecimals: 6
21097
- })(zod.z.string())).refine((value)=>CANONICAL_AMOUNT_REGEX.test(value), AMOUNT_FORMAT_ERROR_MESSAGE);
21098
- /**
21099
- * Schema for cross-chain deposit amount strings.
21100
- *
21101
- * Cross-chain Earn deposits are USDC-only, and the signed ERC-3009 value is
21102
- * denominated in USDC's 6 decimals. Reject more precise inputs up front so
21103
- * the signed value always equals the requested amount instead of a silent
21104
- * truncation. Non-canonical forms (leading dot, leading zeros) are rejected
21105
- * too, matching the Earn Service.
21106
- *
21107
- * @internal
21108
- */ const crossChainAmountSchema = zod.z.string({
21109
- required_error: 'amount is required'
21110
- }).min(1, 'amount is required').pipe(createDecimalStringValidator({
21111
- allowZero: false,
21112
- regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
21113
- attributeName: 'amount',
21114
- maxDecimals: 6
21135
+ maxDecimals: 18
21115
21136
  })(zod.z.string())).refine((value)=>CANONICAL_AMOUNT_REGEX.test(value), AMOUNT_FORMAT_ERROR_MESSAGE);
21116
21137
  /**
21117
21138
  * Schema for cross-chain source fee caps.
21118
21139
  *
21119
- * Cross-chain source fees are denominated in USDC base units. Zero is valid
21120
- * when the quote contains no source-collected fees; otherwise the cap should be
21121
- * the caller-accepted total from a recent quote.
21140
+ * Cross-chain source fees are denominated in the quote's fee token. Zero is
21141
+ * valid when the quote contains no source-collected fees; otherwise the cap
21142
+ * should be the caller-accepted total from a recent quote. The provider
21143
+ * enforces the prepared bundle's fee-token precision before signing.
21122
21144
  *
21123
21145
  * @internal
21124
21146
  */ const crossChainMaxFeeSchema = zod.z.string({
@@ -21127,7 +21149,7 @@ const sourceAdapterContextSchema = zod.z.object({
21127
21149
  allowZero: true,
21128
21150
  regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
21129
21151
  attributeName: 'maxFee',
21130
- maxDecimals: 6
21152
+ maxDecimals: 18
21131
21153
  })(zod.z.string())).refine((value)=>CANONICAL_AMOUNT_REGEX.test(value), AMOUNT_FORMAT_ERROR_MESSAGE);
21132
21154
  /**
21133
21155
  * Build a schema for an optional non-negative decimal filter string.
@@ -21288,7 +21310,7 @@ const crossChainDepositDestinationSchema = zod.z.object({
21288
21310
  from: sourceAdapterContextSchema,
21289
21311
  to: crossChainDepositDestinationSchema,
21290
21312
  vaultAddress: vaultAddressSchema,
21291
- amount: crossChainAmountSchema,
21313
+ amount: amountSchema,
21292
21314
  maxFee: crossChainMaxFeeSchema,
21293
21315
  transferSpeed: zod.z.enum([
21294
21316
  'FAST',
package/swap.d.cts CHANGED
@@ -6283,8 +6283,8 @@ interface CrossChainDepositParams<TFromAdapterCapabilities extends AdapterCapabi
6283
6283
  */
6284
6284
  readonly amount: string;
6285
6285
  /**
6286
- * Maximum source-collected bridge fee the caller is willing to sign for, in
6287
- * human-readable source token units.
6286
+ * Maximum source-collected bridge fee the caller is willing to accept, in
6287
+ * human-readable fee token units from the quote.
6288
6288
  *
6289
6289
  * Derive this from a recent cross-chain deposit quote by summing
6290
6290
  * `quote.fees[].amount`. The prepared bridge bundle must not exceed this cap
package/swap.d.mts CHANGED
@@ -6283,8 +6283,8 @@ interface CrossChainDepositParams<TFromAdapterCapabilities extends AdapterCapabi
6283
6283
  */
6284
6284
  readonly amount: string;
6285
6285
  /**
6286
- * Maximum source-collected bridge fee the caller is willing to sign for, in
6287
- * human-readable source token units.
6286
+ * Maximum source-collected bridge fee the caller is willing to accept, in
6287
+ * human-readable fee token units from the quote.
6288
6288
  *
6289
6289
  * Derive this from a recent cross-chain deposit quote by summing
6290
6290
  * `quote.fees[].amount`. The prepared bridge bundle must not exceed this cap
package/swap.d.ts CHANGED
@@ -6283,8 +6283,8 @@ interface CrossChainDepositParams<TFromAdapterCapabilities extends AdapterCapabi
6283
6283
  */
6284
6284
  readonly amount: string;
6285
6285
  /**
6286
- * Maximum source-collected bridge fee the caller is willing to sign for, in
6287
- * human-readable source token units.
6286
+ * Maximum source-collected bridge fee the caller is willing to accept, in
6287
+ * human-readable fee token units from the quote.
6288
6288
  *
6289
6289
  * Derive this from a recent cross-chain deposit quote by summing
6290
6290
  * `quote.fees[].amount`. The prepared bridge bundle must not exceed this cap