@circle-fin/provider-cctp-v2 1.3.1 → 1.4.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.
Files changed (6) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +98 -14
  3. package/index.cjs +5439 -2119
  4. package/index.d.ts +2139 -148
  5. package/index.mjs +5438 -2120
  6. package/package.json +1 -6
package/index.d.ts CHANGED
@@ -33,7 +33,6 @@ import { TransactionInstruction, Signer, AddressLookupTableAccount } from '@sola
33
33
  */
34
34
  /**
35
35
  * Represents basic information about a currency or token.
36
- * @interface Currency
37
36
  * @category Types
38
37
  * @description Provides the essential properties of a cryptocurrency or token.
39
38
  * @example
@@ -65,7 +64,6 @@ interface Currency {
65
64
  }
66
65
  /**
67
66
  * Base information that all chain definitions must include.
68
- * @interface BaseChainDefinition
69
67
  * @category Types
70
68
  * @description Provides the common properties shared by all blockchain definitions.
71
69
  * @example
@@ -125,6 +123,11 @@ interface BaseChainDefinition {
125
123
  * @description Its presence indicates that USDC is supported.
126
124
  */
127
125
  usdcAddress: string | null;
126
+ /**
127
+ * The contract address for USDT.
128
+ * @description Its presence indicates that USDT is supported.
129
+ */
130
+ usdtAddress: string | null;
128
131
  /**
129
132
  * Optional CCTP configuration.
130
133
  * @description If provided, the chain supports CCTP.
@@ -166,7 +169,6 @@ interface BaseChainDefinition {
166
169
  }
167
170
  /**
168
171
  * Represents chain definitions for Ethereum Virtual Machine (EVM) compatible blockchains.
169
- * @interface EVMChainDefinition
170
172
  * @extends BaseChainDefinition
171
173
  * @category Types
172
174
  * @description Adds properties specific to EVM chains.
@@ -198,7 +200,6 @@ interface EVMChainDefinition extends BaseChainDefinition {
198
200
  }
199
201
  /**
200
202
  * Represents chain definitions for non-EVM blockchains.
201
- * @interface NonEVMChainDefinition
202
203
  * @extends BaseChainDefinition
203
204
  * @category Types
204
205
  * @description Contains properties for blockchains that do not use the EVM.
@@ -251,6 +252,7 @@ type ChainType = EVMChainDefinition['type'] | NonEVMChainDefinition['type'];
251
252
  * rpcEndpoints: ['https://eth.example.com'],
252
253
  * eurcAddress: null,
253
254
  * usdcAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
255
+ * usdtAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7',
254
256
  * cctp: {
255
257
  * domain: 0,
256
258
  * contracts: {
@@ -313,18 +315,75 @@ type ChainDefinitionWithCCTPv2 = ChainDefinition & {
313
315
  * - A Blockchain enum value (e.g., Blockchain.Ethereum)
314
316
  * - A string literal of the blockchain value (e.g., "Ethereum")
315
317
  */
316
- type ChainIdentifier = ChainDefinition | Blockchain | `${Blockchain}`;
318
+ type ChainIdentifier$1 = ChainDefinition | Blockchain | `${Blockchain}`;
319
+ /**
320
+ * Split CCTP contract configuration.
321
+ *
322
+ * Used by chains that deploy separate TokenMessenger and MessageTransmitter contracts.
323
+ * This is the traditional CCTP architecture used by most EVM chains.
324
+ *
325
+ * @example
326
+ * ```typescript
327
+ * const splitConfig: CCTPSplitConfig = {
328
+ * type: 'split',
329
+ * tokenMessenger: '0x1234567890abcdef1234567890abcdef12345678',
330
+ * messageTransmitter: '0xabcdef1234567890abcdef1234567890abcdef12',
331
+ * confirmations: 12
332
+ * }
333
+ * ```
334
+ */
317
335
  interface CCTPSplitConfig {
318
336
  type: 'split';
319
337
  tokenMessenger: string;
320
338
  messageTransmitter: string;
321
339
  confirmations: number;
322
340
  }
341
+ /**
342
+ * Merged CCTP contract configuration.
343
+ *
344
+ * Used by chains that deploy a single unified CCTP contract.
345
+ * This simplified architecture is used by newer chain integrations.
346
+ *
347
+ * @example
348
+ * ```typescript
349
+ * const mergedConfig: CCTPMergedConfig = {
350
+ * type: 'merged',
351
+ * contract: '0x9876543210fedcba9876543210fedcba98765432',
352
+ * confirmations: 1
353
+ * }
354
+ * ```
355
+ */
323
356
  interface CCTPMergedConfig {
324
357
  type: 'merged';
325
358
  contract: string;
326
359
  confirmations: number;
327
360
  }
361
+ /**
362
+ * Version configuration for CCTP contracts.
363
+ *
364
+ * Defines whether the chain uses split or merged CCTP contract architecture.
365
+ * Split configuration uses separate TokenMessenger and MessageTransmitter contracts,
366
+ * while merged configuration uses a single unified contract.
367
+ *
368
+ * @example Split configuration (most EVM chains)
369
+ * ```typescript
370
+ * const splitConfig: VersionConfig = {
371
+ * type: 'split',
372
+ * tokenMessenger: '0x1234567890abcdef1234567890abcdef12345678',
373
+ * messageTransmitter: '0xabcdef1234567890abcdef1234567890abcdef12',
374
+ * confirmations: 12
375
+ * }
376
+ * ```
377
+ *
378
+ * @example Merged configuration (newer chains)
379
+ * ```typescript
380
+ * const mergedConfig: VersionConfig = {
381
+ * type: 'merged',
382
+ * contract: '0x9876543210fedcba9876543210fedcba98765432',
383
+ * confirmations: 1
384
+ * }
385
+ * ```
386
+ */
328
387
  type VersionConfig = CCTPSplitConfig | CCTPMergedConfig;
329
388
  type CCTPContracts = Partial<{
330
389
  v1: VersionConfig;
@@ -334,7 +393,6 @@ type CCTPContracts = Partial<{
334
393
  }>;
335
394
  /**
336
395
  * Configuration for the Cross-Chain Transfer Protocol (CCTP).
337
- * @interface CCTPConfig
338
396
  * @category Types
339
397
  * @description Contains the domain and required contract addresses for CCTP support.
340
398
  * @example
@@ -357,6 +415,22 @@ interface CCTPConfig {
357
415
  * The contracts required for CCTP.
358
416
  */
359
417
  contracts: CCTPContracts;
418
+ /**
419
+ * Indicates whether the chain supports forwarder for source and destination.
420
+ * @example
421
+ * ```typescript
422
+ * const chainWithForwarderSupported: ChainDefinition = {
423
+ * forwarderSupported: {
424
+ * source: true,
425
+ * destination: true,
426
+ * },
427
+ * }
428
+ * ```
429
+ */
430
+ forwarderSupported: {
431
+ source: boolean;
432
+ destination: boolean;
433
+ };
360
434
  }
361
435
  /**
362
436
  * Available kit contract types for enhanced chain functionality.
@@ -372,7 +446,7 @@ interface CCTPConfig {
372
446
  * const invalidType: KitContractType = 'invalid' // TypeScript error
373
447
  * ```
374
448
  */
375
- type KitContractType = 'bridge';
449
+ type KitContractType = 'bridge' | 'adapter';
376
450
  /**
377
451
  * Kit-specific contract addresses for enhanced chain functionality.
378
452
  *
@@ -643,8 +717,20 @@ type EvmPreparedChainRequestParams = {
643
717
  interface SolanaPreparedChainRequestParams {
644
718
  /**
645
719
  * The array of instructions to include in the transaction.
720
+ *
721
+ * @remarks
722
+ * Used for instruction-based transaction building. Mutually exclusive with
723
+ * `serializedTransaction`.
724
+ */
725
+ instructions?: TransactionInstruction[];
726
+ /**
727
+ * A pre-serialized transaction as a Uint8Array (e.g., from a service like Jupiter).
728
+ *
729
+ * @remarks
730
+ * Used for executing pre-built transactions from external services.
731
+ * The transaction may be partially signed. Mutually exclusive with `instructions`.
646
732
  */
647
- instructions: TransactionInstruction[];
733
+ serializedTransaction?: Uint8Array;
648
734
  /**
649
735
  * Additional signers besides the Adapter's wallet (e.g. program-derived authorities).
650
736
  */
@@ -913,7 +999,7 @@ type OperationContext<TAdapterCapabilities extends AdapterCapabilities = Adapter
913
999
  /**
914
1000
  * The blockchain network to use for this operation.
915
1001
  */
916
- chain: ChainIdentifier;
1002
+ chain: ChainIdentifier$1;
917
1003
  } & AddressField<ExtractAddressContext<TAdapterCapabilities>>;
918
1004
  /**
919
1005
  * Fully resolved context for an adapter operation, with concrete chain and address.
@@ -1322,6 +1408,99 @@ interface CCTPv2ActionMap {
1322
1408
  */
1323
1409
  permitParams?: PermitParams;
1324
1410
  };
1411
+ /**
1412
+ * Initiate a cross-chain USDC transfer using a custom bridge contract with hook data for CCTP forwarding.
1413
+ *
1414
+ * This action combines the custom bridge functionality with CCTP forwarding hookData.
1415
+ * It uses either `bridgeWithPreapprovalAndHook` or `bridgeWithPermitAndHook` contract
1416
+ * functions depending on whether permit parameters are provided.
1417
+ *
1418
+ * @remarks
1419
+ * When CCTP forwarding is enabled with custom burn, Circle's relay infrastructure will:
1420
+ * 1. Watch for the burn transaction with forwarding hookData
1421
+ * 2. Fetch the attestation automatically
1422
+ * 3. Submit the destination mint transaction on behalf of the user
1423
+ * 4. Deduct the relay fee from the minted USDC
1424
+ *
1425
+ * The hookData must be formatted with the CCTP forwarding magic bytes prefix
1426
+ * followed by version and length fields. Use the `buildForwardingHookData`
1427
+ * utility to construct properly formatted hookData.
1428
+ *
1429
+ * @example
1430
+ * ```typescript
1431
+ * import { buildForwardingHookData } from '@core/utils'
1432
+ * import { hasCustomContractSupport } from '@core/chains'
1433
+ *
1434
+ * if (hasCustomContractSupport(sourceChain, 'bridge')) {
1435
+ * await adapter.action('cctp.v2.customBurnWithHook', {
1436
+ * amount: BigInt('1000000'),
1437
+ * mintRecipient: '0x...',
1438
+ * maxFee: BigInt('50000'),
1439
+ * minFinalityThreshold: 1000,
1440
+ * fromChain: ethereum,
1441
+ * toChain: base,
1442
+ * hookData: buildForwardingHookData()
1443
+ * })
1444
+ * }
1445
+ * ```
1446
+ */
1447
+ customBurnWithHook: CCTPv2ActionMap['customBurn'] & {
1448
+ /**
1449
+ * Hex-encoded hook data for CCTP forwarding.
1450
+ *
1451
+ * The hookData signals to Circle's Orbit relayer that forwarding is requested.
1452
+ * Must be formatted with the CCTP forwarding magic bytes prefix ("cctp-forward"
1453
+ * right-padded to 24 bytes) followed by uint32 version and uint32 length fields.
1454
+ *
1455
+ * Use the `buildForwardingHookData` utility to construct properly formatted hookData.
1456
+ */
1457
+ hookData: string;
1458
+ };
1459
+ /**
1460
+ * Initiate a cross-chain USDC transfer with hook data for CCTP forwarding.
1461
+ *
1462
+ * This action extends the standard `depositForBurn` with an additional `hookData`
1463
+ * parameter that signals to Circle's Orbit relayer that the user wants automated
1464
+ * attestation fetching and destination mint execution.
1465
+ *
1466
+ * @remarks
1467
+ * When CCTP forwarding is enabled, Circle's relay infrastructure will:
1468
+ * 1. Watch for the burn transaction with forwarding hookData
1469
+ * 2. Fetch the attestation automatically
1470
+ * 3. Submit the destination mint transaction on behalf of the user
1471
+ * 4. Deduct the relay fee from the minted USDC
1472
+ *
1473
+ * The hookData must be formatted with the CCTP forwarding magic bytes prefix
1474
+ * followed by version and length fields. Use the `buildForwardingHookData`
1475
+ * utility to construct properly formatted hookData.
1476
+ *
1477
+ * @example
1478
+ * ```typescript
1479
+ * import { buildForwardingHookData } from '@core/utils'
1480
+ *
1481
+ * await adapter.action('cctp.v2.depositForBurnWithHook', {
1482
+ * amount: BigInt('1000000'),
1483
+ * mintRecipient: '0x...',
1484
+ * maxFee: BigInt('50000'), // Must cover burn fee + forwarding fee
1485
+ * minFinalityThreshold: 1000,
1486
+ * fromChain: ethereum,
1487
+ * toChain: base,
1488
+ * hookData: buildForwardingHookData()
1489
+ * })
1490
+ * ```
1491
+ */
1492
+ depositForBurnWithHook: CCTPv2ActionMap['depositForBurn'] & {
1493
+ /**
1494
+ * Hex-encoded hook data for CCTP forwarding.
1495
+ *
1496
+ * The hookData signals to Circle's Orbit relayer that forwarding is requested.
1497
+ * Must be formatted with the CCTP forwarding magic bytes prefix ("cctp-forward"
1498
+ * right-padded to 24 bytes) followed by uint32 version and uint32 length fields.
1499
+ *
1500
+ * Use the `buildForwardingHookData` utility to construct properly formatted hookData.
1501
+ */
1502
+ hookData: string;
1503
+ };
1325
1504
  }
1326
1505
 
1327
1506
  /**
@@ -1348,31 +1527,443 @@ interface CCTPActionMap {
1348
1527
  }
1349
1528
 
1350
1529
  /**
1351
- * Action map for native token operations (ETH, SOL, MATIC, etc.).
1530
+ * Permit signature standards for gasless token approvals.
1352
1531
  *
1353
- * Native tokens are the primary currency of each blockchain network,
1354
- * used for paying transaction fees and as a store of value.
1355
- * These actions operate on the native token without requiring
1356
- * a separate token contract address.
1532
+ * Defines the permit types that can be used to approve token spending
1533
+ * without requiring a separate approval transaction.
1357
1534
  *
1358
1535
  * @remarks
1359
- * Native token operations differ from ERC-20/SPL token operations
1360
- * in that they don't require contract interactions for basic transfers
1361
- * and balance checks.
1536
+ * - NONE: No permit, tokens must be pre-approved via separate transaction
1537
+ * - EIP2612: Standard ERC-20 permit (USDC, DAI v2, and most modern tokens)
1538
+ */
1539
+ declare enum PermitType {
1540
+ /** No permit required - tokens must be pre-approved */
1541
+ NONE = 0,
1542
+ /** EIP-2612 standard permit */
1543
+ EIP2612 = 1
1544
+ }
1545
+ /**
1546
+ * Token input with permit signature for gasless approval.
1362
1547
  *
1363
- * @see {@link ActionMap} for the complete action structure
1548
+ * The Adapter Contract uses this to pull tokens from the user's wallet
1549
+ * using permit signatures instead of requiring separate approval transactions.
1550
+ *
1551
+ * @example
1552
+ * ```typescript
1553
+ * const tokenInput: TokenInput = {
1554
+ * permitType: PermitType.EIP2612,
1555
+ * token: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
1556
+ * from: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
1557
+ * amount: 1000000n, // 1 USDC
1558
+ * permitCalldata: '0x...' // Encoded permit(value, deadline, v, r, s)
1559
+ * }
1560
+ * ```
1364
1561
  */
