@circle-fin/app-kit 1.9.0 → 1.10.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/earn.d.ts CHANGED
@@ -374,6 +374,14 @@ interface CCTPSplitConfig {
374
374
  type: 'split';
375
375
  tokenMessenger: string;
376
376
  messageTransmitter: string;
377
+ /**
378
+ * Address of the `TokenMessengerWithFees` wrapper, when deployed on this chain.
379
+ *
380
+ * Optional. Present only on chains that support the prepaid FORWARD path
381
+ * (source-chain fee collection via `depositForBurnWithHookAndFees`). Resolve
382
+ * it with `resolveCCTPV2ContractAddress(chain, 'tokenMessengerWithFees')`.
383
+ */
384
+ tokenMessengerWithFees?: string;
377
385
  confirmations: number;
378
386
  }
379
387
  /**
@@ -394,6 +402,14 @@ interface CCTPSplitConfig {
394
402
  interface CCTPMergedConfig {
395
403
  type: 'merged';
396
404
  contract: string;
405
+ /**
406
+ * Address of the `TokenMessengerWithFees` wrapper, when deployed on this chain.
407
+ *
408
+ * Optional. Present only on chains that support the prepaid FORWARD path
409
+ * (source-chain fee collection via `depositForBurnWithHookAndFees`). Resolve
410
+ * it with `resolveCCTPV2ContractAddress(chain, 'tokenMessengerWithFees')`.
411
+ */
412
+ tokenMessengerWithFees?: string;
397
413
  confirmations: number;
398
414
  }
399
415
  /**
@@ -528,6 +544,21 @@ interface GatewayV1Contracts {
528
544
  * @example "0xabcdef1234567890abcdef1234567890abcdef12"
529
545
  */
530
546
  minter: string;
547
+ /**
548
+ * The address of the `DepositForHandler` contract.
549
+ *
550
+ * @description Optional. The handler the GenericExecutor calls on this chain
551
+ * to run a fast cross-chain deposit into the {@link GatewayV1Contracts.wallet}.
552
+ * Present only on chains that are fast-deposit destinations; other Gateway
553
+ * chains omit it.
554
+ *
555
+ * Address format varies by blockchain:
556
+ * - EVM chains: 40-character hexadecimal with 0x prefix (e.g., "0x1234...")
557
+ * - Solana: Base58-encoded 32-byte address (e.g., "9WzDX...")
558
+ *
559
+ * @example "0xD05E7D2E7d30b92c5F17d7d0fC575fce231F1A48"
560
+ */
561
+ depositForHandler?: string;
531
562
  }
532
563
  /**
533
564
  * Versioned map of Gateway contract configurations.
@@ -2177,6 +2208,110 @@ interface CCTPv2ActionMap {
2177
2208
  */
2178
2209
  hookData: string;
2179
2210
  };