1365
- interface NativeActionMap {
1562
+ interface TokenInput {
1366
1563
  /**
1367
- * Get the native token balance (SOL, ETH, etc.) for a wallet address.
1564
+ * Type of permit to execute.
1368
1565
  */
1369
- balanceOf: ActionParameters & {
1370
- /**
1371
- * The address to check the native balance for. If not provided, it will be
1372
- * automatically derived from the adapter context.
1373
- */
1374
- walletAddress?: string | undefined;
1375
- };
1566
+ permitType: PermitType;
1567
+ /**
1568
+ * Token contract address to pull from user.
1569
+ */
1570
+ token: `0x${string}`;
1571
+ /**
1572
+ * Amount of tokens to pull via permit.
1573
+ */
1574
+ amount: bigint;
1575
+ /**
1576
+ * ABI-encoded permit calldata.
1577
+ *
1578
+ * For EIP-2612: encode(value, deadline, v, r, s)
1579
+ * For Permit2: encode(permit, signature)
1580
+ *
1581
+ * @example '0x0000000000000000000000000000000000000000000000000000000000989680...'
1582
+ */
1583
+ permitCalldata: `0x${string}`;
1584
+ }
1585
+ /**
1586
+ * Single instruction to execute within the Adapter Contract.
1587
+ *
1588
+ * Each instruction represents a contract call (swap, fee collection, etc.)
1589
+ * with pre-execution approval and post-execution validation.
1590
+ *
1591
+ * @example
1592
+ * ```typescript
1593
+ * const swapInstruction: Instruction = {
1594
+ * target: '0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE', // LiFi Diamond
1595
+ * data: '0x...', // LiFi swap calldata
1596
+ * value: 0n,
1597
+ * tokenIn: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
1598
+ * amountToApprove: 1000000000n, // 1000 USDC to approve
1599
+ * tokenOut: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT
1600
+ * minTokenOut: 995000000n // 995 USDT minimum (0.5% slippage)
1601
+ * }
1602
+ * ```
1603
+ */
1604
+ interface Instruction {
1605
+ /**
1606
+ * Target contract address to call.
1607
+ *
1608
+ * Can be a DEX router, fee taker contract, or token contract.
1609
+ */
1610
+ target: `0x${string}`;
1611
+ /**
1612
+ * ABI-encoded calldata for the target contract.
1613
+ */
1614
+ data: `0x${string}`;
1615
+ /**
1616
+ * ETH value to send with the call (for native token operations).
1617
+ *
1618
+ * @defaultValue 0n
1619
+ */
1620
+ value: bigint;
1621
+ /**
1622
+ * Token to approve to target before executing instruction.
1623
+ *
1624
+ * Set to zero address (0x00...00) to disable pre-approval.
1625
+ */
1626
+ tokenIn: `0x${string}`;
1627
+ /**
1628
+ * Amount of tokenIn to approve to target before executing instruction.
1629
+ *
1630
+ * @remarks
1631
+ * Field name matches the adapter contract's `amountToApprove` parameter exactly.
1632
+ *
1633
+ * @defaultValue 0n if tokenIn is zero address
1634
+ */
1635
+ amountToApprove: bigint;
1636
+ /**
1637
+ * Token to validate minimum balance after instruction.
1638
+ *
1639
+ * Set to zero address (0x00...00) to disable post-validation.
1640
+ */
1641
+ tokenOut: `0x${string}`;
1642
+ /**
1643
+ * Minimum required balance of tokenOut after instruction.
1644
+ *
1645
+ * @defaultValue 0n if tokenOut is zero address
1646
+ */
1647
+ minTokenOut: bigint;
1648
+ }
1649
+ /**
1650
+ * Token recipient for residual sweep.
1651
+ *
1652
+ * After all instructions complete, the Adapter Contract sweeps
1653
+ * any remaining balances to the specified beneficiaries.
1654
+ */
1655
+ interface TokenRecipient {
1656
+ /**
1657
+ * Token contract address to sweep.
1658
+ */
1659
+ token: `0x${string}`;
1660
+ /**
1661
+ * Address to receive swept tokens.
1662
+ */
1663
+ beneficiary: `0x${string}`;
1664
+ }
1665
+ /**
1666
+ * Execution parameters for the Adapter Contract.
1667
+ *
1668
+ * This struct is signed via EIP-712 by the Circle proxy and verified
1669
+ * on-chain to ensure the execution is authorized.
1670
+ *
1671
+ * @remarks
1672
+ * The executeParams are provided by the stablecoin-service and must be
1673
+ * passed to the Adapter Contract exactly as received (no modification).
1674
+ *
1675
+ * @example
1676
+ * ```typescript
1677
+ * const executeParams: ExecuteParams = {
1678
+ * instructions: [
1679
+ * { target: dexRouter, data: swapCalldata, ... }
1680
+ * ],
1681
+ * tokens: [
1682
+ * { token: USDC, beneficiary: userAddress },
1683
+ * { token: USDT, beneficiary: userAddress }
1684
+ * ],
1685
+ * execId: 123456789n,
1686
+ * deadline: BigInt(Math.floor(Date.now() / 1000) + 1800),
1687
+ * metadata: '0x'
1688
+ * }
1689
+ * ```
1690
+ */
1691
+ interface ExecuteParams {
1692
+ /**
1693
+ * Array of instructions to execute sequentially.
1694
+ *
1695
+ * Each instruction can be a swap, fee collection, or other contract call.
1696
+ */
1697
+ instructions: Instruction[];
1698
+ /**
1699
+ * Token recipients for residual sweep.
1700
+ *
1701
+ * Typically a 2-tuple: [tokenIn recipient, tokenOut recipient]
1702
+ */
1703
+ tokens: TokenRecipient[];
1704
+ /**
1705
+ * Unique execution identifier for replay protection.
1706
+ *
1707
+ * Must be globally unique and is marked as used after execution.
1708
+ */
1709
+ execId: bigint;
1710
+ /**
1711
+ * Execution deadline timestamp (Unix seconds).
1712
+ *
1713
+ * Transaction reverts if block.timestamp is greater than deadline.
1714
+ */
1715
+ deadline: bigint;
1716
+ /**
1717
+ * Optional metadata for tracking and analytics.
1718
+ */
1719
+ metadata: `0x${string}`;
1720
+ }
1721
+ /**
1722
+ * Parameters for executing a swap transaction via the Adapter smart contract.
1723
+ *
1724
+ * This action executes swap transactions through the Adapter Contract, which
1725
+ * handles token approvals via permits (EIP-2612, Permit2, etc.) and executes
1726
+ * multi-step swap instructions atomically on-chain.
1727
+ *
1728
+ * @remarks
1729
+ * The swap flow uses the Adapter Contract pattern:
1730
+ * 1. Service provides `executeParams` and `signature` (proxy-signed EIP-712)
1731
+ * 2. SDK builds `tokenInputs` with permit signatures for gasless approvals
1732
+ * 3. SDK calls AdapterContract.execute(executeParams, tokenInputs, signature)
1733
+ * 4. Adapter Contract pulls tokens via permits, executes swaps, validates outputs
1734
+ *
1735
+ * This enables:
1736
+ * - Single atomic transaction (permit + swap in one tx)
1737
+ * - Gasless approvals via EIP-2612/Permit2
1738
+ * - Slippage protection enforced on-chain
1739
+ * - Multi-step instructions (swap + fees) atomically
1740
+ *
1741
+ * **Permit Support**: The SDK constructs `TokenInput` with `permitCalldata`
1742
+ * containing the encoded permit signature. The Adapter Contract executes
1743
+ * the permit on-chain before pulling tokens.
1744
+ *
1745
+ * @example
1746
+ * ```typescript
1747
+ * import type { ExecuteSwapParams } from '@core/adapter'
1748
+ * import { createSwap } from '@core/service-client'
1749
+ * import { PermitType } from '@core/adapter'
1750
+ *
1751
+ * // Get swap transaction from service
1752
+ * const swapResponse = await createSwap({
1753
+ * tokenInAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
1754
+ * tokenOutAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
1755
+ * tokenInChain: 'Ethereum',
1756
+ * fromAddress: '0x...',
1757
+ * toAddress: '0x...',
1758
+ * amount: '1000000',
1759
+ * apiKey: 'KIT_KEY:...',
1760
+ * })
1761
+ *
1762
+ * // Build token inputs with permit
1763
+ * const tokenInputs: TokenInput[] = [{
1764
+ * permitType: PermitType.EIP2612,
1765
+ * token: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
1766
+ * from: userAddress,
1767
+ * amount: 1000000n,
1768
+ * permitCalldata: '0x...' // Encoded permit signature
1769
+ * }]
1770
+ *
1771
+ * // Prepare action parameters
1772
+ * const params: ExecuteSwapParams = {
1773
+ * executeParams: swapResponse.transaction.executeParams,
1774
+ * tokenInputs,
1775
+ * signature: swapResponse.transaction.signature,
1776
+ * inputAmount: BigInt(swapResponse.amount),
1777
+ * tokenInAddress: swapResponse.tokenInAddress as `0x${string}`
1778
+ * }
1779
+ * ```
1780
+ */
1781
+ interface ExecuteSwapEVMParams extends ActionParameters {
1782
+ /**
1783
+ * Execution parameters from the stablecoin-service.
1784
+ *
1785
+ * Contains instructions, token recipients, execution ID, deadline, and metadata.
1786
+ * This is an EIP-712 signed struct that the Adapter Contract validates.
1787
+ *
1788
+ * Provided by the service - do not modify.
1789
+ */
1790
+ executeParams: ExecuteParams;
1791
+ /**
1792
+ * Token inputs with permit signatures for gasless approvals.
1793
+ *
1794
+ * The SDK constructs this array with permit data for each token that needs
1795
+ * to be pulled from the user's wallet. The Adapter Contract executes these
1796
+ * permits on-chain before executing swap instructions.
1797
+ *
1798
+ * @remarks
1799
+ * For EIP-2612 permits, the SDK must:
1800
+ * 1. Build typed data with token, spender (Adapter), amount, nonce, deadline
1801
+ * 2. Get user signature via `adapter.signTypedData()`
1802
+ * 3. Encode as permitCalldata: encode(value, deadline, v, r, s)
1803
+ *
1804
+ * @example
1805
+ * ```typescript
1806
+ * [{
1807
+ * permitType: PermitType.EIP2612,
1808
+ * token: '0xUSDC',
1809
+ * from: userAddress,
1810
+ * amount: 1000000n,
1811
+ * permitCalldata: '0x...'
1812
+ * }]
1813
+ * ```
1814
+ */
1815
+ tokenInputs: TokenInput[];
1816
+ /**
1817
+ * EIP-712 signature from the Circle proxy service.
1818
+ *
1819
+ * The service signs the executeParams to authorize the execution.
1820
+ * The Adapter Contract verifies this signature on-chain.
1821
+ *
1822
+ * Provided by the service - do not modify.
1823
+ */
1824
+ signature: `0x${string}`;
1825
+ /**
1826
+ * Swap input amount in base units.
1827
+ *
1828
+ * @remarks
1829
+ * The amount of tokens being swapped, provided in the token's base units (e.g., wei for ETH,
1830
+ * smallest denomination for ERC20 tokens). This value should be extracted from the service
1831
+ * response, as it represents the authoritative swap amount for the operation.
1832
+ *
1833
+ * For native currency swaps (ETH → USDC), this amount is sent as the transaction `value`.
1834
+ * For ERC20 swaps (USDC → USDT), this amount determines the permit or approval quantity.
1835
+ *
1836
+ * @see CreateSwapResponse.amount - Service response field containing this value
1837
+ *
1838
+ * @example
1839
+ * ```typescript
1840
+ * import { createSwap } from '@core/service-client'
1841
+ *
1842
+ * // Get swap transaction from service
1843
+ * const response = await createSwap({
1844
+ * tokenInAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
1845
+ * amount: '1000000', // 1 USDC (6 decimals)
1846
+ * ...
1847
+ * })
1848
+ *
1849
+ * // Prepare swap execution using service response amount
1850
+ * await adapter.prepareAction('swap.execute', {
1851
+ * executeParams: response.transaction.executeParams,
1852
+ * tokenInputs,
1853
+ * signature: response.transaction.signature,
1854
+ * inputAmount: BigInt(response.amount),
1855
+ * tokenInAddress: response.tokenInAddress,
1856
+ * }, context)
1857
+ * ```
1858
+ */
1859
+ inputAmount: bigint;
1860
+ /**
1861
+ * Token address being swapped from.
1862
+ *
1863
+ * @remarks
1864
+ * Used to determine if the swap involves native currency (ETH, MATIC, etc.) or ERC20 tokens.
1865
+ * When tokenInAddress is NATIVE_TOKEN_ADDRESS (0xEeee...), the inputAmount is sent as tx.value.
1866
+ *
1867
+ * @see CreateSwapResponse.tokenInAddress - Service response field containing this value
1868
+ *
1869
+ * @example '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' for ETH
1870
+ * @example '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC
1871
+ */
1872
+ tokenInAddress: `0x${string}`;
1873
+ }
1874
+ /**
1875
+ * Parameters for executing a swap transaction on Solana.
1876
+ *
1877
+ * This action executes swap transactions on Solana chains by deserializing
1878
+ * and executing a pre-built transaction provided by the stablecoin-service.
1879
+ *
1880
+ * @remarks
1881
+ * Unlike EVM chains that use the Adapter Contract pattern, Solana swaps
1882
+ * execute a fully serialized transaction provided by the service. The
1883
+ * transaction is base64-encoded and contains all necessary instructions
1884
+ * for the swap operation.
1885
+ *
1886
+ * The service handles:
1887
+ * - DEX aggregator routing (Jupiter, etc.)
1888
+ * - Fee collection
1889
+ * - Slippage protection
1890
+ * - Token account management
1891
+ *
1892
+ * @example
1893
+ * ```typescript
1894
+ * import type { ExecuteSwapSolanaParams } from '@core/adapter'
1895
+ * import { createSwap } from '@core/service-client'
1896
+ *
1897
+ * // Get swap transaction from service
1898
+ * const swapResponse = await createSwap({
1899
+ * tokenInAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
1900
+ * tokenOutAddress: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr',
1901
+ * tokenInChain: 'Solana',
1902
+ * fromAddress: 'YubQzu18FDqJRyNfG8JqHmsdbxhnoQqcKUHBdUkN6tP',
1903
+ * toAddress: 'YubQzu18FDqJRyNfG8JqHmsdbxhnoQqcKUHBdUkN6tP',
1904
+ * amount: '1000000',
1905
+ * apiKey: 'KIT_KEY:...',
1906
+ * })
1907
+ *
1908
+ * // Prepare action parameters
1909
+ * const params: ExecuteSwapSolanaParams = {
1910
+ * serializedTransaction: swapResponse.transaction.data
1911
+ * }
1912
+ * ```
1913
+ */
1914
+ interface ExecuteSwapSolanaParams extends ActionParameters {
1915
+ /**
1916
+ * Base64-encoded serialized Solana transaction.
1917
+ *
1918
+ * This transaction is fully constructed by the stablecoin-service and
1919
+ * contains all swap instructions, fee payments, and token account setup.
1920
+ * The transaction must be deserialized, signed, and submitted to the network.
1921
+ *
1922
+ * Provided by the service - do not modify.
1923
+ *
1924
+ * @example 'AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAJFQg...'
1925
+ */
1926
+ serializedTransaction: string;
1927
+ }
1928
+ /**
1929
+ * Parameters accepted by the swap.execute action, supporting both EVM and Solana chains.
1930
+ *
1931
+ * @remarks
1932
+ * This union type covers all chain-specific swap execution parameter interfaces
1933
+ * currently supported by the App Kit. Extend this union to support
1934
+ * additional blockchains as needed. Each member provides all fields required
1935
+ * to prepare and execute a pre-built swap transaction on its respective chain.
1936
+ *
1937
+ * **Type Narrowing**: The correct parameter type is inferred from the chain type
1938
+ * in the `OperationContext` passed to `adapter.prepareAction()`. Action handlers
1939
+ * use property-based type guards (checking for `executeParams`/`tokenInputs` for EVM
1940
+ * or `serializedTransaction` for Solana) to narrow the union type at runtime.
1941
+ *
1942
+ * - {@link ExecuteSwapEVMParams} - For EVM chains (has `executeParams` and `tokenInputs`)
1943
+ * - {@link ExecuteSwapSolanaParams} - For Solana chains (has `serializedTransaction`)
1944
+ */
1945
+ type ExecuteSwapParams = ExecuteSwapEVMParams | ExecuteSwapSolanaParams;
1946
+ /**
1947
+ * Action map for swap operations on EVM chains.
1948
+ *
1949
+ * This namespace contains actions related to token swapping operations.
1950
+ * These actions handle the execution of pre-built swap transactions from
1951
+ * DEX aggregators and routing services.
1952
+ *
1953
+ * @remarks
1954
+ * The swap namespace is designed to be extensible for future swap-related
1955
+ * operations such as multi-hop swaps, batched swaps, or swap-and-bridge
1956
+ * compositions.
1957
+ */
1958
+ interface SwapActionMap {
1959
+ /**
1960
+ * Execute a pre-built swap transaction.
1961
+ *
1962
+ * This action prepares and executes swap transactions constructed by the
1963
+ * stablecoin-service API. It accepts transaction parameters (to, data, value)
1964
+ * and returns a prepared chain request suitable for gas estimation or execution.
1965
+ */
1966
+ readonly execute: ExecuteSwapParams;
1376
1967
  }
1377
1968
 
1378
1969
  interface TokenActionMap {
@@ -1588,14 +2179,89 @@ interface USDCActionMap {
1588
2179
  }
1589
2180
 
1590
2181
  /**
1591
- * Central registry of all available action namespaces and their operations.
2182
+ * USDT-specific operations that automatically resolve the token address.
1592
2183
  *
1593
- * Define the complete action map structure used throughout the bridge kit.
1594
- * Each top-level key represents a namespace (e.g., 'token', 'usdc') containing
1595
- * related operations. The structure supports arbitrary nesting depth through
1596
- * the recursive utility types provided in this module.
2184
+ * These include standard ERC20 operations. The interface provides the same core
2185
+ * operations as {@link TokenActionMap} but without requiring a `tokenAddress`
2186
+ * parameter.
1597
2187
  *
1598
- * @remarks
2188
+ * @example
2189
+ * ```typescript
2190
+ * // USDT operations (address auto-resolved)
2191
+ * await adapter.action('usdt.transfer', {
2192
+ * to: '0x1234...',
2193
+ * amount: '1000000' // 1 USDT
2194
+ * })
2195
+ *
2196
+ * // vs. general token operations (address required)
2197
+ * await adapter.action('token.transfer', {
2198
+ * tokenAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
2199
+ * to: '0x1234...',
2200
+ * amount: '1000000'
2201
+ * })
2202
+ * ```
2203
+ */
2204
+ type BaseUSDTActions = {
2205
+ [K in keyof TokenActionMap]: Omit<TokenActionMap[K], 'tokenAddress'>;
2206
+ };
2207
+ /**
2208
+ * USDT action map with standard ERC20 operations.
2209
+ *
2210
+ * This provides standard token operations for USDT transfers.
2211
+ */
2212
+ interface USDTActionMap {
2213
+ /**
2214
+ * Transfer USDT tokens directly from the wallet to another address.
2215
+ *
2216
+ * Automatically uses the USDT contract address for the current chain.
2217
+ */
2218
+ transfer: BaseUSDTActions['transfer'];
2219
+ }
2220
+
2221
+ /**
2222
+ * Native token-related action maps for the bridge kit.
2223
+ *
2224
+ * This module provides action definitions for native token operations.
2225
+ */
2226
+ interface NativeActionMap {
2227
+ /**
2228
+ * Transfer native tokens directly from the wallet to another address.
2229
+ */
2230
+ transfer: ActionParameters & {
2231
+ /**
2232
+ * The chain to transfer the native tokens on.
2233
+ */
2234
+ chain?: ChainIdentifier$1;
2235
+ /**
2236
+ * The address to send the native tokens to.
2237
+ */
2238
+ to: string;
2239
+ /**
2240
+ * The amount of native tokens to transfer.
2241
+ */
2242
+ amount: bigint;
2243
+ };
2244
+ /**
2245
+ * Get the native token balance (SOL, ETH, etc.) for a wallet address.
2246
+ */
2247
+ balanceOf: ActionParameters & {
2248
+ /**
2249
+ * The address to check the native balance for. If not provided, it will be
2250
+ * automatically derived from the adapter context.
2251
+ */
2252
+ walletAddress?: string | undefined;
2253
+ };
2254
+ }
2255
+
2256
+ /**
2257
+ * Central registry of all available action namespaces and their operations.
2258
+ *
2259
+ * Define the complete action map structure used throughout the bridge kit.
2260
+ * Each top-level key represents a namespace (e.g., 'token', 'usdc') containing
2261
+ * related operations. The structure supports arbitrary nesting depth through
2262
+ * the recursive utility types provided in this module.
2263
+ *
2264
+ * @remarks
1599
2265
  * This interface serves as the foundation for type-safe action dispatching
1600
2266
  * and provides compile-time validation of action keys and payload types.
1601
2267
  * All action-related utility types derive from this central definition.
@@ -1606,12 +2272,16 @@ interface USDCActionMap {
1606
2272
  interface ActionMap {
1607
2273
  /** CCTP-specific operations with automatic address resolution. */
1608
2274
  readonly cctp: CCTPActionMap;
1609
- /** Native token operations (ETH, SOL, MATIC, etc.). */
1610
- readonly native: NativeActionMap;
1611
2275
  /** General token operations requiring explicit token addresses. */
1612
2276
  readonly token: TokenActionMap;
1613
2277
  /** USDC-specific operations with automatic address resolution. */
1614
2278
  readonly usdc: USDCActionMap;
2279
+ /** USDT-specific operations with automatic address resolution. */
2280
+ readonly usdt: USDTActionMap;
2281
+ /** Native token operations. */
2282
+ readonly native: NativeActionMap;
2283
+ /** Swap operations for DEX aggregator integrations. */
2284
+ readonly swap: SwapActionMap;
1615
2285
  }
1616
2286
  /**
1617
2287
  * Determine if a type represents an action parameter object (leaf node).
@@ -1750,7 +2420,7 @@ type ActionHandler<TActionKey extends ActionKeys> = (params: ActionPayload<TActi
1750
2420
  * This type defines a registry object where each key is a valid action key
1751
2421
  * (as defined by {@link ActionKeys}) and each value is an {@link ActionHandler}
1752
2422
  * capable of processing the payload for that action. This enables strongly-typed
1753
- * handler registration and lookup for all supported actions in the Stablecoin Kits.
2423
+ * handler registration and lookup for all supported actions in the App Kits.
1754
2424
  *
1755
2425
  * @remarks
1756
2426
  * Each handler is typed as {@link ActionHandler}, which means the handler
@@ -2012,11 +2682,11 @@ interface AdapterCapabilities {
2012
2682
  /**
2013
2683
  * Abstract class defining the standard interface for an adapter that interacts with a specific blockchain.
2014
2684
  *
2015
- * A `Adapter` is responsible for encapsulating chain-specific logic necessary to
2685
+ * An `Adapter` is responsible for encapsulating chain-specific logic necessary to
2016
2686
  * perform operations like sending transactions, querying balances, or interacting with smart contracts.
2017
2687
  * Implementations of this class will provide concrete logic for a particular blockchain protocol.
2018
2688
  *
2019
- * This abstraction allows the Stablecoin Kits to work with multiple blockchains in a uniform way.
2689
+ * This abstraction allows the App Kit to work with multiple blockchains in a uniform way.
2020
2690
  *
2021
2691
  * @typeParam TAdapterCapabilities - The adapter capabilities type for compile-time address validation.
2022
2692
  * When provided, enables strict typing of operation context based on the adapter's address control model.
@@ -2277,6 +2947,35 @@ declare abstract class Adapter<TAdapterCapabilities extends AdapterCapabilities
2277
2947
  * @returns A promise that resolves to the total transaction fee as a bigint.
2278
2948
  */
2279
2949
  abstract calculateTransactionFee(baseComputeUnits: bigint, bufferBasisPoints: bigint | undefined, chain: ChainDefinition): Promise<EstimatedGas>;
2950
+ /**
2951
+ * Get the decimal places for a token address on a given chain.
2952
+ *
2953
+ * This method fetches the number of decimal places from a token contract.
2954
+ * Different chain types implement this differently:
2955
+ * - EVM: Calls the `decimals()` function on ERC-20 contracts
2956
+ * - Solana: Reads the `decimals` field from the SPL token mint account
2957
+ *
2958
+ * @param tokenAddress - The token contract address (EVM) or mint address (Solana)
2959
+ * @param chain - The chain definition where the token is deployed
2960
+ * @returns Promise resolving to the number of decimal places for the token
2961
+ * @throws Error when the token contract doesn't exist or decimals cannot be fetched
2962
+ *
2963
+ * @example
2964
+ * ```typescript
2965
+ * import { EthersAdapter } from '@circle-fin/adapter-ethers-v6'
2966
+ * import { Ethereum } from '@core/chains'
2967
+ *
2968
+ * const adapter = new EthersAdapter({ signer })
2969
+ *
2970
+ * // Fetch decimals for DAI token
2971
+ * const decimals = await adapter.getTokenDecimals(
2972
+ * '0x6B175474E89094C44Da98b954EedeAC495271d0F',
2973
+ * Ethereum
2974
+ * )
2975
+ * console.log(decimals) // 18
2976
+ * ```
2977
+ */
2978
+ abstract getTokenDecimals(tokenAddress: string, chain: ChainDefinition): Promise<number>;
2280
2979
  }