2211
+ /**
2212
+ * Initiate a prepaid cross-chain USDC transfer through the `TokenMessengerWithFees` wrapper.
2213
+ *
2214
+ * Burn USDC on the source chain while collecting all fees up front against a
2215
+ * signed quote. The wrapper collects the fee via `FeeManager`, then delegates
2216
+ * to the unmodified `TokenMessengerV2`. When `hookData` is provided (the
2217
+ * GenericExecutor FORWARD path) the wrapper's `depositForBurnWithHookAndFees`
2218
+ * contract method is used; otherwise `depositForBurnWithFees` is used.
2219
+ *
2220
+ * @remarks
2221
+ * SDK/contract naming: this SDK action is `depositForBurnWithFees` but, when a
2222
+ * `hookData` is present, it dispatches to the `depositForBurnWithHookAndFees`
2223
+ * contract method on `TokenMessengerWithFees` (NOT on `TokenMessengerV2`).
2224
+ *
2225
+ * Fee payment channel (must match the quote's `feeToken`):
2226
+ * - Native fee (`feeToken` is the zero address): exactly `feeTotalAmount` is
2227
+ * attached as `msg.value`.
2228
+ * - ERC-20 fee (e.g. USDC): no value is attached; the caller must first approve
2229
+ * the wrapper for `feeTotalAmount` (see the provider's fee approval helper).
2230
+ *
2231
+ * @remarks
2232
+ * Unlike `depositForBurn`, the `TokenMessengerWithFees` contract methods do NOT
2233
+ * take `maxFee` or `minFinalityThreshold` — fee and finality behavior are
2234
+ * derived from the signed quote — so those fields are omitted from this action.
2235
+ *
2236
+ * @example
2237
+ * ```typescript
2238
+ * await adapter.action('cctp.v2.depositForBurnWithFees', {
2239
+ * amount: BigInt('1000000'),
2240
+ * mintRecipient: executorAddress, // GenericExecutor (bytes32)
2241
+ * destinationCaller: executorAddress, // GenericExecutor (bytes32)
2242
+ * fromChain: ethereum,
2243
+ * toChain: arc,
2244
+ * hookData: geForwardHookData, // cctp-forward-wrapped GenericExecutor blob
2245
+ * claim: { signedQuote: '0x...', refundAddress: '0x...' },
2246
+ * feeToken: '0x0000000000000000000000000000000000000000', // native
2247
+ * feeTotalAmount: 3500000n,
2248
+ * })
2249
+ * ```
2250
+ */
2251
+ depositForBurnWithFees: Omit<CCTPv2ActionMap['depositForBurn'], 'maxFee' | 'minFinalityThreshold'> & {
2252
+ /**
2253
+ * Optional hex-encoded hook data for the GenericExecutor FORWARD path.
2254
+ *
2255
+ * When present, the `depositForBurnWithHookAndFees` contract method is used
2256
+ * and the blob must be wrapped in the `cctp-forward` envelope (the wrapper
2257
+ * rejects a FORWARD fee quote whose hook lacks it). When omitted, the plain
2258
+ * `depositForBurnWithFees` contract method is used.
2259
+ */
2260
+ hookData?: string;
2261
+ /**
2262
+ * Signed fee quote claim passed to the `TokenMessengerWithFees` wrapper.
2263
+ *
2264
+ * `signedQuote` is the `[uint8 0x01][abi.encode(Quote)]` blob returned by the
2265
+ * Fee Quote service; `refundAddress` receives any fee overpayment refund.
2266
+ */
2267
+ claim: QuoteClaim;
2268
+ /**
2269
+ * Fee token from the signed quote.
2270
+ *
2271
+ * The zero address (`0x000…0`) means the fee is paid in native currency and
2272
+ * is attached as `msg.value`. Any other address (e.g. USDC) means an ERC-20
2273
+ * fee that must be approved to the wrapper beforehand. This is independent of
2274
+ * `burnToken`, which is always USDC.
2275
+ */
2276
+ feeToken: string;
2277
+ /**
2278
+ * Total fee amount from the signed quote, in `feeToken` minor units.
2279
+ *
2280
+ * Firm only until the quote's `expiresAt`. For a native fee this is the exact
2281
+ * `msg.value`; for an ERC-20 fee this is the amount approved to the wrapper.
2282
+ */
2283
+ feeTotalAmount: bigint;
2284
+ };
2285
+ }
2286
+ /**
2287
+ * Signed fee quote claim consumed by the `TokenMessengerWithFees` wrapper.
2288
+ *
2289
+ * Mirrors the on-chain `IFeeManager.QuoteClaim` struct.
2290
+ *
2291
+ * @example
2292
+ * ```typescript
2293
+ * const claim: QuoteClaim = {
2294
+ * signedQuote: '0x01...', // [uint8 0x01][abi.encode(Quote)]
2295
+ * refundAddress: '0xUserWallet...',
2296
+ * }
2297
+ * ```
2298
+ */
2299
+ interface QuoteClaim {
2300
+ /**
2301
+ * Opaque signed quote bytes (`0x` hex) from the fee-quote service
2302
+ * (`SignedFeeQuote.signedQuote` returned by `fetchFeeQuote`). Pass verbatim;
2303
+ * do not decode.
2304
+ *
2305
+ * The quote binds the FORWARD fee item to the on-chain call via `argsHash`;
2306
+ * passing a quote that does not match the burn args reverts `QuoteArgsMismatch`.
2307
+ */
2308
+ signedQuote: string;
2309
+ /**
2310
+ * Address that receives any refund of overpaid fees.
2311
+ *
2312
+ * Typically the user wallet that authorized the burn.
2313
+ */
2314
+ refundAddress: string;
2180
2315
  }
2181
2316
 
2182
2317
  /**
@@ -6082,6 +6217,30 @@ declare class Amount implements AmountFields {
6082
6217
  toJSON(): AmountJSON;
6083
6218
  }
6084
6219
 
6220
+ /** @internal */
6221
+ declare const bridgeQuoteExpirySchema: z.ZodCatch<z.ZodOptional<z.ZodDiscriminatedUnion<"mode", [z.ZodObject<{
6222
+ mode: z.ZodLiteral<"TIMESTAMP">;
6223
+ expiresAt: z.ZodString;
6224
+ }, "strip", z.ZodTypeAny, {
6225
+ mode: "TIMESTAMP";
6226
+ expiresAt: string;
6227
+ }, {
6228
+ mode: "TIMESTAMP";
6229
+ expiresAt: string;
6230
+ }>, z.ZodObject<{
6231
+ mode: z.ZodLiteral<"BLOCK_NUMBER">;
6232
+ expiresAtBlock: z.ZodNumber;
6233
+ blockEstimatedAt: z.ZodOptional<z.ZodString>;
6234
+ }, "strip", z.ZodTypeAny, {
6235
+ mode: "BLOCK_NUMBER";
6236
+ expiresAtBlock: number;
6237
+ blockEstimatedAt?: string | undefined;
6238
+ }, {
6239
+ mode: "BLOCK_NUMBER";
6240
+ expiresAtBlock: number;
6241
+ blockEstimatedAt?: string | undefined;
6242
+ }>]>>>;
6243
+
6085
6244
  /**
6086
6245
  * A single vault query specifying chain and vault address.
6087
6246
  *
@@ -6129,6 +6288,7 @@ interface VaultRewardInfo {
6129
6288
  * assetAddress: '0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf',
6130
6289
  * lltv: 0.86,
6131
6290
  * supplyUsd: 50000000.25,
6291
+ * allocationPct: 0.6,
6132
6292
  * }
6133
6293
  * ```
6134
6294
  */
@@ -6141,6 +6301,17 @@ interface CollateralInfo {
6141
6301
  readonly lltv: number;
6142
6302
  /** Approximate supplied value in USD, e.g. 50000000.25 for $50,000,000.25. */
6143
6303
  readonly supplyUsd: number;
6304
+ /**
6305
+ * Share of the vault's supply allocated to this collateral market
6306
+ * (e.g., 0.6 = 60%).
6307
+ *
6308
+ * Populated for Morpho V1 vaults; `null` when the underlying product
6309
+ * exposes no per-market allocation (e.g. Morpho V2). Optional for now — a
6310
+ * backend that predates this field omits it entirely, matching the other
6311
+ * optional facets on {@link EarnOpportunityBase}; a future release makes it
6312
+ * required once every backend emits it.
6313
+ */
6314
+ readonly allocationPct?: number | null | undefined;
6144
6315
  }
6145
6316
  /**
6146
6317
  * Vault warning from the underlying earn protocol.
@@ -6160,65 +6331,253 @@ interface VaultWarning {
6160
6331
  readonly level: 'YELLOW' | 'RED';
6161
6332
  }
6162
6333
  /**
6163
- * Describe a yield-bearing vault available through the earn service.
6334
+ * Manager (e.g., curator) responsible for a yield opportunity.
6335
+ *
6336
+ * Generalizes Morpho's "curator". `null` on the opportunity when the
6337
+ * underlying product has no per-opportunity manager (e.g., a pooled
6338
+ * lending market).
6164
6339
  *
6165
6340
  * @example
6166
6341
  * ```typescript
6167
- * const vault: VaultInfo = {
6168
- * vaultAddress: '0xAabbeF1D3971c710276ed41eC791BbE14CdB8E88',
6169
- * chain: 'Arc_Testnet',
6170
- * name: 'Steakhouse USDC',
6171
- * protocol: 'MORPHO',
6172
- * asset: 'USDC',
6173
- * assetAddress: '0x3600000000000000000000000000000000000000',
6174
- * currentApy: 0.0425,
6175
- * nativeApy: 0.035,
6176
- * vaultFee: 0.05,
6177
- * rewards: [{ token: 'MORPHO', tokenAddress: '0x...', apy: 0.0075 }],
6178
- * collateral: [{ asset: 'cbBTC', assetAddress: '0x...', lltv: 0.86, supplyUsd: 50000000.25 }],
6179
- * totalDeposits: Amount.fromJSON({ raw: '15000000000000', decimals: 6 }),
6180
- * liquidity: Amount.fromJSON({ raw: '5000000000000', decimals: 6 }),
6342
+ * const manager: ManagerInfo = {
6343
+ * name: 'Steakhouse',
6344
+ * address: '0x...',
6345
+ * type: 'curator',
6346
+ * }
6347
+ * ```
6348
+ */
6349
+ interface ManagerInfo {
6350
+ /** Human-readable manager name. */
6351
+ readonly name: string;
6352
+ /** On-chain manager address, when the product exposes one. */
6353
+ readonly address?: string | undefined;
6354
+ /**
6355
+ * Manager role within the product. Only `'curator'` is emitted today
6356
+ * (Morpho V1/V2); additional roles are added as the providers that emit
6357
+ * them land.
6358
+ */
6359
+ readonly type: 'curator';
6360
+ }
6361
+ /**
6362
+ * Yield profile for an opportunity, including trailing averages.
6363
+ *
6364
+ * `current` is always present; trailing and native values are `null` when
6365
+ * unavailable for this instance (e.g., a vault younger than the lookback
6366
+ * window). `source`/`asOf` carry provenance for derived/staleness-prone
6367
+ * values.
6368
+ *
6369
+ * @example
6370
+ * ```typescript
6371
+ * const apyProfile: ApyProfile = {
6372
+ * current: 0.085,
6373
+ * native: 0.071,
6374
+ * d7: 0.082,
6375
+ * d30: 0.079,
6376
+ * d90: 0.081,
6377
+ * rewardShare: 0.16,
6378
+ * source: 'morpho:avgNetApy',
6379
+ * asOf: '2026-06-23T18:00:00Z',
6380
+ * }
6381
+ * ```
6382
+ */
6383
+ interface ApyProfile {
6384
+ /** Current total APY including rewards. */
6385
+ readonly current: number;
6386
+ /** Native APY excluding reward incentives; `null` when unavailable. */
6387
+ readonly native: number | null;
6388
+ /** Trailing 7-day average net APY; `null` when unavailable. */
6389
+ readonly d7: number | null;
6390
+ /** Trailing 30-day average net APY; `null` when unavailable. */
6391
+ readonly d30: number | null;
6392
+ /** Trailing 90-day average net APY; `null` when unavailable. */
6393
+ readonly d90: number | null;
6394
+ /** Share of current APY attributable to rewards; `null` when unavailable. */
6395
+ readonly rewardShare: number | null;
6396
+ /** Provenance of the trailing values (`native` or `circle:<source>`). */
6397
+ readonly source?: string | undefined;
6398
+ /** RFC3339 timestamp of the newest input used for the trailing values. */
6399
+ readonly asOf?: string | undefined;
6400
+ }
6401
+ /**
6402
+ * Fee split for an opportunity.
6403
+ *
6404
+ * Each component is `null` when the product does not levy it (e.g., Morpho
6405
+ * V1 vaults have no management fee).
6406
+ *
6407
+ * @example
6408
+ * ```typescript
6409
+ * const fee: FeeInfo = { performance: 0.1, management: null }
6410
+ * ```
6411
+ */
6412
+ interface FeeInfo {
6413
+ /** Performance fee as a decimal (e.g., 0.1 = 10%); `null` when unavailable. */
6414
+ readonly performance: number | null;
6415
+ /** Management fee as a decimal; `null` when unavailable. */
6416
+ readonly management: number | null;
6417
+ }
6418
+ /**
6419
+ * Liquidity profile for an opportunity.
6420
+ *
6421
+ * @example
6422
+ * ```typescript
6423
+ * const liquidityProfile: LiquidityProfile = {
6424
+ * totalDeposits: Amount.fromJSON({ raw: '45000000000000', decimals: 6 }),
6425
+ * available: Amount.fromJSON({ raw: '5200000000000', decimals: 6 }),
6426
+ * totalSupply: Amount.fromJSON({ raw: '44900000000000000000', decimals: 18 }),
6181
6427
  * status: 'active',
6182
- * circleGuarded: false,
6183
6428
  * }
6184
6429
  * ```
6185
6430
  */