2281
2980
 
2282
2981
  /**
@@ -2333,111 +3032,1210 @@ interface ApiPollingConfig {
2333
3032
  * console.log('Event received:', payload);
2334
3033
  * });
2335
3034
  *
2336
- * // Dispatch an event
2337
- * transferEvents.dispatch('completed', {
2338
- * txHash: '0x123',
2339
- * destinationTxHash: '0xabc'
2340
- * });
3035
+ * // Dispatch an event
3036
+ * transferEvents.dispatch('completed', {
3037
+ * txHash: '0x123',
3038
+ * destinationTxHash: '0xabc'
3039
+ * });
3040
+ * ```
3041
+ */
3042
+ declare class Actionable<AllActions> {
3043
+ private readonly handlers;
3044
+ private readonly wildcard;
3045
+ /**
3046
+ * Register a handler for a specific action.
3047
+ *
3048
+ * @param action - The specific action key to listen for.
3049
+ * @param handler - The callback function to execute when the action occurs.
3050
+ *
3051
+ * @example
3052
+ * ```typescript
3053
+ * const events = new Actionable<{ dataReceived: string }>();
3054
+ *
3055
+ * events.on('dataReceived', (data) => {
3056
+ * console.log(`Received: ${data}`);
3057
+ * });
3058
+ * ```
3059
+ */
3060
+ on<Action extends keyof AllActions>(action: Action, handler: (payload: AllActions[Action]) => void): void;
3061
+ /**
3062
+ * Register a handler for all actions using the wildcard '*'.
3063
+ *
3064
+ * @param action - The wildcard '*' signifying interest in all actions.
3065
+ * @param handler - The callback function to execute for any action.
3066
+ *
3067
+ * @example
3068
+ * ```typescript
3069
+ * const events = new Actionable<{ started: boolean; completed: string }>();
3070
+ *
3071
+ * events.on('*', (payload) => {
3072
+ * console.log('Action occurred:', payload);
3073
+ * });
3074
+ * ```
3075
+ */
3076
+ on(action: '*', handler: (payload: AllActions[keyof AllActions]) => void): void;
3077
+ /**
3078
+ * Remove a previously registered handler for a specific action.
3079
+ *
3080
+ * @param action - The specific action key to unregister from.
3081
+ * @param handler - The callback function to remove.
3082
+ *
3083
+ * @example
3084
+ * ```typescript
3085
+ * const events = new Actionable<{ dataReceived: string }>();
3086
+ *
3087
+ * const handler = (data: string) => console.log(data);
3088
+ * events.on('dataReceived', handler);
3089
+ *
3090
+ * // Later, to remove the handler:
3091
+ * events.off('dataReceived', handler);
3092
+ * ```
3093
+ */
3094
+ off<Action extends keyof AllActions>(action: Action, handler: (payload: AllActions[Action]) => void): void;
3095
+ /**
3096
+ * Remove a previously registered wildcard handler.
3097
+ *
3098
+ * @param action - The wildcard '*' signifying removal from all actions.
3099
+ * @param handler - The callback function to remove.
3100
+ *
3101
+ * @example
3102
+ * ```typescript
3103
+ * const events = new Actionable<{ started: boolean; completed: string }>();
3104
+ *
3105
+ * const globalHandler = (payload: any) => console.log(payload);
3106
+ * events.on('*', globalHandler);
3107
+ *
3108
+ * // Later, to remove the handler:
3109
+ * events.off('*', globalHandler);
3110
+ * ```
3111
+ */
3112
+ off(action: '*', handler: (payload: AllActions[keyof AllActions]) => void): void;
3113
+ /**
3114
+ * Dispatch an action with its payload to all registered handlers.
3115
+ *
3116
+ * This method notifies both:
3117
+ * - Handlers registered specifically for this action
3118
+ * - Wildcard handlers registered for all actions
3119
+ *
3120
+ * @param action - The action key identifying the event type.
3121
+ * @param payload - The data associated with the action.
3122
+ *
3123
+ * @example
3124
+ * ```typescript
3125
+ * type Actions = {
3126
+ * transferStarted: { amount: string; destination: string };
3127
+ * transferComplete: { txHash: string };
3128
+ * };
3129
+ *
3130
+ * const events = new Actionable<Actions>();
3131
+ *
3132
+ * // Dispatch an event
3133
+ * events.dispatch('transferStarted', {
3134
+ * amount: '100',
3135
+ * destination: '0xABC123'
3136
+ * });
3137
+ * ```
3138
+ */
3139
+ dispatch<K extends keyof AllActions>(action: K, payload: AllActions[K]): void;
3140
+ }
3141
+
3142
+ /**
3143
+ * Module augmentation to register known token symbols.
3144
+ *
3145
+ * @remarks
3146
+ * This file augments the `TokenSymbolRegistry` interface to provide
3147
+ * type-safe autocomplete for built-in tokens.
3148
+ *
3149
+ * When imported, TypeScript will recognize 'USDC' as a valid
3150
+ * `TokenSymbol` value with autocomplete support.
3151
+ *
3152
+ * Other packages or applications can create their own augmentations
3153
+ * to add additional tokens.
3154
+ *
3155
+ * @example
3156
+ * ```typescript
3157
+ * import '@core/tokens' // Automatically includes this augmentation
3158
+ *
3159
+ * const symbol: TokenSymbol = 'USDC' // ✓ Autocomplete shows USDC
3160
+ * ```
3161
+ */
3162
+ declare module './types' {
3163
+ /**
3164
+ * Module augmentation: Adds known token symbols as valid keys
3165
+ * to the TokenSymbolRegistry interface.
3166
+ *
3167
+ * Keys are explicitly listed to ensure IDE autocomplete works properly.
3168
+ */
3169
+ interface TokenSymbolRegistry {
3170
+ USDC: true;
3171
+ USDT: true;
3172
+ EURC: true;
3173
+ DAI: true;
3174
+ USDE: true;
3175
+ PYUSD: true;
3176
+ WETH: true;
3177
+ WBTC: true;
3178
+ WSOL: true;
3179
+ WAVAX: true;
3180
+ WPOL: true;
3181
+ ETH: true;
3182
+ POL: true;
3183
+ PLUME: true;
3184
+ MON: true;
3185
+ }
3186
+ }
3187
+
3188
+ /**
3189
+ * Module augmentation to register Blockchain enum values as ChainIdentifiers.
3190
+ *
3191
+ * @remarks
3192
+ * This file augments the `ChainRegistry` interface to provide type-safe
3193
+ * autocomplete for all `Blockchain` enum values from `@core/chains`.
3194
+ *
3195
+ * When this augmentation is imported (via `@core/tokens`), TypeScript will
3196
+ * recognize all blockchain identifiers as valid `ChainIdentifier` values
3197
+ * with IDE autocomplete support.
3198
+ *
3199
+ * The `Blockchain` enum values are converted to their string representations,
3200
+ * enabling both enum values and string literals to be accepted as chain identifiers.
3201
+ *
3202
+ * @example
3203
+ * ```typescript
3204
+ * import { Blockchain } from '@core/chains'
3205
+ * import type { ChainIdentifier } from '@core/tokens'
3206
+ *
3207
+ * // Using enum value
3208
+ * const chain1: ChainIdentifier = Blockchain.Ethereum
3209
+ *
3210
+ * // Using string literal (with autocomplete!)
3211
+ * const chain2: ChainIdentifier = 'Base'
3212
+ *
3213
+ * // Arbitrary strings also work (escape hatch for custom chains)
3214
+ * const chain3: ChainIdentifier = 'my-custom-chain'
3215
+ * ```
3216
+ */
3217
+
3218
+ declare module './types' {
3219
+ /**
3220
+ * Module augmentation: Adds all Blockchain enum values as valid keys
3221
+ * to the ChainRegistry interface for type-safe chain identifier support.
3222
+ *
3223
+ * This ensures both enum property access (e.g., Blockchain.Ethereum) and plain
3224
+ * string literals (e.g., 'Ethereum') are accepted by TypeScript as chain keys,
3225
+ * providing robust autocomplete and error checking.
3226
+ *
3227
+ * NOTE:
3228
+ * - This interface intentionally has no body. It merges a mapped Record type
3229
+ * into ChainRegistry solely for type augmentation.
3230
+ * - This empty-body construct is a necessary TypeScript idiom for module
3231
+ * augmentation with Record types—directly listing mapped keys is not
3232
+ * feasible in interface extensions.
3233
+ *
3234
+ * eslint-disable-next-line directives below suppress linter complaints about
3235
+ * the empty interface/mapping, which are benign and required for this pattern.
3236
+ */
3237
+ interface ChainRegistry extends Record<`${Blockchain}`, true> {
3238
+ }
3239
+ }
3240
+
3241
+ /**
3242
+ * Creates a union type that preserves IDE autocomplete for known literals
3243
+ * while still accepting any string at runtime.
3244
+ *
3245
+ * @remarks
3246
+ * This pattern uses `Record<never, never>` (an empty record type) to prevent
3247
+ * TypeScript from widening string literals to just `string`. This gives us
3248
+ * the best of both worlds: autocomplete for known values and flexibility
3249
+ * for arbitrary strings.
3250
+ *
3251
+ * @typeParam T - The known string literal union to preserve.
3252
+ */
3253
+ type LiteralUnion<T extends string> = T | (string & Record<never, never>);
3254
+ /**
3255
+ * Registry for known chain identifiers (augmentation target).
3256
+ *
3257
+ * @remarks
3258
+ * This empty interface exists solely for module augmentation. Extend it to
3259
+ * register chain identifiers for type-safe token definitions.
3260
+ *
3261
+ * **Why an interface?** TypeScript only allows module augmentation on
3262
+ * interfaces, not type aliases.
3263
+ *
3264
+ * **Note:** This is NOT the EVM "chain ID" (numeric like 1 for Ethereum).
3265
+ * It's a human-readable identifier like "Ethereum", "Solana", "Base".
3266
+ *
3267
+ * **Usage**
3268
+ *
3269
+ * Without augmentation, `ChainIdentifier` defaults to `string`.
3270
+ * With `@core/chains` imported, you get autocomplete for all `Blockchain` values.
3271
+ *
3272
+ * **Custom Chains**
3273
+ *
3274
+ * ```typescript
3275
+ * declare module '@core/tokens' {
3276
+ * interface ChainRegistry {
3277
+ * MyChain: true
3278
+ * MyTestnet: true
3279
+ * }
3280
+ * }
3281
+ * // Now ChainIdentifier includes 'MyChain' | 'MyTestnet' | ...
3282
+ * ```
3283
+ *
3284
+ * The value (`true`) is a placeholder—only the keys matter.
3285
+ *
3286
+ * NOTE: The eslint-disable below suppresses warnings about empty interfaces.
3287
+ * This is intentional—the interface exists solely as an augmentation target.
3288
+ */
3289
+ interface ChainRegistry {
3290
+ }
3291
+ /**
3292
+ * Union of all registered chain identifiers.
3293
+ *
3294
+ * @remarks
3295
+ * Derived from `ChainRegistry` keys:
3296
+ * - Without augmentation: `string`
3297
+ * - With `@core/chains`: `'Ethereum' | 'Solana' | ...` plus any string
3298
+ *
3299
+ * Uses `LiteralUnion` to preserve IDE autocomplete while allowing
3300
+ * arbitrary strings at runtime.
3301
+ *
3302
+ * @example
3303
+ * ```typescript
3304
+ * const chain: ChainIdentifier = 'Ethereum' // Autocomplete works
3305
+ * const custom: ChainIdentifier = 'my-chain' // Also valid
3306
+ * ```
3307
+ */
3308
+ type ChainIdentifier = keyof ChainRegistry extends never ? string : LiteralUnion<Extract<keyof ChainRegistry, string>>;
3309
+ /**
3310
+ * Maps chain identifiers to their token locators.
3311
+ *
3312
+ * @remarks
3313
+ * The key is a chain identifier (type-safe when `KnownChainIdentifiers` is
3314
+ * augmented). This enables a single token definition to work across chains.
3315
+ *
3316
+ * When `@core/chains` is imported, you get autocomplete for known chains
3317
+ * like `Ethereum`, `Base`, `Solana`, etc.
3318
+ *
3319
+ * @example
3320
+ * ```typescript
3321
+ * import { Blockchain } from '@core/chains'
3322
+ *
3323
+ * const usdcLocators: ChainLocatorMap = {
3324
+ * [Blockchain.Ethereum]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
3325
+ * [Blockchain.Solana]: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
3326
+ * [Blockchain.Base]: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
3327
+ * }
3328
+ *
3329
+ * // Or with string keys (always works)
3330
+ * const locators: ChainLocatorMap = {
3331
+ * 'ethereum': '0xa0b86991...',
3332
+ * 'my-custom-chain': '0x1234...',
3333
+ * }
3334
+ * ```
3335
+ */
3336
+ type ChainLocatorMap = Record<ChainIdentifier, string>;
3337
+ /**
3338
+ * Complete definition of a token including metadata and chain locators.
3339
+ *
3340
+ * @remarks
3341
+ * This is the canonical representation of a token in the registry.
3342
+ * It includes the symbol, decimals, and chain-specific locators.
3343
+ *
3344
+ * @example
3345
+ * ```typescript
3346
+ * const usdc: TokenDefinition = {
3347
+ * symbol: 'USDC',
3348
+ * decimals: 6,
3349
+ * locators: {
3350
+ * [Blockchain.Ethereum]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
3351
+ * [Blockchain.Solana]: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
3352
+ * },
3353
+ * }
3354
+ * ```
3355
+ */
3356
+ interface TokenDefinition {
3357
+ /**
3358
+ * The token symbol (e.g., "USDC", "EURC").
3359
+ */
3360
+ readonly symbol: string;
3361
+ /**
3362
+ * The default number of decimal places for the token.
3363
+ * Used when no chain-specific override exists in {@link chainDecimals}.
3364
+ * @example 6 for USDC, 18 for most ERC20 tokens
3365
+ */
3366
+ readonly decimals: number;
3367
+ /**
3368
+ * Chain-specific locators for the token.
3369
+ * Keys are chain identifiers, values are the token address/locator on that chain.
3370
+ * Not all chains need to be present - tokens may only exist on a subset of chains.
3371
+ */
3372
+ readonly locators: Partial<ChainLocatorMap>;
3373
+ /**
3374
+ * Optional per-chain decimal overrides.
3375
+ *
3376
+ * Some tokens have different decimal places on different chains
3377
+ * (e.g., USDe is 18 decimals on EVM but 9 decimals on Solana).
3378
+ * When present, the value for a specific chain takes precedence
3379
+ * over the default {@link decimals}.
3380
+ */
3381
+ readonly chainDecimals?: Partial<Record<ChainIdentifier, number>>;
3382
+ }
3383
+ /**
3384
+ * A raw token locator selector with explicit decimals.
3385
+ *
3386
+ * @remarks
3387
+ * Use this form when working with arbitrary tokens not in the registry.
3388
+ * The `locator` is the chain-specific address, and `decimals` is required
3389
+ * unless using lenient mode.
3390
+ *
3391
+ * @example
3392
+ * ```typescript
3393
+ * // Selecting a custom token by address
3394
+ * const selector: RawTokenSelector = {
3395
+ * locator: '0x1234567890abcdef1234567890abcdef12345678',
3396
+ * decimals: 18,
3397
+ * }
3398
+ * ```
3399
+ */
3400
+ interface RawTokenSelector {
3401
+ /**
3402
+ * The chain-specific token locator (address, program ID, etc.).
3403
+ */
3404
+ readonly locator: string;
3405
+ /**
3406
+ * The number of decimal places.
3407
+ * Required in strict mode, optional in lenient mode.
3408
+ */
3409
+ readonly decimals?: number;
3410
+ }
3411
+ /**
3412
+ * Registry for known token symbols (augmentation target).
3413
+ *
3414
+ * @remarks
3415
+ * This empty interface exists solely for module augmentation. Extend it to
3416
+ * register token symbols for type-safe selection.
3417
+ *
3418
+ * **Why an interface?** TypeScript only allows module augmentation on
3419
+ * interfaces, not type aliases.
3420
+ *
3421
+ * **Usage**
3422
+ *
3423
+ * Without augmentation, `TokenSymbol` defaults to `string`.
3424
+ *
3425
+ * ```typescript
3426
+ * declare module '@core/tokens' {
3427
+ * interface TokenSymbolRegistry {
3428
+ * USDC: true
3429
+ * EURC: true
3430
+ * }
3431
+ * }
3432
+ * // Now TokenSymbol includes 'USDC' | 'EURC' | ...
3433
+ * ```
3434
+ *
3435
+ * NOTE: The eslint-disable below suppresses warnings about empty interfaces.
3436
+ * This is intentional—the interface exists solely as an augmentation target.
3437
+ */
3438
+ interface TokenSymbolRegistry {
3439
+ }
3440
+ /**
3441
+ * Union type of all registered token symbols.
3442
+ *
3443
+ * @remarks
3444
+ * This type is derived from the keys of `TokenSymbolRegistry`:
3445
+ * - **Without augmentation** — Simply `string` (any value)
3446
+ * - **With augmentation** — `'USDC' | 'USDT' | ...` plus any string
3447
+ *
3448
+ * Uses `LiteralUnion` to preserve autocomplete for known values while
3449
+ * still accepting any string at runtime.
3450
+ *
3451
+ * @example
3452
+ * ```typescript
3453
+ * // With symbols.augment imported - autocomplete works
3454
+ * const symbol: TokenSymbol = 'USDC'
3455
+ *
3456
+ * // Custom strings still accepted
3457
+ * const symbol: TokenSymbol = 'MY_TOKEN'
3458
+ * ```
3459
+ */
3460
+ type TokenSymbol = keyof TokenSymbolRegistry extends never ? string : LiteralUnion<Extract<keyof TokenSymbolRegistry, string>>;
3461
+ /**
3462
+ * Selector for identifying a token.
3463
+ *
3464
+ * @remarks
3465
+ * Can be one of:
3466
+ * - A symbol string (e.g., "USDC") - resolves from registry
3467
+ * - A raw locator object with explicit decimals - for arbitrary tokens
3468
+ *
3469
+ * Using a symbol is preferred when the token is in the registry, as it
3470
+ * automatically resolves decimals and the correct chain address.
3471
+ *
3472
+ * @example
3473
+ * ```typescript
3474
+ * // By symbol (preferred for known tokens)
3475
+ * const selector1: TokenSelector = 'USDC'
3476
+ *
3477
+ * // By raw locator (for arbitrary tokens)
3478
+ * const selector2: TokenSelector = {
3479
+ * locator: '0x1234...',
3480
+ * decimals: 18,
3481
+ * }
3482
+ * ```
3483
+ */
3484
+ type TokenSelector = TokenSymbol | RawTokenSelector;
3485
+ /**
3486
+ * The resolved token information after registry lookup.
3487
+ *
3488
+ * @remarks
3489
+ * This is the result of resolving a `TokenSelector` against a chain.
3490
+ * It always contains the locator and decimals, and optionally the symbol
3491
+ * if the token was resolved from the registry.
3492
+ */
3493
+ interface ResolvedToken {
3494
+ /**
3495
+ * The token symbol, if known.
3496
+ * Present when resolved from registry, absent for raw locators.
3497
+ */
3498
+ readonly symbol?: string;
3499
+ /**
3500
+ * The number of decimal places for the token.
3501
+ */
3502
+ readonly decimals: number;
3503
+ /**
3504
+ * The chain-specific token locator (address, program ID, etc.).
3505
+ */
3506
+ readonly locator: string;
3507
+ }
3508
+ /**
3509
+ * Interface for the token registry.
3510
+ *
3511
+ * @remarks
3512
+ * The registry is the sole source of truth for token information.
3513
+ * It supports both symbol-based and raw locator-based token selection.
3514
+ *
3515
+ * @example
3516
+ * ```typescript
3517
+ * import { createTokenRegistry } from '@core/tokens'
3518
+ *
3519
+ * // Create registry (includes built-in tokens like USDC)
3520
+ * const registry = createTokenRegistry()
3521
+ *
3522
+ * // Resolve by symbol
3523
+ * const usdc = registry.resolve('USDC', 'Ethereum')
3524
+ * console.log(usdc.locator) // '0xa0b86991...'
3525
+ *
3526
+ * // Resolve raw locator
3527
+ * const custom = registry.resolve({ locator: '0x...', decimals: 18 }, 'Ethereum')
3528
+ * ```
3529
+ */
3530
+ interface TokenRegistry {
3531
+ /**
3532
+ * Resolve a token selector to concrete token information for a chain.
3533
+ *
3534
+ * @param selector - The token to resolve (symbol or raw locator).
3535
+ * @param chainId - The chain identifier to resolve for.
3536
+ * @returns The resolved token information.
3537
+ * @throws When the token cannot be resolved (unknown symbol, missing decimals, etc.).
3538
+ */
3539
+ resolve(selector: TokenSelector, chainId: ChainIdentifier): ResolvedToken;
3540
+ /**
3541
+ * Resolve a token by chain-specific locator (address/program ID).
3542
+ *
3543
+ * @param address - The token locator to resolve.
3544
+ * @param chainId - The chain identifier to resolve for.
3545
+ * @returns The resolved token information.
3546
+ * @throws When no registry token matches the locator on the chain.
3547
+ */
3548
+ resolveByAddress(address: string, chainId: ChainIdentifier): ResolvedToken;
3549
+ /**
3550
+ * Get a token definition by symbol.
3551
+ *
3552
+ * @param symbol - The token symbol (e.g., "USDC").
3553
+ * @returns The token definition, or undefined if not found.
3554
+ */
3555
+ get(symbol: string): TokenDefinition | undefined;
3556
+ /**
3557
+ * Check if a symbol is registered.
3558
+ *
3559
+ * @param symbol - The token symbol to check.
3560
+ * @returns True if the symbol is in the registry.
3561
+ */
3562
+ has(symbol: string): boolean;
3563
+ /**
3564
+ * Get all registered token symbols.
3565
+ *
3566
+ * @returns An array of registered symbol strings.
3567
+ */
3568
+ symbols(): string[];
3569
+ /**
3570
+ * Get all registered token definitions.
3571
+ *
3572
+ * @returns An array of all TokenDefinition objects in the registry.
3573
+ */
3574
+ entries(): TokenDefinition[];
3575
+ }
3576
+
3577
+ /**
3578
+ * Structured fields that can be attached to log entries.
3579
+ */
3580
+ type LogFields = Record<string, unknown>;
3581
+ /**
3582
+ * Logger interface providing structured logging with child scoping.
3583
+ *
3584
+ * @remarks
3585
+ * This interface defines a minimal, framework-agnostic logging contract.
3586
+ * The underlying implementation uses pino for transport handling (console,
3587
+ * file, remote, JSON, pretty, etc.) but consumers only interact with this
3588
+ * stable interface.
3589
+ *
3590
+ * @example
3591
+ * ```typescript
3592
+ * import { createLogger } from '@core/runtime'
3593
+ *
3594
+ * const logger = createLogger({ level: 'debug' })
3595
+ *
3596
+ * // Simple message
3597
+ * logger.info('Server started')
3598
+ *
3599
+ * // Message with structured fields
3600
+ * logger.info('Request received', { method: 'POST', path: '/api/transfer' })
3601
+ *
3602
+ * // Create child logger with context
3603
+ * const requestLogger = logger.child({ requestId: 'abc-123' })
3604
+ * requestLogger.debug('Processing transfer')
3605
+ * // Output includes: requestId in all subsequent logs
3606
+ * ```
3607
+ */
3608
+ interface Logger {
3609
+ /**
3610
+ * Log a debug-level message.
3611
+ * @param message - The log message.
3612
+ * @param fields - Optional structured fields.
3613
+ * @returns void
3614
+ */
3615
+ debug(message: string, fields?: LogFields): void;
3616
+ /**
3617
+ * Log an info-level message.
3618
+ * @param message - The log message.
3619
+ * @param fields - Optional structured fields.
3620
+ * @returns void
3621
+ */
3622
+ info(message: string, fields?: LogFields): void;
3623
+ /**
3624
+ * Log a warning-level message.
3625
+ * @param message - The log message.
3626
+ * @param fields - Optional structured fields.
3627
+ * @returns void
3628
+ */
3629
+ warn(message: string, fields?: LogFields): void;
3630
+ /**
3631
+ * Log an error-level message.
3632
+ * @param message - The log message.
3633
+ * @param fields - Optional structured fields.
3634
+ * @returns void
3635
+ */
3636
+ error(message: string, fields?: LogFields): void;
3637
+ /**
3638
+ * Create a child logger with additional contextual bindings.
3639
+ *
3640
+ * @param tags - Key-value pairs to add to the child logger's context.
3641
+ * Undefined values are filtered out automatically.
3642
+ * @returns A new Logger instance with merged bindings.
3643
+ *
3644
+ * @example
3645
+ * ```typescript
3646
+ * const requestLogger = logger.child({ requestId: 'req-123' })
3647
+ * const userLogger = requestLogger.child({ userId: 'user-456' })
3648
+ * // All logs from userLogger include both requestId and userId
3649
+ * ```
3650
+ */
3651
+ child(tags: LogFields): Logger;
3652
+ }
3653
+
3654
+ /**
3655
+ * Handler function for event subscriptions.
3656
+ */
3657
+ type EventHandler = (event: Event) => void | Promise<void>;
3658
+ /**
3659
+ * Event bus for publishing and subscribing to events.
3660
+ *
3661
+ * @remarks
3662
+ * Supports wildcard topic subscriptions:
3663
+ * - `*` matches exactly one segment
3664
+ * - `**` matches zero or more segments
3665
+ *
3666
+ * @example
3667
+ * ```typescript
3668
+ * const bus = createEventBus()
3669
+ *
3670
+ * // Subscribe to all events
3671
+ * bus.on((event) => console.log(event))
3672
+ *
3673
+ * // Subscribe to specific topic
3674
+ * bus.on('tx.wait.started', (event) => console.log(event))
3675
+ *
3676
+ * // Subscribe with wildcard
3677
+ * bus.on('tx.wait.*', (event) => console.log(event))
3678
+ * bus.on('tx.**', (event) => console.log(event))
3679
+ *
3680
+ * // Emit events
3681
+ * bus.emit({ name: 'tx.wait.started', data: { txId: '0x123' } })
3682
+ * ```
3683
+ */
3684
+ interface EventBus {
3685
+ /**
3686
+ * Emit an event to all matching subscribers.
3687
+ *
3688
+ * @param event - The event to emit.
3689
+ * @remarks
3690
+ * Synchronous and never throws. Handler errors are isolated.
3691
+ */
3692
+ emit(event: Event): void;
3693
+ /**
3694
+ * Create a child event bus with scoped tags.
3695
+ *
3696
+ * @param tags - Tags to merge into all emitted events.
3697
+ * @returns A new EventBus with merged tags.
3698
+ */
3699
+ child(tags: Tags): EventBus;
3700
+ /**
3701
+ * Subscribe to all events.
3702
+ *
3703
+ * @param handler - Function called for every event.
3704
+ * @returns Unsubscribe function.
3705
+ */
3706
+ on(handler: EventHandler): () => void;
3707
+ /**
3708
+ * Subscribe to events matching a pattern.
3709
+ *
3710
+ * @param pattern - Topic pattern (supports `*` and `**` wildcards).
3711
+ * @param handler - Function called for matching events.
3712
+ * @returns Unsubscribe function.
3713
+ *
3714
+ * @remarks
3715
+ * Wildcard semantics:
3716
+ * - `*` matches exactly one segment (no dots)
3717
+ * - `**` matches zero or more segments (only valid at end)
3718
+ *
3719
+ * @example
3720
+ * ```typescript
3721
+ * bus.on('*', handler) // matches 'tx', 'user' (single segment only)
3722
+ * bus.on('tx.wait.*', handler) // matches tx.wait.started, tx.wait.failed
3723
+ * bus.on('tx.wait.**', handler) // matches tx.wait, tx.wait.started, tx.wait.foo.bar
3724
+ * bus.on('**', handler) // matches all events
3725
+ * ```
3726
+ */
3727
+ on(pattern: string, handler: EventHandler): () => void;
3728
+ }
3729
+
3730
+ /**
3731
+ * Metrics type definitions for the Runtime module.
3732
+ *
3733
+ * @remarks
3734
+ * Define a minimal, pluggable metrics interface that can be backed by any
3735
+ * metrics library (hot-shots, dd-trace, prom-client, etc.).
3736
+ *
3737
+ * The interface supports:
3738
+ * - Counters for monotonically increasing values
3739
+ * - Histograms for distributions (latencies, sizes)
3740
+ * - Timers as a convenience wrapper for timing operations
3741
+ * - Label scoping via `child()` for dimensional metrics
3742
+ *
3743
+ * @example
3744
+ * ```typescript
3745
+ * // Basic usage
3746
+ * metrics.counter('requests.total').inc({ method: 'POST' })
3747
+ * metrics.histogram('request.duration').observe({ status: 200 }, 42.5)
3748
+ *
3749
+ * // Timer convenience
3750
+ * const stop = metrics.timer('db.query').start({ table: 'users' })
3751
+ * await query()
3752
+ * stop() // Records duration automatically
3753
+ *
3754
+ * // Scoping adds base labels to all metrics
3755
+ * const scoped = metrics.child({ service: 'bridge', env: 'prod' })
3756
+ * scoped.counter('transfers').inc() // Includes service + env labels
3757
+ * ```
3758
+ */
3759
+ /**
3760
+ * Labels for dimensional metrics.
3761
+ *
3762
+ * @remarks
3763
+ * Labels (also called tags in some systems) are key-value pairs that
3764
+ * provide dimensions for metric aggregation and filtering.
3765
+ * Values must be primitives for serialization compatibility.
3766
+ *
3767
+ * @example
3768
+ * ```typescript
3769
+ * const labels: MetricLabels = {
3770
+ * chain: 'Ethereum',
3771
+ * status: 'success',
3772
+ * retries: 3,
3773
+ * cached: true,
3774
+ * }
3775
+ * ```
3776
+ */
3777
+ type MetricLabels = Record<string, string | number | boolean>;
3778
+ /**
3779
+ * A counter metric for monotonically increasing values.
3780
+ *
3781
+ * @remarks
3782
+ * Use counters for values that only go up: request counts, error counts,
3783
+ * bytes processed, etc. The value resets only on process restart.
3784
+ *
3785
+ * @see {@link Metrics.counter} to obtain a Counter instance.
3786
+ *
3787
+ * @example
3788
+ * ```typescript
3789
+ * const counter = metrics.counter('http.requests')
3790
+ *
3791
+ * // Increment by 1
3792
+ * counter.inc()
3793
+ * counter.inc({ method: 'GET' })
3794
+ *
3795
+ * // Increment by specific value
3796
+ * counter.inc(5)
3797
+ * counter.inc({ method: 'POST' }, 3)
3798
+ * ```
3799
+ */
3800
+ interface Counter {
3801
+ /**
3802
+ * Increment the counter value.
3803
+ *
3804
+ * @param labelsOrValue - The labels object or increment value.
3805
+ * When a number, increments by that amount with no labels.
3806
+ * When an object, uses as labels with optional value in second param.
3807
+ * @param value - The increment value when first arg is labels. Default: 1.
3808
+ * @returns void
3809
+ *
3810
+ * @example
3811
+ * ```typescript
3812
+ * counter.inc() // +1, no labels
3813
+ * counter.inc(5) // +5, no labels
3814
+ * counter.inc({ method: 'GET' }) // +1, with labels
3815
+ * counter.inc({ method: 'POST' }, 3) // +3, with labels
3816
+ * ```
3817
+ */
3818
+ inc(labelsOrValue?: MetricLabels | number, value?: number): void;
3819
+ }
3820
+ /**
3821
+ * A histogram metric for recording value distributions.
3822
+ *
3823
+ * @remarks
3824
+ * Use histograms for values that vary and need percentile analysis:
3825
+ * request durations, response sizes, queue depths, etc.
3826
+ *
3827
+ * @see {@link Metrics.histogram} to obtain a Histogram instance.
3828
+ *
3829
+ * @example
3830
+ * ```typescript
3831
+ * const histogram = metrics.histogram('http.duration')
3832
+ *
3833
+ * // Record a value
3834
+ * histogram.observe(42.5)
3835
+ * histogram.observe({ status: 200 }, 42.5)
3836
+ * ```
3837
+ */
3838
+ interface Histogram {
3839
+ /**
3840
+ * Record an observation value.
3841
+ *
3842
+ * @param labelsOrValue - The labels object or observed value.
3843
+ * When a number, records that value with no labels.
3844
+ * When an object, uses as labels with value in second param.
3845
+ * @param value - The observed value when first arg is labels. Default: 0.
3846
+ * @returns void
3847
+ *
3848
+ * @example
3849
+ * ```typescript
3850
+ * histogram.observe(42.5) // Value only
3851
+ * histogram.observe({ status: 200 }, 42.5) // With labels
3852
+ * ```
3853
+ */
3854
+ observe(labelsOrValue?: MetricLabels | number, value?: number): void;
3855
+ }
3856
+ /**
3857
+ * A timer metric for measuring operation durations.
3858
+ *
3859
+ * @remarks
3860
+ * Timers provide a convenience wrapper that automatically records durations
3861
+ * to an underlying histogram. Call `start()` to begin timing and the
3862
+ * returned function to stop and record the elapsed time in milliseconds.
3863
+ *
3864
+ * @see {@link Metrics.timer} to obtain a Timer instance.
3865
+ *
3866
+ * @example
3867
+ * ```typescript
3868
+ * const timer = metrics.timer('db.query')
3869
+ *
3870
+ * // Start timing
3871
+ * const stop = timer.start({ table: 'users' })
3872
+ * await performQuery()
3873
+ * stop() // Records duration in milliseconds
3874
+ * ```
3875
+ */
3876
+ interface Timer {
3877
+ /**
3878
+ * Start timing an operation.
3879
+ *
3880
+ * @param labels - The optional labels for the timing observation.
3881
+ * @returns A stop function that records the duration when called.
3882
+ *
3883
+ * @example
3884
+ * ```typescript
3885
+ * const stop = timer.start({ operation: 'fetch' })
3886
+ * await fetchData()
3887
+ * stop() // Records elapsed time
3888
+ * ```
3889
+ */
3890
+ start(labels?: MetricLabels): () => void;
3891
+ }
3892
+ /**
3893
+ * Main metrics interface for instrumentation.
3894
+ *
3895
+ * @remarks
3896
+ * This interface is designed to be thin and pluggable. Implementations
3897
+ * can delegate to any metrics library:
3898
+ *
3899
+ * - **hot-shots**: StatsD/DogStatsD client
3900
+ * - **dd-trace**: Datadog APM
3901
+ * - **prom-client**: Prometheus
3902
+ * - **opentelemetry-js**: OpenTelemetry
3903
+ *
3904
+ * The `child()` method creates a scoped metrics instance that automatically
3905
+ * includes base labels on all metric operations.
3906
+ *
3907
+ * @see {@link createMockMetrics} for testing.
3908
+ * @see {@link noopMetrics} for a no-op implementation.
3909
+ *
3910
+ * @example
3911
+ * ```typescript
3912
+ * // Create a scoped metrics instance
3913
+ * const appMetrics = metrics.child({
3914
+ * service: 'app-kit',
3915
+ * version: '1.0.0',
3916
+ * })
3917
+ *
3918
+ * // All metrics include service + version labels
3919
+ * appMetrics.counter('transfers.initiated').inc({ chain: 'ethereum' })
2341
3920
  * ```
2342
3921
  */
2343
- declare class Actionable<AllActions> {
2344
- private readonly handlers;
2345
- private readonly wildcard;
3922
+ interface Metrics {
2346
3923
  /**
2347
- * Register a handler for a specific action.
3924
+ * Get or create a counter metric by name.
2348
3925
  *
2349
- * @param action - The specific action key to listen for.
2350
- * @param handler - The callback function to execute when the action occurs.
3926
+ * @param name - The metric name (e.g., 'http.requests.total').
3927
+ * @returns A Counter instance for the given name.
2351
3928
  *
2352
3929
  * @example
2353
3930
  * ```typescript
2354
- * const events = new Actionable<{ dataReceived: string }>();
2355
- *
2356
- * events.on('dataReceived', (data) => {
2357
- * console.log(`Received: ${data}`);
2358
- * });
3931
+ * const counter = metrics.counter('requests.total')
3932
+ * counter.inc({ method: 'GET' })
2359
3933
  * ```
2360
3934
  */
2361
- on<Action extends keyof AllActions>(action: Action, handler: (payload: AllActions[Action]) => void): void;
3935
+ counter(name: string): Counter;
2362
3936
  /**
2363
- * Register a handler for all actions using the wildcard '*'.
3937
+ * Get or create a histogram metric by name.
2364
3938
  *
2365
- * @param action - The wildcard '*' signifying interest in all actions.
2366
- * @param handler - The callback function to execute for any action.
3939
+ * @param name - The metric name (e.g., 'http.request.duration').
3940
+ * @returns A Histogram instance for the given name.
2367
3941
  *
2368
3942
  * @example
2369
3943
  * ```typescript
2370
- * const events = new Actionable<{ started: boolean; completed: string }>();
2371
- *
2372
- * events.on('*', (payload) => {
2373
- * console.log('Action occurred:', payload);
2374
- * });
3944
+ * const histogram = metrics.histogram('request.duration')
3945
+ * histogram.observe({ status: 200 }, 42.5)
2375
3946
  * ```
2376
3947
  */
2377
- on(action: '*', handler: (payload: AllActions[keyof AllActions]) => void): void;
3948
+ histogram(name: string): Histogram;
2378
3949
  /**
2379
- * Remove a previously registered handler for a specific action.
3950
+ * Get or create a timer metric by name.
2380
3951
  *
2381
- * @param action - The specific action key to unregister from.
2382
- * @param handler - The callback function to remove.
3952
+ * @param name - The metric name (e.g., 'db.query.duration').
3953
+ * @returns A Timer instance for the given name.
2383
3954
  *
2384
3955
  * @example
2385
3956
  * ```typescript
2386
- * const events = new Actionable<{ dataReceived: string }>();
2387
- *
2388
- * const handler = (data: string) => console.log(data);
2389
- * events.on('dataReceived', handler);
2390
- *
2391
- * // Later, to remove the handler:
2392
- * events.off('dataReceived', handler);
3957
+ * const stop = metrics.timer('db.query').start()
3958
+ * await query()
3959
+ * stop()
2393
3960
  * ```
2394
3961
  */
2395
- off<Action extends keyof AllActions>(action: Action, handler: (payload: AllActions[Action]) => void): void;
3962
+ timer(name: string): Timer;
2396
3963
  /**
2397
- * Remove a previously registered wildcard handler.
3964
+ * Create a child metrics instance with scoped labels.
2398
3965
  *
2399
- * @param action - The wildcard '*' signifying removal from all actions.
2400
- * @param handler - The callback function to remove.
3966
+ * @param labels - The base labels to include on all metric operations.
3967
+ * @returns A new Metrics instance with merged labels.
3968
+ *
3969
+ * @remarks
3970
+ * Labels from the child are merged with any labels passed to individual
3971
+ * metric operations. Call-site labels take precedence for the same key.
2401
3972
  *
2402
3973
  * @example
2403
3974
  * ```typescript
2404
- * const events = new Actionable<{ started: boolean; completed: string }>();
3975
+ * const scoped = metrics.child({ chain: 'Ethereum' })
3976
+ * scoped.counter('transfers').inc({ status: 'success' })
3977
+ * // Labels: { chain: 'Ethereum', status: 'success' }
3978
+ * ```
3979
+ */
3980
+ child(labels: MetricLabels): Metrics;
3981
+ }
3982
+
3983
+ /**
3984
+ * Core type definitions for the runtime package.
3985
+ *
3986
+ * @remarks
3987
+ * This module defines the foundational types used throughout the SDK:
3988
+ *
3989
+ * - {@link Runtime} - Complete runtime with all services (clock, logger, metrics, events)
3990
+ * - {@link ExecutionContext} - Context for middleware with observability surface
3991
+ * - {@link Clock}, {@link Tags}, {@link Event} - Supporting types
3992
+ *
3993
+ * **Naming Convention**
3994
+ *
3995
+ * | Input Type | Resolved Type | Description |
3996
+ * |------------|---------------|-------------|
3997
+ * | `Partial<Runtime>` | `Runtime` | Runtime services |
3998
+ * | `OperationMeta` | `OperationContext` | WHAT - operation target |
3999
+ * | `InvocationMeta` | `InvocationContext` | WHO/HOW - call chain |
4000
+ *
4001
+ * @packageDocumentation
4002
+ */
4003
+
4004
+ /**
4005
+ * Clock interface for time operations.
4006
+ *
4007
+ * @remarks
4008
+ * Abstracting time allows tests to control timing without real delays.
4009
+ * Production code uses {@link defaultClock}, tests use mock implementations.
4010
+ *
4011
+ * @example
4012
+ * ```typescript
4013
+ * import { defaultClock, type Clock } from '@core/runtime'
4014
+ *
4015
+ * const start = defaultClock.now()
4016
+ * // ... do work ...
4017
+ * const elapsed = defaultClock.since(start)
4018
+ * ```
4019
+ */
4020
+ interface Clock {
4021
+ /**
4022
+ * Return the current timestamp in milliseconds since Unix epoch.
2405
4023
  *
2406
- * const globalHandler = (payload: any) => console.log(payload);
2407
- * events.on('*', globalHandler);
4024
+ * @returns Current time in milliseconds.
4025
+ */
4026
+ now(): number;
4027
+ /**
4028
+ * Calculate elapsed time since a given timestamp.
2408
4029
  *
2409
- * // Later, to remove the handler:
2410
- * events.off('*', globalHandler);
2411
- * ```
4030
+ * @param start - The start timestamp in milliseconds.
4031
+ * @returns Elapsed time in milliseconds (`now() - start`).
2412
4032
  */
2413
- off(action: '*', handler: (payload: AllActions[keyof AllActions]) => void): void;
4033
+ since: (start: number) => number;
4034
+ }
4035
+ /**
4036
+ * Contextual metadata tags for logging and metrics.
4037
+ *
4038
+ * @remarks
4039
+ * Tags are key-value pairs attached to log entries and metrics.
4040
+ * Values can be primitives or undefined (undefined values are filtered out).
4041
+ *
4042
+ * Common tags include:
4043
+ * - `opId` - Operation identifier for correlation
4044
+ * - `chain` - Blockchain network name
4045
+ * - `phase` - Current pipeline phase
4046
+ *
4047
+ * @example
4048
+ * ```typescript
4049
+ * import type { Tags } from '@core/runtime'
4050
+ *
4051
+ * const tags: Tags = {
4052
+ * opId: 'op-abc123',
4053
+ * chain: 'Ethereum',
4054
+ * phase: 'validate',
4055
+ * optional: undefined, // Will be filtered out
4056
+ * }
4057
+ * ```
4058
+ */
4059
+ type Tags = Record<string, string | number | boolean | undefined>;
4060
+ /**
4061
+ * A structured event emitted by the runtime.
4062
+ *
4063
+ * @remarks
4064
+ * Events provide a standardized way to capture lifecycle moments,
4065
+ * actions, and state changes throughout the SDK.
4066
+ */
4067
+ interface Event {
4068
+ /** The event name/identifier. */
4069
+ name: string;
4070
+ /** Log level for the event. */
4071
+ level?: 'debug' | 'info' | 'warn' | 'error';
4072
+ /** Timestamp (epoch ms) when the event occurred. */
4073
+ at?: number;
4074
+ /** Contextual tags for filtering/categorization. */
4075
+ tags?: Tags;
4076
+ /** Arbitrary payload data associated with the event. */
4077
+ data?: unknown;
4078
+ }
4079
+ /**
4080
+ * Complete runtime with all services guaranteed present.
4081
+ *
4082
+ * @remarks
4083
+ * The runtime is the container for all cross-cutting concerns: logging,
4084
+ * timing, events, and metrics. All services are guaranteed to be available.
4085
+ *
4086
+ * **Creating a Runtime**
4087
+ *
4088
+ * Use {@link createRuntime} to create a fully-configured runtime:
4089
+ * ```typescript
4090
+ * import { createRuntime } from '@core/runtime'
4091
+ *
4092
+ * const runtime = createRuntime()
4093
+ * runtime.logger.info('Hello')
4094
+ * runtime.metrics.counter('requests').inc()
4095
+ * ```
4096
+ *
4097
+ * **Partial Runtime Input**
4098
+ *
4099
+ * When accepting runtime configuration as input, use `Partial<Runtime>`:
4100
+ * ```typescript
4101
+ * function myFunction(options: { runtime?: Partial<Runtime> }) {
4102
+ * const runtime = createRuntime(options.runtime)
4103
+ * // ...
4104
+ * }
4105
+ * ```
4106
+ *
4107
+ * @example
4108
+ * ```typescript
4109
+ * import { createRuntime, type Runtime } from '@core/runtime'
4110
+ *
4111
+ * const runtime: Runtime = createRuntime()
4112
+ *
4113
+ * // All services are guaranteed present
4114
+ * runtime.logger.info('Processing request')
4115
+ * runtime.metrics.counter('requests').inc()
4116
+ * runtime.events.emit({ name: 'request.received' })
4117
+ * const now = runtime.clock.now()
4118
+ * ```
4119
+ */
4120
+ interface Runtime {
2414
4121
  /**
2415
- * Dispatch an action with its payload to all registered handlers.
4122
+ * Clock for time operations.
2416
4123
  *
2417
- * This method notifies both:
2418
- * - Handlers registered specifically for this action
2419
- * - Wildcard handlers registered for all actions
4124
+ * @remarks
4125
+ * Provides the current timestamp. Use {@link defaultClock} for production
4126
+ * or custom clocks for deterministic testing.
4127
+ */
4128
+ clock: Clock;
4129
+ /**
4130
+ * Logger for structured logging.
2420
4131
  *
2421
- * @param action - The action key identifying the event type.
2422
- * @param payload - The data associated with the action.
4132
+ * @remarks
4133
+ * Provides debug, info, warn, error methods with structured data support.
4134
+ */
4135
+ logger: Logger;
4136
+ /**
4137
+ * Metrics collector for observability.
2423
4138
  *
2424
- * @example
2425
- * ```typescript
2426
- * type Actions = {
2427
- * transferStarted: { amount: string; destination: string };
2428
- * transferComplete: { txHash: string };
2429
- * };
4139
+ * @remarks
4140
+ * Provides counters, histograms, and timers for application metrics.
4141
+ */
4142
+ metrics: Metrics;
4143
+ /**
4144
+ * Event bus for pub/sub events.
2430
4145
  *
2431
- * const events = new Actionable<Actions>();
4146
+ * @remarks
4147
+ * Enables decoupled event emission and subscription across the SDK.
4148
+ */
4149
+ events: EventBus;
4150
+ }
4151
+ /**
4152
+ * A component in the call chain.
4153
+ *
4154
+ * @remarks
4155
+ * Each caller identifies itself with a type (app, kit, provider, adapter)
4156
+ * and a name/version. This enables proper attribution in logs, metrics, and traces.
4157
+ *
4158
+ * @example
4159
+ * ```typescript
4160
+ * import type { Caller } from '@core/runtime'
4161
+ *
4162
+ * const appCaller: Caller = { type: 'app', name: 'MyDApp', version: '1.0.0' }
4163
+ * const kitCaller: Caller = { type: 'kit', name: 'BridgeKit', version: '2.0.0' }
4164
+ * ```
4165
+ */
4166
+ interface Caller {
4167
+ /**
4168
+ * Type of component in the call hierarchy.
2432
4169
  *
2433
- * // Dispatch an event
2434
- * events.dispatch('transferStarted', {
2435
- * amount: '100',
2436
- * destination: '0xABC123'
2437
- * });
2438
- * ```
4170
+ * @remarks
4171
+ * Common types: `app`, `kit`, `provider`, `adapter`
2439
4172
  */
2440
- dispatch<K extends keyof AllActions>(action: K, payload: AllActions[K]): void;
4173
+ readonly type: string;
4174
+ /** Name of the component (e.g., 'BridgeKit', 'cctp-v2'). */
4175
+ readonly name: string;
4176
+ /** Version of the component (e.g., '1.0.0'). */
4177
+ readonly version?: string | undefined;
4178
+ }
4179
+ /**
4180
+ * User input for invocation metadata.
4181
+ *
4182
+ * @remarks
4183
+ * Defines **WHO** called and **HOW** to observe: trace correlation, runtime override,
4184
+ * and caller chain. This is the user-facing input type, resolved to `InvocationContext`.
4185
+ *
4186
+ * Passed as the optional invocation argument to actions and primitives.
4187
+ *
4188
+ * @example
4189
+ * ```typescript
4190
+ * import type { InvocationMeta } from '@core/runtime'
4191
+ *
4192
+ * // Minimal - just traceId
4193
+ * const meta: InvocationMeta = { traceId: 'abc-123' }
4194
+ *
4195
+ * // Full - with runtime override and caller chain
4196
+ * const meta: InvocationMeta = {
4197
+ * traceId: 'abc-123',
4198
+ * runtime: myRuntime,
4199
+ * callers: [
4200
+ * { type: 'app', name: 'MyDApp', version: '1.0.0' },
4201
+ * ],
4202
+ * }
4203
+ * ```
4204
+ */
4205
+ interface InvocationMeta {
4206
+ /**
4207
+ * Trace ID for distributed tracing correlation.
4208
+ *
4209
+ * @remarks
4210
+ * If not provided, generated automatically.
4211
+ */
4212
+ readonly traceId?: string | undefined;
4213
+ /**
4214
+ * Runtime override (complete replacement).
4215
+ *
4216
+ * @remarks
4217
+ * When provided, this runtime completely replaces the default runtime.
4218
+ * Must be a complete Runtime instance (e.g., from `createRuntime()`).
4219
+ * If not provided, the default runtime is used.
4220
+ */
4221
+ readonly runtime?: Runtime | undefined;
4222
+ /**
4223
+ * Token registry override (complete replacement).
4224
+ *
4225
+ * @remarks
4226
+ * When provided, this registry completely replaces the default token registry.
4227
+ * Enables kits to pass their token registry to adapters.
4228
+ * If not provided, the default token registry is used.
4229
+ */
4230
+ readonly tokens?: TokenRegistry | undefined;
4231
+ /**
4232
+ * Call chain - each caller appends itself.
4233
+ *
4234
+ * @remarks
4235
+ * Ordered from outermost (first) to innermost (last).
4236
+ * Example: [app, kit, provider]
4237
+ */
4238
+ readonly callers?: readonly Caller[] | undefined;
2441
4239
  }
2442
4240
 
2443
4241
  /**
@@ -2569,6 +4367,14 @@ TChainDefinition extends ChainDefinition = ChainDefinition> {
2569
4367
  token: 'USDC';
2570
4368
  /** Bridge configuration (e.g., fast burn settings) */
2571
4369
  config: BridgeConfig;
4370
+ /**
4371
+ * Optional invocation metadata for tracing and correlation.
4372
+ *
4373
+ * When provided, the `traceId` is used to correlate all events emitted during
4374
+ * the bridge operation. If not provided, an OpenTelemetry-compatible traceId
4375
+ * will be auto-generated.
4376
+ */
4377
+ invocationMeta?: InvocationMeta;
2572
4378
  }