6186
- interface VaultInfo {
6187
- /** On-chain vault contract address. */
6188
- readonly vaultAddress: string;
6189
- /** Blockchain where the vault is deployed. */
6431
+ interface LiquidityProfile {
6432
+ /** Total value deposited in base-unit amount form. */
6433
+ readonly totalDeposits: Amount;
6434
+ /** Available liquidity in base-unit amount form. */
6435
+ readonly available: Amount;
6436
+ /** Outstanding vault share tokens (ERC4626 totalSupply) in base-unit form. */
6437
+ readonly totalSupply: Amount;
6438
+ /** Current liquidity status. */
6439
+ readonly status: 'active' | 'low_liquidity';
6440
+ }
6441
+ /**
6442
+ * Risk signals for an opportunity.
6443
+ *
6444
+ * @example
6445
+ * ```typescript
6446
+ * const riskSignals: RiskSignals = {
6447
+ * circleSentinel: true,
6448
+ * warnings: [],
6449
+ * earnKitWarnings: [],
6450
+ * }
6451
+ * ```
6452
+ */
6453
+ interface RiskSignals {
6454
+ /** Whether the opportunity is covered by Circle Sentinel. */
6455
+ readonly circleSentinel: boolean;
6456
+ /** Protocol warnings for this opportunity. */
6457
+ readonly warnings?: readonly VaultWarning[] | undefined;
6458
+ /** Circle-specific warnings (e.g., unsupported reward protocol). */
6459
+ readonly earnKitWarnings?: readonly string[] | undefined;
6460
+ }
6461
+ /**
6462
+ * Facets common to every earn opportunity, plus the deprecated flat
6463
+ * fields retained for backward compatibility.
6464
+ *
6465
+ * The flat aliases are emitted by the backend alongside the nested facets
6466
+ * and mapped straight through, so existing consumers keep reading them until
6467
+ * they are removed in a future major release. Narrow on
6468
+ * {@link EarnOpportunity.productType} to access product-specific fields.
6469
+ */
6470
+ interface EarnOpportunityBase {
6471
+ /** Blockchain where the opportunity is deployed. */
6190
6472
  readonly chain: `${EarnChain}`;
6191
- /** Human-readable vault name. */
6473
+ /** Human-readable opportunity name. */
6192
6474
  readonly name: string;
6193
- /** Vault protocol identifier. */
6475
+ /** Protocol identifier. */
6194
6476
  readonly protocol: string;
6195
6477
  /** Underlying deposit asset symbol (e.g., 'USDC'). */
6196
6478
  readonly asset: string;
6197
6479
  /** Underlying deposit asset contract address. */
6198
6480
  readonly assetAddress: string;
6199
- /** Total annualized percentage yield including rewards. */
6481
+ /** Reward tokens distributed by this opportunity. */
6482
+ readonly rewards: readonly VaultRewardInfo[];
6483
+ /** Primary on-chain address (protocol-neutral; replaces vaultAddress). */
6484
+ readonly address?: string | undefined;
6485
+ /** RFC3339 freshness timestamp: provider state ts, else cache sync time. */
6486
+ readonly asOf?: string | undefined;
6487
+ /** Manager/curator identity; `null` when the product has no manager. */
6488
+ readonly manager?: ManagerInfo | null | undefined;
6489
+ /** Yield profile including trailing averages. */
6490
+ readonly apyProfile?: ApyProfile | undefined;
6491
+ /** Fee split. */
6492
+ readonly fee?: FeeInfo | undefined;
6493
+ /** Liquidity profile. */
6494
+ readonly liquidityProfile?: LiquidityProfile | undefined;
6495
+ /** Risk signals. */
6496
+ readonly riskSignals?: RiskSignals | undefined;
6497
+ /** @deprecated use {@link EarnOpportunityBase.address} */
6498
+ readonly vaultAddress: string;
6499
+ /** @deprecated use {@link ApyProfile.current} via apyProfile */
6200
6500
  readonly currentApy: number;
6201
- /** Native APY excluding reward incentives. */
6501
+ /** @deprecated use {@link ApyProfile.native} via apyProfile */
6202
6502
  readonly nativeApy: number;
6203
- /** Vault fee as a decimal (e.g., 0.05 = 5%). */
6503
+ /** @deprecated use {@link FeeInfo.performance} via fee */
6204
6504
  readonly vaultFee: number;
6205
- /** Reward tokens distributed by this vault. */
6206
- readonly rewards: readonly VaultRewardInfo[];
6207
- /** Collateral markets backing this vault. */
6208
- readonly collateral: readonly CollateralInfo[];
6209
- /** Total value deposited in base-unit amount form. */
6505
+ /** @deprecated use {@link LiquidityProfile.totalDeposits} via liquidityProfile */
6210
6506
  readonly totalDeposits: Amount;
6211
- /** Available liquidity in the vault. */
6507
+ /** @deprecated use {@link LiquidityProfile.available} via liquidityProfile */
6212
6508
  readonly liquidity: Amount;
6213
- /** Current vault status. */
6509
+ /** @deprecated use {@link LiquidityProfile.status} via liquidityProfile */
6214
6510
  readonly status: 'active' | 'low_liquidity';
6215
- /** Whether the vault is on Circle's curated Circle-guarded list. */
6511
+ /** @deprecated use {@link RiskSignals.circleSentinel} via riskSignals */
6216
6512
  readonly circleGuarded: boolean;
6217
- /** Morpho protocol warnings for this vault. */
6513
+ /** @deprecated use {@link RiskSignals.warnings} via riskSignals */
6218
6514
  readonly warnings?: readonly VaultWarning[] | undefined;
6219
- /** Circle-specific warnings (e.g., unsupported reward protocol). */
6515
+ /** @deprecated use {@link RiskSignals.earnKitWarnings} via riskSignals */
6220
6516
  readonly earnKitWarnings?: readonly string[] | undefined;
6221
6517
  }
6518
+ /**
6519
+ * A yield-bearing vault opportunity (`productType: 'vault'`).
6520
+ *
6521
+ * Carries the universal {@link EarnOpportunityBase} facets plus the
6522
+ * vault-specific `collateral` markets.
6523
+ *
6524
+ * @example
6525
+ * ```typescript
6526
+ * const vault: VaultOpportunity = {
6527
+ * productType: 'vault',
6528
+ * address: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
6529
+ * chain: 'Arc_Testnet',
6530
+ * name: 'Steakhouse USDC',
6531
+ * protocol: 'MORPHO',
6532
+ * asset: 'USDC',
6533
+ * assetAddress: '0x3600000000000000000000000000000000000000',
6534
+ * asOf: '2026-06-23T18:00:00Z',
6535
+ * manager: { name: 'Steakhouse', address: '0x...', type: 'curator' },
6536
+ * apyProfile: { current: 0.085, native: 0.071, d7: 0.082, d30: 0.079, d90: 0.081, rewardShare: 0.16 },
6537
+ * fee: { performance: 0.1, management: null },
6538
+ * liquidityProfile: {
6539
+ * totalDeposits: Amount.fromJSON({ raw: '15000000000000', decimals: 6 }),
6540
+ * available: Amount.fromJSON({ raw: '5000000000000', decimals: 6 }),
6541
+ * totalSupply: Amount.fromJSON({ raw: '14950000000000000000', decimals: 18 }),
6542
+ * status: 'active',
6543
+ * },
6544
+ * riskSignals: { circleSentinel: true, warnings: [], earnKitWarnings: [] },
6545
+ * rewards: [{ token: 'MORPHO', tokenAddress: '0x...', apy: 0.0075 }],
6546
+ * collateral: [{ asset: 'cbBTC', assetAddress: '0x...', lltv: 0.86, supplyUsd: 50000000.25, allocationPct: 0.6 }],
6547
+ * // deprecated flat aliases (dual-emitted during migration)
6548
+ * vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
6549
+ * currentApy: 0.085,
6550
+ * nativeApy: 0.071,
6551
+ * vaultFee: 0.1,
6552
+ * totalDeposits: Amount.fromJSON({ raw: '15000000000000', decimals: 6 }),
6553
+ * liquidity: Amount.fromJSON({ raw: '5000000000000', decimals: 6 }),
6554
+ * status: 'active',
6555
+ * circleGuarded: true,
6556
+ * }
6557
+ * ```
6558
+ */
6559
+ interface VaultOpportunity extends EarnOpportunityBase {
6560
+ /**
6561
+ * Universal discriminator identifying the opportunity shape.
6562
+ *
6563
+ * Optional for now — a backend that predates the field omits it, matching
6564
+ * the other optional facets — so this release stays source-compatible for
6565
+ * code that constructs the type. A future release makes it required once
6566
+ * every backend emits it. Always present on responses from an emitting
6567
+ * backend; narrow on it before reading product-specific fields.
6568
+ */
6569
+ readonly productType?: 'vault';
6570
+ /** Collateral markets backing this vault. */
6571
+ readonly collateral: readonly CollateralInfo[];
6572
+ }
6573
+ /**
6574
+ * A yield opportunity available through the earn service.
6575
+ *
6576
+ * Modeled as a discriminated union on `productType` over a shared base.
6577
+ * Only the `vault` variant ships today; additional product types (e.g.
6578
+ * `lending_market`, `rwa_token`) are added as additive union members.
6579
+ */
6580
+ type EarnOpportunity = VaultOpportunity;
6222
6581
  /**
6223
6582
  * Per-vault error from a batch vault lookup.
6224
6583
  *
@@ -6398,6 +6757,14 @@ interface AssetAmount {
6398
6757
  */
6399
6758
  readonly status?: string | undefined;
6400
6759
  }