2573
4379
  /**
2574
4380
  * A step in the bridge process.
@@ -2598,6 +4404,15 @@ interface BridgeStep {
2598
4404
  explorerUrl?: string;
2599
4405
  /** Optional data for the step */
2600
4406
  data?: unknown;
4407
+ /**
4408
+ * Whether this step was executed via Circle's Forwarder (relay service).
4409
+ * Only applicable for mint steps.
4410
+ *
4411
+ * - `true`: The mint was handled by Circle's Orbit relayer
4412
+ * - `false`: The user submitted the mint transaction directly
4413
+ * - `undefined`: Not applicable (non-mint steps)
4414
+ */
4415
+ forwarded?: boolean;
2601
4416
  /** Optional human-readable error message */
2602
4417
  errorMessage?: string;
2603
4418
  /** Optional raw error object (can be Viem/Ethers/Chain error) */
@@ -2649,6 +4464,13 @@ interface BridgeResult {
2649
4464
  * result for retry operations to maintain consistency with the original burn.
2650
4465
  */
2651
4466
  recipientAddress?: string;
4467
+ /**
4468
+ * Whether Circle's Forwarder was used for this bridge operation.
4469
+ *
4470
+ * When true, the mint transaction was handled by Circle's Orbit relayer
4471
+ * instead of requiring the user to submit it manually.
4472
+ */
4473
+ useForwarder?: boolean;
2652
4474
  };
2653
4475
  /** Array of steps that were executed during the bridge process */
2654
4476
  steps: BridgeStep[];
@@ -2706,8 +4528,8 @@ interface EstimateResult {
2706
4528
  }[];
2707
4529
  /** Array of protocol and service fees for the transfer */
2708
4530
  fees: {
2709
- /** The type of fee - either from the bridge kit or the underlying provider */
2710
- type: 'kit' | 'provider';
4531
+ /** The type of fee - from the bridge kit, provider (CCTP), or forwarder (Circle Orbit relayer) */
4532
+ type: 'kit' | 'provider' | 'forwarder';
2711
4533
  /** The token in which the fee is charged (currently only USDC) */
2712
4534
  token: 'USDC';
2713
4535
  /** The fee amount (as a string to avoid precision issues) */
@@ -2857,19 +4679,38 @@ interface CustomFee {
2857
4679
  * have access to both the source and destination chain information needed for
2858
4680
  * validation and execution.
2859
4681
  *
4682
+ * The destination adapter (`to`) is optional to support forwarder-only destinations
4683
+ * where Circle's Orbit relayer handles the mint transaction without requiring a
4684
+ * destination adapter. When `to` is undefined, the retry operation relies on
4685
+ * IRIS API confirmation instead of on-chain transaction confirmation.
4686
+ *
2860
4687
  * @example
2861
4688
  * ```typescript
4689
+ * // Standard retry with both adapters
2862
4690
  * const retryContext: RetryContext = {
2863
- * from: { adapter: sourceAdapter, chain: 'Ethereum' },
2864
- * to: { adapter: destAdapter, chain: 'Base' }
4691
+ * from: sourceAdapter,
4692
+ * to: destAdapter
4693
+ * }
4694
+ *
4695
+ * // Forwarder-only retry (no destination adapter)
4696
+ * const forwarderRetryContext: RetryContext = {
4697
+ * from: sourceAdapter,
4698
+ * to: undefined // Forwarder handles mint
2865
4699
  * }
2866
4700
  * ```
2867
4701
  */
2868
4702
  interface RetryContext<TFromAdapterCapabilities extends AdapterCapabilities = AdapterCapabilities, TToAdapterCapabilities extends AdapterCapabilities = AdapterCapabilities> {
2869
4703
  /** The source adapter context for the retry operation */
2870
4704
  from: Adapter<TFromAdapterCapabilities>;
2871
- /** The destination adapter context for the retry operation */
2872
- to: Adapter<TToAdapterCapabilities>;
4705
+ /**
4706
+ * The destination adapter context for the retry operation.
4707
+ *
4708
+ * Optional for forwarder-only destinations where Circle's Orbit relayer
4709
+ * handles the mint transaction. When undefined, the retry operation relies
4710
+ * on IRIS API confirmation (`forwardState === 'CONFIRMED'`) instead of
4711
+ * on-chain transaction confirmation via the adapter.
4712
+ */
4713
+ to?: Adapter<TToAdapterCapabilities>;
2873
4714
  }
2874
4715
  /**
2875
4716
  * Result of analyzing bridge steps to determine retry feasibility and continuation point.
@@ -2966,6 +4807,7 @@ declare abstract class BridgingProvider<TProviderActions = Record<string, unknow
2966
4807
  * @param source - The source chain definition
2967
4808
  * @param destination - The destination chain definition
2968
4809
  * @param token - The token to transfer (currently only USDC is supported)
4810
+ * @param useForwarder - When `true`, also checks that the route supports forwarding
2969
4811
  * @returns `true` if the provider supports this route, `false` otherwise
2970
4812
  *
2971
4813
  * @example
@@ -2977,7 +4819,7 @@ declare abstract class BridgingProvider<TProviderActions = Record<string, unknow
2977
4819
  * }
2978
4820
  * ```
2979
4821
  */
2980
- abstract supportsRoute(source: ChainDefinition, destination: ChainDefinition, token: 'USDC'): boolean;
4822
+ abstract supportsRoute(source: ChainDefinition, destination: ChainDefinition, token: 'USDC', useForwarder?: boolean): boolean;
2981
4823
  /**
2982
4824
  * Executes a cross-chain bridge operation between the specified source and destination chains.
2983
4825
  *
@@ -3098,6 +4940,8 @@ declare abstract class BridgingProvider<TProviderActions = Record<string, unknow
3098
4940
  *
3099
4941
  * @param result - The failed bridge result to retry
3100
4942
  * @param context - The retry context containing source and destination adapter contexts
4943
+ * @param invocationMeta - Optional invocation metadata for tracing and correlation.
4944
+ * When provided, enables custom traceId, runtime overrides, and caller chain tracking.
3101
4945
  * @returns Promise resolving to the retry bridge result
3102
4946
  * @throws {Error} If retry is not supported by this provider
3103
4947
  *
@@ -3113,7 +4957,7 @@ declare abstract class BridgingProvider<TProviderActions = Record<string, unknow
3113
4957
  * }
3114
4958
  * ```
3115
4959
  */
3116
- retry<TFromAdapterCapabilities extends AdapterCapabilities, TToAdapterCapabilities extends AdapterCapabilities>(result: BridgeResult, context: RetryContext<TFromAdapterCapabilities, TToAdapterCapabilities>): Promise<BridgeResult>;
4960
+ retry<TFromAdapterCapabilities extends AdapterCapabilities, TToAdapterCapabilities extends AdapterCapabilities>(result: BridgeResult, context: RetryContext<TFromAdapterCapabilities, TToAdapterCapabilities>, invocationMeta?: InvocationMeta): Promise<BridgeResult>;
3117
4961
  /**
3118
4962
  * Analyzes bridge steps to determine retry feasibility and continuation point.
3119
4963
  *
@@ -3286,6 +5130,21 @@ interface AttestationMessage {
3286
5130
  * The delay reason if the attestation is delayed.
3287
5131
  */
3288
5132
  delayReason?: string | null;
5133
+ /**
5134
+ * Forwarding state from Circle's relayer.
5135
+ * Only present when useForwarder was enabled during burn.
5136
+ * @description
5137
+ * - 'PENDING': Relayer is processing the mint
5138
+ * - 'CONFIRMED': Relayer has submitted the mint tx (waiting for finality)
5139
+ * - 'COMPLETE': Relayer has successfully completed the mint tx with finality
5140
+ * - 'FAILED': Relayer failed to process the mint (user may need manual mint)
5141
+ */
5142
+ forwardState?: 'PENDING' | 'CONFIRMED' | 'COMPLETE' | 'FAILED' | null;
5143
+ /**
5144
+ * Transaction hash of the mint tx submitted by Circle's relayer.
5145
+ * Only present when forwardState is 'CONFIRMED' or 'COMPLETE'.
5146
+ */
5147
+ forwardTxHash?: string | null;
3289
5148
  }
3290
5149
 
3291
5150
  /**
@@ -3319,6 +5178,66 @@ type BridgeFetchAttestationStep = BridgeStep &
3319
5178
  data?: undefined;
3320
5179
  });
3321
5180
 
5181
+ /**
5182
+ * Forwarder-only destination for CCTP v2 transfers without an adapter.
5183
+ *
5184
+ * Used when Circle's Forwarder handles the mint transaction and no destination
5185
+ * adapter is available. Requires both `useForwarder: true` and a `recipientAddress`.
5186
+ *
5187
+ * When using this destination type:
5188
+ * - The mint step completes when the IRIS API confirms `forwardState === 'CONFIRMED'`
5189
+ * - No on-chain transaction confirmation is performed (no adapter available)
5190
+ * - The mint step's `data` field will be undefined (no transaction receipt)
5191
+ */
5192
+ interface CCTPV2ForwarderOnlyDestination<TChainDefinition extends ChainDefinitionWithCCTPv2 = ChainDefinitionWithCCTPv2> {
5193
+ /** The destination chain where USDC will be minted */
5194
+ chain: TChainDefinition;
5195
+ /** The recipient address that will receive the minted USDC */
5196
+ recipientAddress: string;
5197
+ /** Must be true for forwarder-only destinations */
5198
+ useForwarder: true;
5199
+ /** The resolved address (same as recipientAddress for forwarder-only) */
5200
+ address?: string;
5201
+ }
5202
+ /**
5203
+ * Destination context for CCTP v2 transfers with optional forwarder support.
5204
+ *
5205
+ * Supports two modes:
5206
+ * 1. **With adapter**: Standard destination where user or forwarder submits mint transaction.
5207
+ * The adapter is used for chain validation and transaction confirmation.
5208
+ * 2. **Forwarder-only**: No adapter provided. Circle's Orbit relayer handles the mint
5209
+ * and confirmation is based on IRIS API response only.
5210
+ *
5211
+ * @example
5212
+ * ```typescript
5213
+ * // With adapter (standard or forwarder-assisted)
5214
+ * const dest1: CCTPV2DestinationContext = {
5215
+ * adapter,
5216
+ * chain: BaseSepolia,
5217
+ * address: '0x...',
5218
+ * useForwarder: true // optional
5219
+ * }
5220
+ *
5221
+ * // Forwarder-only (no adapter)
5222
+ * const dest2: CCTPV2DestinationContext = {
5223
+ * chain: BaseSepolia,
5224
+ * recipientAddress: '0x...',
5225
+ * useForwarder: true
5226
+ * }
5227
+ * ```
5228
+ */
5229
+ type CCTPV2DestinationContext<TAdapterCapabilities extends AdapterCapabilities = AdapterCapabilities, TChainDefinition extends ChainDefinitionWithCCTPv2 = ChainDefinitionWithCCTPv2> = (DestinationWalletContext<TAdapterCapabilities, TChainDefinition> & {
5230
+ /**
5231
+ * Enable Circle's Forwarder to handle the mint transaction.
5232
+ *
5233
+ * When true, Circle's Orbit relayer will fetch the attestation and submit
5234
+ * the mint transaction on the user's behalf. The relay fee is deducted from
5235
+ * the minted USDC at mint time.
5236
+ *
5237
+ * @defaultValue false
5238
+ */
5239
+ useForwarder?: boolean;
5240
+ }) | CCTPV2ForwarderOnlyDestination<TChainDefinition>;
3322
5241
  /**
3323
5242
  * CCTPv2 bridge parameters.
3324
5243
  *
@@ -3332,31 +5251,56 @@ type CCTPV2BridgeParams<TFromAdapterCapabilities extends AdapterCapabilities = A
3332
5251
  */
3333
5252
  source: WalletContext<TFromAdapterCapabilities, ChainDefinitionWithCCTPv2>;
3334
5253
  /**
3335
- * The destination wallet context containing the chain definition and wallet address.
5254
+ * The destination wallet context containing the chain definition, wallet address,
5255
+ * and optional forwarder configuration.
3336
5256
  */
3337
- destination: DestinationWalletContext<TToAdapterCapabilities, ChainDefinitionWithCCTPv2>;
5257
+ destination: CCTPV2DestinationContext<TToAdapterCapabilities>;
3338
5258
  /**
3339
5259
  * The bridge configuration for the CCTPv2 transfer.
3340
5260
  */
3341
5261
  config: BridgeConfig;
3342
5262
  };