6760
+ /**
6761
+ * Source-fee quote expiry metadata returned by bridge prepare.
6762
+ *
6763
+ * `TIMESTAMP` expiries use an ISO-8601 UTC `expiresAt`; `BLOCK_NUMBER`
6764
+ * expiries use a source-chain `expiresAtBlock`, with an optional ISO-8601 UTC
6765
+ * `blockEstimatedAt` for when that block estimate was produced.
6766
+ */
6767
+ type EarnBridgeQuoteExpiry = Readonly<Exclude<z.infer<typeof bridgeQuoteExpirySchema>, undefined>>;
6401
6768
  /**
6402
6769
  * Result of a deposit operation returned by
6403
6770
  * {@link EarningProvider.deposit}.
@@ -6498,6 +6865,13 @@ interface EarnCrossChainDepositResult {
6498
6865
  * @example '2026-05-19T00:00:00Z'
6499
6866
  */
6500
6867
  readonly expiresAt: string;
6868
+ /** ISO-8601 UTC timestamp at which the source-fee quote was issued. */
6869
+ readonly quoteIssuedAt?: string | undefined;
6870
+ /**
6871
+ * Optional source-fee quote expiry metadata for display and refresh UX.
6872
+ * This deadline is independent of the prepared-bundle `expiresAt` above.
6873
+ */
6874
+ readonly quoteExpiry?: EarnBridgeQuoteExpiry | undefined;
6501
6875
  }