5263
+ /**
5264
+ * Base payload structure for CCTP v2 action events.
5265
+ *
5266
+ * @remarks
5267
+ * All CCTP v2 actions share these common fields for protocol identification
5268
+ * and tracing support.
5269
+ */
5270
+ interface CCTPV2ActionBase {
5271
+ /** The protocol identifier */
5272
+ protocol: 'cctp';
5273
+ /** The protocol version */
5274
+ version: 'v2';
5275
+ /**
5276
+ * Optional trace ID for end-to-end correlation.
5277
+ *
5278
+ * When provided, this ID links all events in a single bridge operation, enabling
5279
+ * integration with distributed tracing systems (OpenTelemetry, Datadog, etc.).
5280
+ *
5281
+ * Format: 32-character hex string or custom string (user-provided via invocation context).
5282
+ */
5283
+ traceId?: string;
5284
+ }
3343
5285
  /**
3344
5286
  * Action types supported by the CCTP v2 provider.
3345
5287
  *
3346
5288
  * @remarks
3347
5289
  * This interface defines the structure of actions that can be dispatched during
3348
- * CCTP v2 transfer operations. Each action includes protocol metadata and a
3349
- * `values` field containing the full bridge step with transaction details.
5290
+ * CCTP v2 transfer operations. Each action includes protocol metadata, an optional
5291
+ * `traceId` for correlation, and a `values` field containing the full bridge step
5292
+ * with transaction details.
3350
5293
  *
3351
5294
  * All transaction-based steps (approve, burn, mint) include `txHash` and
3352
5295
  * `explorerUrl` fields for direct links to block explorers.
3353
5296
  *
3354
5297
  * @example
3355
5298
  * ```typescript
3356
- * // Action structure received by event listeners
5299
+ * // Action structure received by event listeners (with traceId)
3357
5300
  * const burnAction: CCTPV2Actions['burn'] = {
3358
5301
  * protocol: 'cctp',
3359
5302
  * version: 'v2',
5303
+ * traceId: '550e8400-e29b-41d4-a716-446655440000', // optional
3360
5304
  * method: 'burn',
3361
5305
  * values: {
3362
5306
  * name: 'burn',
@@ -3373,9 +5317,7 @@ interface CCTPV2Actions {
3373
5317
  * Approval action for CCTP v2 transfers.
3374
5318
  * Used to approve token spending before initiating a burn operation.
3375
5319
  */