6502
6876
  /**
6503
6877
  * Status of one hop (source relay or destination mint) of a cross-chain
@@ -6758,27 +7132,21 @@ interface DepositQuoteInfo {
6758
7132
  */
6759
7133
  readonly fees: readonly AssetAmount[];
6760
7134
  /**
6761
- * Estimated native gas fees for the transactions needed to deposit
6762
- * (e.g. token approval and the deposit itself).
7135
+ * Estimated native gas fees for the transactions needed to deposit (e.g.
7136
+ * token approval and the deposit itself).
6763
7137
  *
6764
7138
  * Optional so that custom {@link EarningProvider} implementations are not
6765
7139
  * required to produce gas estimates. The bundled Earn Service provider
6766
- * always populates this with one entry per transaction in the flow. When an
6767
- * individual estimate cannot be produced, that entry is still present with
6768
- * `fees` set to `null` and `error` describing why — so a non-empty array
6769
- * does not imply every estimate succeeded; inspect each entry's `fees`.
6770
- *
6771
- * The number of entries depends on where estimation stopped: when the flow
6772
- * fails before the approval step is examined, a single entry named after
6773
- * the main action is returned. Look entries up by `name`, not by index.
6774
- *
6775
- * Each estimate simulates the transaction against current chain state.
6776
- * When a token approval is still pending (typically a first-time deposit),
6777
- * the deposit simulation reverts because the allowance is not yet in
6778
- * place, so the deposit entry resolves with `fees: null` while the
6779
- * approval entry still carries a real estimate. Render a fallback (e.g.
6780
- * "available after approval") for that case rather than treating it as an
6781
- * error.
7140
+ * populates this from the server-side estimate returned on the quote, with
7141
+ * one entry per action. Empty for cross-chain quotes, which resolve no
7142
+ * single source chain.
7143
+ *
7144
+ * Each entry is the Earn Service's estimate for that action, so a pending
7145
+ * token approval no longer causes the deposit entry to fail. An entry may
7146
+ * still carry `fees: null` with an `error` when the service could not
7147
+ * estimate it, so a non-empty array does not imply every estimate
7148
+ * succeeded — inspect each entry's `fees` and look entries up by `name`,
7149
+ * not by index.
6782
7150
  */
6783
7151
  readonly gasFees?: readonly EarnGasFeeEstimate$1[] | undefined;
6784
7152
  }
@@ -6819,26 +7187,21 @@ interface WithdrawalQuoteInfo {
6819
7187
  */
6820
7188
  readonly fees: readonly AssetAmount[];
6821
7189
  /**
6822
- * Estimated native gas fees for the transactions needed to withdraw.
7190
+ * Estimated native gas fees for the transactions needed to withdraw (e.g.
7191
+ * vault-share approval and the withdrawal itself).
6823
7192
  *
6824
7193
  * Optional so that custom {@link EarningProvider} implementations are not
6825
7194
  * required to produce gas estimates. The bundled Earn Service provider
6826
- * always populates this with one entry per transaction in the flow. When an
6827
- * individual estimate cannot be produced, that entry is still present with
6828
- * `fees` set to `null` and `error` describing why — so a non-empty array
6829
- * does not imply every estimate succeeded; inspect each entry's `fees`.
6830
- *
6831
- * The number of entries depends on where estimation stopped: when the flow
6832
- * fails before the approval step is examined, a single entry named after
6833
- * the main action is returned. Look entries up by `name`, not by index.
6834
- *
6835
- * Each estimate simulates the transaction against current chain state.
6836
- * When a vault-share approval is still pending (typically the first
6837
- * withdrawal from a vault), the withdrawal simulation reverts because the
6838
- * allowance is not yet in place, so the withdrawal entry resolves with
6839
- * `fees: null` while the approval entry still carries a real estimate.
6840
- * Render a fallback (e.g. "available after approval") for that case
6841
- * rather than treating it as an error.
7195
+ * populates this from the server-side estimate returned on the quote, with
7196
+ * one entry per action. Empty for cross-chain quotes, which resolve no
7197
+ * single source chain.
7198
+ *
7199
+ * Each entry is the Earn Service's estimate for that action, so a pending
7200
+ * vault-share approval no longer causes the withdrawal entry to fail. An
7201
+ * entry may still carry `fees: null` with an `error` when the service could
7202
+ * not estimate it, so a non-empty array does not imply every estimate
7203
+ * succeeded — inspect each entry's `fees` and look entries up by `name`,
7204
+ * not by index.
6842
7205
  */
6843
7206
  readonly gasFees?: readonly EarnGasFeeEstimate$1[] | undefined;
6844
7207
  /**
@@ -6871,16 +7234,13 @@ interface ClaimRewardsQuoteInfo {
6871
7234
  /** Reward tokens available for claiming. */
6872
7235
  readonly rewards: readonly AssetAmount[];
6873
7236
  /**
6874
- * Estimated native gas fees for claiming rewards. Empty when there are no
6875
- * rewards to claim.
7237
+ * Estimated native gas fees for claiming rewards.
6876
7238
  *
6877
7239
  * Optional so that custom {@link EarningProvider} implementations are not
6878
- * required to produce gas estimates. Otherwise the bundled Earn Service
6879
- * provider populates this with one entry per claim transaction; when an
6880
- * estimate cannot be produced, that entry is still present with `fees` set
6881
- * to `null` and `error` describing why — so a non-empty array does not imply
6882
- * every estimate succeeded; inspect each entry's `fees`. Look entries up by
6883
- * `name`, not by index.
7240
+ * required to produce gas estimates. The bundled Earn Service provider does
7241
+ * not return a gas estimate for claim-rewards quotes, so this is always
7242
+ * empty (`[]`); it is retained for API symmetry with the deposit and
7243
+ * withdrawal quotes.
6884
7244
  */
6885
7245
  readonly gasFees?: readonly EarnGasFeeEstimate$1[] | undefined;
6886
7246
  }