3376
- approve: {
3377
- protocol: 'cctp';
3378
- version: 'v2';
5320
+ approve: CCTPV2ActionBase & {
3379
5321
  method: 'approve';
3380
5322
  values: BridgeStep;
3381
5323
  };
@@ -3383,9 +5325,7 @@ interface CCTPV2Actions {
3383
5325
  * Burn action for CCTP v2 transfers.
3384
5326
  * Used to burn tokens on the source chain to initiate a cross-chain transfer.
3385
5327
  */
3386
- burn: {
3387
- protocol: 'cctp';
3388
- version: 'v2';
5328
+ burn: CCTPV2ActionBase & {
3389
5329
  method: 'burn';
3390
5330
  values: BridgeStep;
3391
5331
  };
@@ -3393,9 +5333,7 @@ interface CCTPV2Actions {
3393
5333
  * Attestation action for CCTP v2 transfers.
3394
5334
  * Used to fetch the attestation message for the source wallet.
3395
5335
  */
3396
- fetchAttestation: {
3397
- protocol: 'cctp';
3398
- version: 'v2';
5336
+ fetchAttestation: CCTPV2ActionBase & {
3399
5337
  method: 'fetchAttestation';
3400
5338
  values: BridgeFetchAttestationStep;
3401
5339
  };
@@ -3403,9 +5341,7 @@ interface CCTPV2Actions {
3403
5341
  * Mint action for CCTP v2 transfers.
3404
5342
  * Used to mint tokens on the destination chain after a successful burn.
3405
5343
  */
3406
- mint: {
3407
- protocol: 'cctp';
3408
- version: 'v2';
5344
+ mint: CCTPV2ActionBase & {
3409
5345
  method: 'mint';
3410
5346
  values: BridgeStep;
3411
5347
  };
@@ -3413,9 +5349,7 @@ interface CCTPV2Actions {
3413
5349
  * Re-attestation action for CCTP v2 transfers.
3414
5350
  * Used to request a fresh attestation when the original has expired.
3415
5351
  */
3416
- reAttest: {
3417
- protocol: 'cctp';
3418
- version: 'v2';
5352
+ reAttest: CCTPV2ActionBase & {
3419
5353
  method: 'reAttest';
3420
5354
  values: BridgeFetchAttestationStep;
3421
5355
  };
@@ -3607,7 +5541,7 @@ declare class CCTPV2BridgingProvider extends BridgingProvider<CCTPV2Actions> imp
3607
5541
  * const retryResult = await provider.retry(failedResult, retryContext)
3608
5542
  * ```
3609
5543
  */
3610
- retry<TFromAdapterCapabilities extends AdapterCapabilities, TToAdapterCapabilities extends AdapterCapabilities>(result: BridgeResult, context: RetryContext<TFromAdapterCapabilities, TToAdapterCapabilities>): Promise<BridgeResult>;
5544
+ retry<TFromAdapterCapabilities extends AdapterCapabilities, TToAdapterCapabilities extends AdapterCapabilities>(result: BridgeResult, context: RetryContext<TFromAdapterCapabilities, TToAdapterCapabilities>, invocationMeta?: InvocationMeta): Promise<BridgeResult>;
3611
5545
  /**
3612
5546
  * Estimate the cost and fees for a CCTP v2 cross-chain bridge operation.
3613
5547
  *
@@ -3616,7 +5550,9 @@ declare class CCTPV2BridgingProvider extends BridgingProvider<CCTPV2Actions> imp
3616
5550
  * chains, as well as any applicable protocol fees.
3617
5551
  *
3618
5552
  * @param params - The bridge parameters containing source, destination, amount, and optional config.
5553
+ * Supports optional `invocation` for tracing and correlation.
3619
5554
  * @returns Promise resolving to detailed cost breakdown including:
5555
+ * - `traceId`: Correlation ID from invocation context (if provided)
3620
5556
  * - `gasFees`: Array of gas estimates for each step (Approve, Burn, Mint)
3621
5557
  * - Gas amounts in native token smallest units (wei for ETH, lamports for SOL, etc.)
3622
5558
  * - `fees`: Array of protocol and kit fees
@@ -3627,16 +5563,29 @@ declare class CCTPV2BridgingProvider extends BridgingProvider<CCTPV2Actions> imp
3627
5563
  *
3628
5564
  * @example
3629
5565
  * ```typescript
5566
+ * import {
5567
+ * resolveInvocationContext,
5568
+ * createRuntime,
5569
+ * } from '\@core/runtime'
5570
+ * import { createTokenRegistry } from '\@core/tokens'
5571
+ *
5572
+ * const defaults = {
5573
+ * runtime: createRuntime(),
5574
+ * tokens: createTokenRegistry(),
5575
+ * }
5576
+ *
3630
5577
  * const estimate = await provider.estimate({
3631
5578
  * source: { adapter: sourceAdapter, chain: 'Ethereum' },
3632
5579
  * destination: { adapter: destAdapter, chain: 'Base' },
3633
5580
  * amount: '10.50',
3634
- * token: 'USDC'
5581
+ * token: 'USDC',
5582
+ * invocationMeta: {
5583
+ * callers: [{ type: 'app', name: 'MyDApp', version: '1.0.0' }],
5584
+ * },
3635
5585
  * })
3636
5586
  *
3637
- * console.log('Total cost:', estimate.totalCost)
3638
- * console.log('Source gas:', estimate.sourceGas)
3639
- * console.log('Destination gas:', estimate.destinationGas)
5587
+ * console.log('Total gas fees:', estimate.gasFees.length)
5588
+ * console.log('Protocol fees:', estimate.fees.length)
3640
5589
  * ```
3641
5590
  */
3642
5591
  estimate<TFromAdapterCapabilities extends AdapterCapabilities, TToAdapterCapabilities extends AdapterCapabilities>(params: CCTPV2BridgeParams<TFromAdapterCapabilities, TToAdapterCapabilities>): Promise<EstimateResult>;
@@ -3655,7 +5604,8 @@ declare class CCTPV2BridgingProvider extends BridgingProvider<CCTPV2Actions> imp
3655
5604
  */
3656
5605
  private addGasFeeEstimate;
3657
5606
  /**
3658
- * Helper method to add provider fee estimates to the result.
5607
+ * Helper method to add provider and forwarder fee estimates to the result.
5608
+ * Only adds fee entries when the fee is greater than 0.
3659
5609
  */
3660
5610
  private addProviderFeeEstimate;
3661
5611
  /**
@@ -3832,10 +5782,14 @@ declare class CCTPV2BridgingProvider extends BridgingProvider<CCTPV2Actions> imp
3832
5782
  *
3833
5783
  * This method validates that both chains have CCTP v2 contracts deployed and configured.
3834
5784
  * It uses the `isCCTPV2Supported` guard function to check each chain individually.
5785
+ * When `useForwarder` is `true`, additionally checks that the source chain supports
5786
+ * forwarding as source and the destination chain supports forwarding as destination.
3835
5787
  *
3836
5788
  * @param source - The source chain definition to check for CCTP v2 support
3837
5789
  * @param destination - The destination chain definition to check for CCTP v2 support
3838
- * @returns `true` if both chains support CCTP v2, `false` otherwise
5790
+ * @param token - The token to transfer (currently only USDC is supported)
5791
+ * @param useForwarder - When `true`, also checks that the route supports forwarding
5792
+ * @returns `true` if both chains support CCTP v2 (and forwarding if requested), `false` otherwise
3839
5793
  *
3840
5794
  * @example
3841
5795
  * ```typescript
@@ -3844,19 +5798,30 @@ declare class CCTPV2BridgingProvider extends BridgingProvider<CCTPV2Actions> imp
3844
5798
  *
3845
5799
  * if (canTransfer) {
3846
5800
  * console.log('CCTP v2 transfer is supported between these chains')
3847
- * } else {
3848
- * console.log('One or both chains do not support CCTP v2')
5801
+ * }
5802
+ *
5803
+ * const canForward = provider.supportsRoute(Ethereum, Base, 'USDC', true)
5804
+ *
5805
+ * if (canForward) {
5806
+ * console.log('CCTP v2 transfer with forwarding is supported')
3849
5807
  * }
3850
5808
  * ```
3851
5809
  */
3852
- supportsRoute(source: ChainDefinition, destination: ChainDefinition, token: 'USDC'): boolean;
5810
+ supportsRoute(source: ChainDefinition, destination: ChainDefinition, token: 'USDC', useForwarder?: boolean): boolean;
3853
5811
  /**
3854
5812
  * Determines the appropriate maximum fee for a cross-chain bridge operation.
3855
5813
  *
3856
- * For FAST bridge operations, it calculates a dynamic fee based on the bridge amount
3857
- * and fast bridge burn fee, or uses a provided maxFee if specified.
5814
+ * The fee calculation depends on transfer speed and forwarding settings:
3858
5815
  *
3859
- * For SLOW bridge operations, it returns 0 as there are no additional fees.
5816
+ * | Transfer Speed | useForwarder | maxFee provided | Behavior |
5817
+ * |----------------|--------------|-----------------|----------|
5818
+ * | SLOW | false | N/A | maxFee = 0 |
5819
+ * | SLOW | true | No | maxFee = forwardingFee |
5820
+ * | SLOW | true | Yes | maxFee = config.maxFee |
5821
+ * | FAST | false | No | maxFee = calculatedBurnFee |
5822
+ * | FAST | false | Yes | maxFee = config.maxFee |
5823
+ * | FAST | true | No | maxFee = calculatedBurnFee + forwardingFee |
5824
+ * | FAST | true | Yes | maxFee = config.maxFee |
3860
5825
  *
3861
5826
  * @param params - The bridge parameters object containing:
3862
5827
  * - `source`: The source wallet context with chain definition and adapter
@@ -3882,7 +5847,10 @@ declare class CCTPV2BridgingProvider extends BridgingProvider<CCTPV2Actions> imp
3882
5847
  * console.log('Max fee:', maxFee)
3883
5848
  * ```
3884
5849
  */
3885
- getMaxFee<TFromAdapterCapabilities extends AdapterCapabilities, TToAdapterCapabilities extends AdapterCapabilities>(params: BridgeParams<TFromAdapterCapabilities, TToAdapterCapabilities>): Promise<bigint>;
5850
+ getMaxFee<TFromAdapterCapabilities extends AdapterCapabilities, TToAdapterCapabilities extends AdapterCapabilities>(params: BridgeParams<TFromAdapterCapabilities, TToAdapterCapabilities>): Promise<{
5851
+ providerFee: bigint;
5852
+ forwarderFee: bigint;
5853
+ }>;
3886
5854
  /**
3887
5855
  * Prepares a CCTP v2 burn operation to initiate a cross-chain bridge operation.
3888
5856
  *
@@ -3971,6 +5939,29 @@ rawAddress: string,
3971
5939
  /** The USDC mint address (ignored for EVM chains, required base58 address for Solana) */
3972
5940
  mintAddress: string) => Promise<string>;
3973
5941
 
5942
+ /**
5943
+ * Validates and converts a fee value to bigint.
5944
+ *
5945
+ * This utility performs:
5946
+ * 1. Conversion from string or number to bigint
5947
+ * 2. Validation that the value is non-negative
5948
+ *
5949
+ * @param value - The fee value to validate and convert (string or number)
5950
+ * @param fieldName - The name of the field for error messages
5951
+ * @returns The validated fee as a bigint
5952
+ * @throws {KitError} If the value cannot be converted to bigint or is negative
5953
+ *
5954
+ * @example
5955
+ * ```typescript
5956
+ * const fee = validateAndConvertFee('1000000', 'minimumFee')
5957
+ * console.log(fee) // 1000000n
5958
+ *
5959
+ * const fee2 = validateAndConvertFee(500, 'forwardingFee')
5960
+ * console.log(fee2) // 500n
5961
+ * ```
5962
+ */
5963
+ declare function validateAndConvertFee(value: number | string, fieldName: string): bigint;
5964
+
3974
5965
  /** A block number value that can be provided as bigint, number, or string. */
3975
5966
  type BlockNumberInput = bigint | number | string | undefined;
3976
5967
  /**
@@ -4068,5 +6059,5 @@ declare const getBlocksUntilExpiry: (attestation: AttestationMessage, currentBlo
4068
6059
  */
4069
6060
  declare const isMintFailureRelatedToAttestation: (error: unknown) => boolean;
4070
6061
 
4071
- export { CCTPV2BridgingProvider, getBlocksUntilExpiry, getMintRecipientAccount, isAttestationExpired, isMintFailureRelatedToAttestation };
6062
+ export { CCTPV2BridgingProvider, getBlocksUntilExpiry, getMintRecipientAccount, isAttestationExpired, isMintFailureRelatedToAttestation, validateAndConvertFee };
4072
6063
  export type { CCTPV2Config };