@@ -6891,8 +7251,8 @@ interface ClaimRewardsQuoteInfo {
6891
7251
  * `vaults` while per-vault failures are in `errors`.
6892
7252
  */
6893
7253
  interface GetVaultsResult {
6894
- /** Successfully resolved vault information. */
6895
- readonly vaults: readonly VaultInfo[];
7254
+ /** Successfully resolved opportunities. */
7255
+ readonly vaults: readonly EarnOpportunity[];
6896
7256
  /** Per-vault errors for failed lookups. */
6897
7257
  readonly errors: readonly VaultError[];
6898
7258
  }
@@ -6945,7 +7305,7 @@ interface ExploreVaultsPagination {
6945
7305
  */
6946
7306
  interface ExploreVaultsResult {
6947
7307
  /** Vaults matching the query, in the requested sort order. */
6948
- readonly vaults: readonly VaultInfo[];
7308
+ readonly vaults: readonly EarnOpportunity[];
6949
7309
  /** Pagination metadata for the query. */
6950
7310
  readonly pagination: ExploreVaultsPagination;
6951
7311
  }
@@ -7508,12 +7868,42 @@ type EarnAssetAmount = Omit<AssetAmount, 'amount'> & {
7508
7868
  /** Token amount in human-readable decimal format. */
7509
7869
  readonly amount: string;
7510
7870
  };
7511
- /** Vault information returned by the SDK. */
7512
- type EarnVaultInfo = Omit<VaultInfo, 'totalDeposits' | 'liquidity'> & {
7871
+ /**
7872
+ * Distributive `Omit` over a union.
7873
+ *
7874
+ * A plain `Omit<Union, K>` is not distributive: `keyof (A | B)` collapses to
7875
+ * the shared keys, dropping every variant-specific field and the discriminant
7876
+ * narrowing. Distributing over each member preserves the union.
7877
+ */
7878
+ type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
7879
+ /** Liquidity profile returned by the SDK with amounts as decimal strings. */
7880
+ type EarnLiquidityProfile = Omit<LiquidityProfile, 'totalDeposits' | 'available' | 'totalSupply'> & {
7881
+ /** Total value deposited in human-readable decimal format. */
7882
+ readonly totalDeposits: string;
7883
+ /** Available liquidity in human-readable decimal format. */
7884
+ readonly available: string;
7885
+ /** Outstanding vault share tokens (ERC4626 totalSupply) in decimal format. */
7886
+ readonly totalSupply: string;
7887
+ };
7888
+ /**
7889
+ * Vault information returned by the SDK.
7890
+ *
7891
+ * Derived with a distributive `Omit` so each opportunity variant keeps its
7892
+ * product-specific fields and the `productType` discriminant.
7893
+ */
7894
+ type EarnVaultInfo = DistributiveOmit<EarnOpportunity, 'totalDeposits' | 'liquidity' | 'liquidityProfile'> & {
7513
7895
  /** Total value deposited in human-readable decimal format. */
7514
7896
  readonly totalDeposits: string;
7515
7897
  /** Available liquidity in the vault in human-readable decimal format. */
7516
7898
  readonly liquidity: string;
7899
+ /**
7900
+ * Liquidity profile with amounts as human-readable decimal strings.
7901
+ *
7902
+ * Optional during the expand/contract migration window: a backend that
7903
+ * predates the nested facets omits it, so it is absent until the response
7904
+ * carries it.
7905
+ */
7906
+ readonly liquidityProfile?: EarnLiquidityProfile;
7517
7907
  };
7518
7908
  /** Result of a batch vault lookup. */
7519
7909
  type EarnGetVaultsResult = Omit<GetVaultsResult, 'vaults'> & {
@@ -8044,26 +8434,28 @@ interface AppKitContext {
8044
8434
  * Event handlers registered for AppKit operations.
8045
8435
  *
8046
8436
  * This property stores event handlers that are registered via the AppKit's
8047
- * `on()` method. Handlers are grouped by operation type. The current runtime
8048
- * bucket is `bridge`, and the context can add more operation buckets as AppKit
8049
- * wires action handlers for additional kits.
8437
+ * `on()` method. Handlers are grouped by operation type. The runtime buckets
8438
+ * are `bridge` and `earn`; the context can add more operation buckets as
8439
+ * AppKit wires action handlers for additional kits.
8050
8440
  *
8051
- * Within each operation bucket, handlers are keyed by action name (for example,
8052
- * `bridge.approve`) or `*` for wildcard handlers. Each action can have multiple
8053
- * handlers registered, allowing multiple subscribers to listen to the same event.
8441
+ * Within each operation bucket, handlers are keyed by action name (for
8442
+ * example, `bridge.approve` or `earn.deposit`) or `*` for wildcard handlers.
8443
+ * Each action can have multiple handlers registered, allowing multiple
8444
+ * subscribers to listen to the same event.
8054
8445
  *
8055
8446
  * The handlers are stored in the context to allow deferred registration with
8056
- * underlying operation kits, enabling a clean separation between event registration
8057
- * and operation execution.
8447
+ * underlying operation kits, enabling a clean separation between event
8448
+ * registration and operation execution.
8058
8449
  *
8059
8450
  * @example
8060
8451
  * ```typescript
8061
8452
  * const context = createContext()
8062
8453
  * // Handlers registered via kit.on() are stored by operation type
8063
- * // Bridge handlers are registered with BridgeKit when bridge() is executed
8454
+ * // Bridge handlers are registered with BridgeKit when bridge() runs
8455
+ * // Earn handlers are registered with EarnKit when earn operations run
8064
8456
  * ```
8065
8457
  */
8066
- actions: Record<'bridge', Record<string, ((payload: unknown) => void)[]>>;
8458
+ actions: Record<'bridge' | 'earn', Record<string, ((payload: unknown) => void)[]>>;
8067
8459
  /**
8068
8460
  * Disable error telemetry for all sub-kits.
8069
8461
  *
@@ -8356,5 +8748,40 @@ declare function getWithdrawalQuote<TFromAdapterCapabilities extends AdapterCapa
8356
8748
  * ```
8357
8749
  */
8358
8750
  declare function getClaimRewardsQuote<TFromAdapterCapabilities extends AdapterCapabilities = AdapterCapabilities>(context: AppKitContext, params: GetClaimRewardsQuoteParams<TFromAdapterCapabilities>): Promise<EarnClaimRewardsQuoteInfo>;
8751
+ /**
8752
+ * Resume a multi-phase earn operation that previously failed.
8753
+ *
8754
+ * Pass the {@link KitError} caught from `deposit`, `withdraw`, or
8755
+ * `claimRewards`. Completed phases can be skipped when the error carries
8756
+ * earn retry context. Call `isRetryableError(error)` first.
8757
+ *
8758
+ * @remarks
8759
+ * Retry re-fetches execution params and may re-submit the execute
8760
+ * transaction. Treat this as best-effort recovery if a prior execute
8761
+ * broadcast may still be in flight.
8762
+ *
8763
+ * @param context - AppKit context
8764
+ * @param error - The error caught from a previous multi-phase earn operation
8765
+ * @returns Promise resolving to the result of the resumed operation
8766
+ * @throws If the error is not retryable or lacks earn retry context
8767
+ *
8768
+ * @example
8769
+ * ```typescript
8770
+ * import { isRetryableError } from '@circle-fin/app-kit'
8771
+ * import { createContext } from '@circle-fin/app-kit/context'
8772
+ * import { retry } from '@circle-fin/app-kit/earn'
8773
+ *
8774
+ * const context = createContext()
8775
+ *
8776
+ * try {
8777
+ * await deposit(context, params)
8778
+ * } catch (error) {
8779
+ * if (isRetryableError(error)) {
8780
+ * const result = await retry(context, error)
8781
+ * }
8782
+ * }
8783
+ * ```
8784
+ */
8785
+ declare function retry(context: AppKitContext, error: unknown): Promise<EarnDepositOutcome | EarnWithdrawResult | EarnClaimRewardsResult>;
8359
8786
 
8360
- export { claimRewards, deposit, exploreVaults, exploreVaultsIterator, getClaimRewardsQuote, getCrossChainDepositStatus, getDepositQuote, getPosition, getVaults, getWithdrawalQuote, waitForCrossChainDeposit, withdraw };
8787
+ export { claimRewards, deposit, exploreVaults, exploreVaultsIterator, getClaimRewardsQuote, getCrossChainDepositStatus, getDepositQuote, getPosition, getVaults, getWithdrawalQuote, retry, waitForCrossChainDeposit, withdraw };