/** * Copyright (c) 2026, Circle Internet Group, Inc. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { z } from '/home/runner/_work/stablecoin-kits-private/stablecoin-kits-private/node_modules/zod/dist/types/index.d.ts'; import { Abi } from 'abitype'; import { TransactionInstruction, Signer, AddressLookupTableAccount } from '@solana/web3.js'; /** * @packageDocumentation * @module ChainDefinitions * * This module provides a complete type system for blockchain chain definitions. * It supports both EVM and non‑EVM chains, token configurations, and multiple * versions of the Cross-Chain Transfer Protocol (CCTP). Additionally, utility types * are provided to extract subsets of chains (e.g. chains supporting USDC, EURC, or specific * CCTP versions) from a provided collection. * * All types are fully documented with TSDoc to maximize developer experience. */ /** * Represents basic information about a currency or token. * @category Types * @description Provides the essential properties of a cryptocurrency or token. * @example * ```typescript * const ethCurrency: Currency = { * name: "Ether", * symbol: "ETH", * decimals: 18 * }; * ``` */ interface Currency { /** * The full name of the currency. * @example "Ether", "USDC" */ name: string; /** * The symbol or ticker of the currency. * @example "ETH", "USDC" */ symbol: string; /** * The number of decimal places for the currency. * @description Defines the divisibility of the currency (e.g., 1 ETH = 10^18 wei). * @example 18 for ETH, 6 for USDC */ decimals: number; } /** * Base information that all chain definitions must include. * @category Types * @description Provides the common properties shared by all blockchain definitions. * @example * ```typescript * const baseChain: BaseChainDefinition = { * chain: Blockchain.Ethereum, * name: "Ethereum", * nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 }, * isTestnet: false * }; * ``` */ interface BaseChainDefinition { /** * The blockchain identifier from the {@link Blockchain} enum. */ chain: Blockchain; /** * The display name of the blockchain. * @example "Ethereum", "Solana", "Avalanche" */ name: string; /** * Optional title or alternative name for the blockchain. * @example "Ethereum Mainnet", "Solana Mainnet" */ title?: string; /** * Information about the native currency of the blockchain. */ nativeCurrency: Currency; /** * Indicates whether this is a testnet or mainnet. * @description Used to differentiate between production and testing environments. */ isTestnet: boolean; /** * Template URL for the blockchain explorer to view transactions. * @description URL template with a `\{hash\}` placeholder for transaction hash. * @example "https://etherscan.io/tx/\{hash\}", "https://sepolia.etherscan.io/tx/\{hash\}" */ explorerUrl: string; /** * Default RPC endpoints for connecting to the blockchain network. * @description Array of reliable public RPC endpoints that can be used for read and write operations. * The first endpoint in the array is considered the primary endpoint. * @example ["https://cloudflare-eth.com", "https://ethereum.publicnode.com"] */ rpcEndpoints: readonly string[]; /** * The contract address for EURC. * @description Its presence indicates that EURC is supported. */ eurcAddress: string | null; /** * The contract address for USDC. * @description Its presence indicates that USDC is supported. */ usdcAddress: string | null; /** * The contract address for USDT. * @description Its presence indicates that USDT is supported. */ usdtAddress: string | null; /** * Optional CCTP configuration. * @description If provided, the chain supports CCTP. */ cctp: CCTPConfig | null; /** * Optional kit-specific contract addresses for enhanced chain functionality. * * @description When provided, the chain supports additional kit-specific logic in addition * to standard CCTP. This enables hybrid flows where both standard approve/burn/mint * and enhanced custom features are available. When undefined, the chain uses only * the standard CCTP flow. * * The address format varies by blockchain: * - EVM chains: 40-character hexadecimal with 0x prefix (e.g., "0x1234...") * - Solana: Base58-encoded 32-byte address (e.g., "9WzDX...") * - Other chains: Platform-specific address formats * * @example * ```typescript * // EVM chain with bridge contract * const evmChain: ChainDefinition = { * // ... other properties * kitContracts: { * bridge: "0x1234567890abcdef1234567890abcdef12345678" * } * } * * // Solana chain with bridge contract * const solanaChain: ChainDefinition = { * // ... other properties * kitContracts: { * bridge: "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM" * } * } * ``` */ kitContracts?: KitContracts; /** * Optional Gateway contract configuration for Gateway protocol support. * * @description When provided, the chain supports the Gateway protocol for * cross-chain transfers. Gateway provides an alternative bridging mechanism * with its own set of smart contracts (GatewayWallet and GatewayMinter). * * Use the {@link isGatewayV1Supported} type guard to check if a chain * supports Gateway v1 before accessing these properties. * * @example * ```typescript * // Chain with Gateway v1 support * const chainWithGateway: ChainDefinition = { * // ... other properties * gateway: { * domain: 6, * forwarderSupported: { source: true, destination: true }, * contracts: { * v1: { * wallet: '0x1234567890abcdef1234567890abcdef12345678', * minter: '0xabcdef1234567890abcdef1234567890abcdef12' * } * } * } * } * * // Check Gateway support * if (isGatewayV1Supported(chainWithGateway)) { * console.log('Gateway wallet:', chainWithGateway.gateway.contracts.v1.wallet) * } * ``` * * @see {@link GatewayConfig} for the structure of Gateway configuration. * @see {@link isGatewayV1Supported} for checking Gateway v1 support. */ gateway?: GatewayConfig; } /** * Represents chain definitions for Ethereum Virtual Machine (EVM) compatible blockchains. * @extends BaseChainDefinition * @category Types * @description Adds properties specific to EVM chains. * @example * ```typescript * const ethereum: EVMChainDefinition = { * type: 'evm', * chain: Blockchain.Ethereum, * chainId: 1, * name: 'Ethereum', * title: 'Ethereum Mainnet', * nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, * isTestnet: false * }; * ``` */ interface EVMChainDefinition extends BaseChainDefinition { /** * Discriminator for EVM chains. * @description Used for type narrowing when handling different chain types. */ type: 'evm'; /** * The unique identifier for the blockchain. * @description Standard EVM chain ID as defined in EIP-155. * @example 1 for Ethereum Mainnet, 137 for Polygon. */ chainId: number; } /** * Represents chain definitions for non-EVM blockchains. * @extends BaseChainDefinition * @category Types * @description Contains properties for blockchains that do not use the EVM. * @example * ```typescript * const solana: NonEVMChainDefinition = { * type: 'solana', * chain: Blockchain.Solana, * name: 'Solana', * nativeCurrency: { name: 'Solana', symbol: 'SOL', decimals: 9 }, * isTestnet: false * }; * ``` */ interface NonEVMChainDefinition extends BaseChainDefinition { /** * Discriminator for non-EVM chains. * @description Identifies the specific blockchain platform. */ type: 'algorand' | 'avalanche' | 'solana' | 'aptos' | 'near' | 'stellar' | 'sui' | 'hedera' | 'noble' | 'polkadot'; } /** * The type of chain. * @alias ChainType * @category Types * @description Represents the type of chain. * @example * ```typescript * const chainType: ChainType = 'evm' * ``` */ type ChainType = EVMChainDefinition['type'] | NonEVMChainDefinition['type']; /** * Public chain definition type. * @alias ChainDefinition * @category Types * @description Represents either an EVM-based or non-EVM-based blockchain definition. * This type is used by developers to define chain configurations. * @example * ```typescript * // Standard chain with CCTP support only * const ethereumChain: ChainDefinition = { * type: 'evm', * chain: Blockchain.Ethereum, * chainId: 1, * name: 'Ethereum', * nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, * isTestnet: false, * explorerUrl: 'https://etherscan.io/tx/{hash}', * rpcEndpoints: ['https://eth.example.com'], * eurcAddress: null, * usdcAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', * usdtAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7', * cctp: { * domain: 0, * contracts: { * v2: { * type: 'split', * tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d', * messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64', * confirmations: 65, * fastConfirmations: 2 * } * } * }, * kitContracts: undefined * }; * * // Chain with custom contract support (hybrid flow) * const customChain: ChainDefinition = { * ...ethereumChain, * kitContracts: { * bridge: '0x1234567890abcdef1234567890abcdef12345678' * } * }; * ``` */ type ChainDefinition = EVMChainDefinition | NonEVMChainDefinition; /** * Chain definition with CCTPv2 configuration. * @alias ChainDefinitionWithCCTPv2 * @extends ChainDefinition * @category Types * @description Represents a chain definition that includes CCTPv2 configuration. This is useful for typescript consumers to narrow down the type of chain definition to a chain that supports CCTPv2. * @example * ```typescript * const ethereumWithCCTPv2: ChainDefinitionWithCCTPv2 = { * ...ethereum, * cctp: { * domain: 0, * contracts: { * v2: { * type: 'merged', * contract: '0x123...' * } * } * } * }; * ``` */ type ChainDefinitionWithCCTPv2 = ChainDefinition & { cctp: CCTPConfig & { contracts: { v2: VersionConfig; }; }; usdcAddress: string; }; /** * Chain identifier that can be used in transfer parameters and factory functions. * This can be either: * - A ChainDefinition object * - A Blockchain enum value (e.g., Blockchain.Ethereum) * - A string literal of the blockchain value (e.g., "Ethereum") */ type ChainIdentifier$1 = ChainDefinition | Blockchain | `${Blockchain}`; /** * Split CCTP contract configuration. * * Used by chains that deploy separate TokenMessenger and MessageTransmitter contracts. * This is the traditional CCTP architecture used by most EVM chains. * * @example * ```typescript * const splitConfig: CCTPSplitConfig = { * type: 'split', * tokenMessenger: '0x1234567890abcdef1234567890abcdef12345678', * messageTransmitter: '0xabcdef1234567890abcdef1234567890abcdef12', * confirmations: 12 * } * ``` */ interface CCTPSplitConfig { type: 'split'; tokenMessenger: string; messageTransmitter: string; confirmations: number; } /** * Merged CCTP contract configuration. * * Used by chains that deploy a single unified CCTP contract. * This simplified architecture is used by newer chain integrations. * * @example * ```typescript * const mergedConfig: CCTPMergedConfig = { * type: 'merged', * contract: '0x9876543210fedcba9876543210fedcba98765432', * confirmations: 1 * } * ``` */ interface CCTPMergedConfig { type: 'merged'; contract: string; confirmations: number; } /** * Version configuration for CCTP contracts. * * Defines whether the chain uses split or merged CCTP contract architecture. * Split configuration uses separate TokenMessenger and MessageTransmitter contracts, * while merged configuration uses a single unified contract. * * @example Split configuration (most EVM chains) * ```typescript * const splitConfig: VersionConfig = { * type: 'split', * tokenMessenger: '0x1234567890abcdef1234567890abcdef12345678', * messageTransmitter: '0xabcdef1234567890abcdef1234567890abcdef12', * confirmations: 12 * } * ``` * * @example Merged configuration (newer chains) * ```typescript * const mergedConfig: VersionConfig = { * type: 'merged', * contract: '0x9876543210fedcba9876543210fedcba98765432', * confirmations: 1 * } * ``` */ type VersionConfig = CCTPSplitConfig | CCTPMergedConfig; type CCTPContracts = Partial<{ v1: VersionConfig; v2: VersionConfig & { fastConfirmations: number; }; }>; /** * Configuration for the Cross-Chain Transfer Protocol (CCTP). * @category Types * @description Contains the domain and required contract addresses for CCTP support. * @example * ``` * const cctpConfig: CCTPConfig = { * domain: 0, * contracts: { * TokenMessenger: '0xabc', * MessageReceiver: '0xdef' * } * }; * ``` */ interface CCTPConfig { /** * The CCTP domain identifier. */ domain: number; /** * The contracts required for CCTP. */ contracts: CCTPContracts; /** * Indicates whether the chain supports forwarder for source and destination. * @example * ```typescript * const chainWithForwarderSupported: ChainDefinition = { * forwarderSupported: { * source: true, * destination: true, * }, * } * ``` */ forwarderSupported: { source: boolean; destination: boolean; }; } /** * Available kit contract types for enhanced chain functionality. * * @description Defines the valid contract types that can be deployed on chains * to provide additional features beyond standard CCTP functionality. * * @example * ```typescript * import type { KitContractType } from '@core/chains' * * const contractType: KitContractType = 'bridge' // Valid * const invalidType: KitContractType = 'invalid' // TypeScript error * ``` */ type KitContractType = 'bridge' | 'adapter'; /** * Configuration for Gateway v1 contracts. * * @description Contains the addresses for the GatewayWallet and GatewayMinter * smart contracts that enable Gateway functionality on a chain. * * @example * ```typescript * import type { GatewayV1Contracts } from '@core/chains' * * const v1Contracts: GatewayV1Contracts = { * wallet: '0x1234567890abcdef1234567890abcdef12345678', * minter: '0xabcdef1234567890abcdef1234567890abcdef12' * } * ``` */ interface GatewayV1Contracts { /** * The address of the GatewayWallet smart contract. * * @description The GatewayWallet contract manages wallet operations * for Gateway transactions. * * Address format varies by blockchain: * - EVM chains: 40-character hexadecimal with 0x prefix (e.g., "0x1234...") * - Solana: Base58-encoded 32-byte address (e.g., "9WzDX...") * * @example "0x1234567890abcdef1234567890abcdef12345678" */ wallet: string; /** * The address of the GatewayMinter smart contract. * * @description The GatewayMinter contract handles minting operations * for Gateway transactions. * * Address format varies by blockchain: * - EVM chains: 40-character hexadecimal with 0x prefix (e.g., "0x1234...") * - Solana: Base58-encoded 32-byte address (e.g., "9WzDX...") * * @example "0xabcdef1234567890abcdef1234567890abcdef12" */ minter: string; } /** * Versioned map of Gateway contract configurations. * * @description Maps protocol versions to their contract addresses, following * the same pattern as {@link CCTPContracts}. Each version is optional so that * chains can support any combination of Gateway protocol versions. * * @example * ```typescript * import type { GatewayContracts } from '@core/chains' * * const contracts: GatewayContracts = { * v1: { * wallet: '0x1234567890abcdef1234567890abcdef12345678', * minter: '0xabcdef1234567890abcdef1234567890abcdef12' * } * } * ``` */ type GatewayContracts = Partial<{ v1: GatewayV1Contracts; }>; /** * Configuration for the Gateway protocol on a blockchain. * * @description Contains the Gateway domain identifier and version-specific * contract configurations. Follows the same structure as {@link CCTPConfig}: * a domain number plus a versioned contracts map. * * @example * ```typescript * import type { GatewayConfig } from '@core/chains' * * const gatewayConfig: GatewayConfig = { * domain: 0, * forwarderSupported: { source: true, destination: true }, * contracts: { * v1: { * wallet: '0x1234567890abcdef1234567890abcdef12345678', * minter: '0xabcdef1234567890abcdef1234567890abcdef12' * } * } * } * ``` */ interface GatewayConfig { /** * The Gateway domain identifier for this chain. * * @description Similar to CCTP domains, this number uniquely identifies * the chain within the Gateway protocol. * * @example 0 for Ethereum, 6 for Base */ domain: number; /** * Version-specific Gateway contract addresses. * * @description Contains the addresses for each supported Gateway protocol * version, following the same pattern as {@link CCTPContracts}. */ contracts: GatewayContracts; /** * Indicate whether the chain supports the Forwarding Service as a source * and/or destination within the Gateway protocol. * * @example * ```typescript * forwarderSupported: { source: true, destination: true } * ``` */ forwarderSupported: { /** Whether this chain can be used as a source in forwarded transfers. */ source: boolean; /** Whether this chain can be used as a destination in forwarded transfers. */ destination: boolean; }; } /** * Kit-specific contract addresses for enhanced chain functionality. * * @description Maps contract types to their addresses on a specific chain. * All contract types are optional, allowing chains to selectively support * specific kit features. * * @example * ```typescript * import type { KitContracts } from '@core/chains' * * const contracts: KitContracts = { * bridge: "0x1234567890abcdef1234567890abcdef12345678" * } * * // Future example with multiple contract types: * const futureContracts: KitContracts = { * bridge: "0x1234567890abcdef1234567890abcdef12345678", * // Note: other contract types would be added to KitContractType union * // customType: "0xabcdef1234567890abcdef1234567890abcdef12" * } * ``` */ type KitContracts = Partial>; /** * Enumeration of all blockchains known to this library. * * This enum contains every blockchain that has a chain definition, regardless * of whether bridging is currently supported. For chains that support bridging * via CCTPv2, see {@link BridgeChain}. * * @enum * @category Enums * @description Provides string identifiers for each blockchain with a definition. * @see {@link BridgeChain} for the subset of chains that support CCTPv2 bridging. */ declare enum Blockchain { Algorand = "Algorand", Algorand_Testnet = "Algorand_Testnet", Aptos = "Aptos", Aptos_Testnet = "Aptos_Testnet", Arc_Testnet = "Arc_Testnet", Arbitrum = "Arbitrum", Arbitrum_Sepolia = "Arbitrum_Sepolia", Avalanche = "Avalanche", Avalanche_Fuji = "Avalanche_Fuji", Base = "Base", Base_Sepolia = "Base_Sepolia", Celo = "Celo", Celo_Alfajores_Testnet = "Celo_Alfajores_Testnet", Codex = "Codex", Codex_Testnet = "Codex_Testnet", Edge = "Edge", Edge_Testnet = "Edge_Testnet", Ethereum = "Ethereum", Ethereum_Sepolia = "Ethereum_Sepolia", Hedera = "Hedera", Hedera_Testnet = "Hedera_Testnet", HyperEVM = "HyperEVM", HyperEVM_Testnet = "HyperEVM_Testnet", Injective = "Injective", Injective_Testnet = "Injective_Testnet", Ink = "Ink", Ink_Testnet = "Ink_Testnet", Linea = "Linea", Linea_Sepolia = "Linea_Sepolia", Monad = "Monad", Monad_Testnet = "Monad_Testnet", Morph = "Morph", Morph_Testnet = "Morph_Testnet", NEAR = "NEAR", NEAR_Testnet = "NEAR_Testnet", Noble = "Noble", Noble_Testnet = "Noble_Testnet", Optimism = "Optimism", Optimism_Sepolia = "Optimism_Sepolia", Pharos = "Pharos", Pharos_Testnet = "Pharos_Testnet", Polkadot_Asset_Hub = "Polkadot_Asset_Hub", Polkadot_Westmint = "Polkadot_Westmint", Plume = "Plume", Plume_Testnet = "Plume_Testnet", Polygon = "Polygon", Polygon_Amoy_Testnet = "Polygon_Amoy_Testnet", Sei = "Sei", Sei_Testnet = "Sei_Testnet", Solana = "Solana", Solana_Devnet = "Solana_Devnet", Sonic = "Sonic", Sonic_Testnet = "Sonic_Testnet", Stellar = "Stellar", Stellar_Testnet = "Stellar_Testnet", Sui = "Sui", Sui_Testnet = "Sui_Testnet", Unichain = "Unichain", Unichain_Sepolia = "Unichain_Sepolia", World_Chain = "World_Chain", World_Chain_Sepolia = "World_Chain_Sepolia", XDC = "XDC", XDC_Apothem = "XDC_Apothem", ZKSync_Era = "ZKSync_Era", ZKSync_Sepolia = "ZKSync_Sepolia" } /** * Enum representing the subset of {@link Blockchain} that supports swap operations. * * This enum provides compile-time type safety for swap chain selection, * ensuring only supported chains are available in IDE autocomplete * when building swap parameters. * * @remarks * Unlike the full {@link Blockchain} enum, SwapChain only includes networks * where the swap functionality is actively supported by the library. * Using this enum prevents runtime errors from attempting unsupported * cross-chain swaps. * * Currently supports: * - Ethereum mainnet * - Base mainnet * - Polygon mainnet * - Solana mainnet * * @example * ```typescript * import { SwapChain, swap, createSwapKitContext } from '@circle-fin/swap-kit' * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * * const context = createSwapKitContext() * const adapter = createViemAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY * }) * * // ✅ Autocomplete shows only swap-supported chains * const result = await swap(context, { * from: { * adapter, * chain: SwapChain.Ethereum // Autocomplete: Ethereum, Base, Polygon, Solana * }, * tokenIn: 'USDC', * tokenOut: 'USDT', * amount: '100.0' * }) * ``` * * @example * ```typescript * // String literals also work (constrained to SwapChain values) * const result = await swap(context, { * from: { * adapter, * chain: 'Ethereum' // ✅ Only SwapChain strings allowed * }, * tokenIn: 'USDC', * tokenOut: 'NATIVE', * amount: '50.0' * }) * * // ❌ TypeScript error - Sui not in SwapChain enum * const invalidResult = await swap(context, { * from: { * adapter, * chain: 'Sui' // Compile-time error! * }, * tokenIn: 'USDC', * tokenOut: 'USDT', * amount: '100.0' * }) * ``` */ /** * Enum representing chains that support same-chain swaps through the Swap Kit. * * Unlike the full {@link Blockchain} enum, SwapChain includes mainnet * networks and explicitly whitelisted testnets (e.g., {@link Arc_Testnet}) * where adapter contracts are deployed (CCTPv2 support). * * Dynamic validation via {@link isSwapSupportedChain} ensures chains * automatically work when adapter contracts and supported tokens are deployed. * * @example * ```typescript * import { SwapChain } from '@core/chains' * import { swap } from '@circle-fin/swap-kit' * * const result = await swap(context, { * from: { * adapter, * chain: SwapChain.Arbitrum // Now supported! * }, * tokenIn: 'USDC', * tokenOut: 'WETH', * amount: '100.0' * }) * ``` * * @see {@link isSwapSupportedChain} for runtime validation * @see {@link getSwapSupportedChains} for all supported chains */ declare enum SwapChain { Ethereum = "Ethereum", Base = "Base", Polygon = "Polygon", Solana = "Solana", Arbitrum = "Arbitrum", Optimism = "Optimism", Avalanche = "Avalanche", Linea = "Linea", Ink = "Ink", World_Chain = "World_Chain", Unichain = "Unichain", Plume = "Plume", Sei = "Sei", Sonic = "Sonic", XDC = "XDC", HyperEVM = "HyperEVM", Monad = "Monad", Arc_Testnet = "Arc_Testnet" } /** * Chain definition that supports swap operations. * * @remarks * Expanded from 4 to ~18 supported chains based on: * - CCTPv2 support (adapter contract deployed) * - Mainnet chains plus whitelisted testnets (e.g., Arc Testnet) * * Use `isSwapSupportedChain()` for runtime validation. */ type SwapChainDefinition = ChainDefinition & { chain: Blockchain.Ethereum | Blockchain.Base | Blockchain.Polygon | Blockchain.Solana | Blockchain.Arbitrum | Blockchain.Optimism | Blockchain.Avalanche | Blockchain.Linea | Blockchain.Ink | Blockchain.World_Chain | Blockchain.Unichain | Blockchain.Plume | Blockchain.Sei | Blockchain.Sonic | Blockchain.XDC | Blockchain.HyperEVM | Blockchain.Monad | Blockchain.Arc_Testnet; }; /** * Chain identifier accepted by swap operations. * * Supports: * - ChainDefinition objects (e.g., Ethereum, Arbitrum, Optimism) * - SwapChain enum values * - String literals of SwapChain values * * Validated based on CCTPv2 support and token availability. */ type SwapChainIdentifier = SwapChainDefinition | SwapChain | `${SwapChain}`; /** * @packageDocumentation * @module SwapTokenSchemas * * Zod validation schemas for supported swap tokens. */ /** * Zod schema for validating supported swap token symbols. * * Accepts any token symbol from the SWAP_TOKEN_REGISTRY plus NATIVE. * Input matching is case-insensitive and normalized to uppercase. * * @example * ```typescript * import { supportedSwapTokenSchema } from '@core/chains' * * const result = supportedSwapTokenSchema.safeParse('USDC') * if (result.success) { * console.log('Valid swap token:', result.data) * } * ``` */ declare const supportedSwapTokenSchema: z.ZodPipeline, z.ZodEnum<[string, ...string[], "NATIVE"]>>; /** * Type inferred from the swap token schema. * Represents all valid swap token symbols. */ type SupportedSwapToken = z.infer; /** * Retrieve a chain definition by its blockchain enum value. * * Searches the set of known chain definitions and returns the one matching the provided * blockchain enum or string value. Throws an error if no matching chain is found. * * @param blockchain - The blockchain enum or its string representation to look up. * @returns The corresponding ChainDefinition object for the given blockchain. * * @throws Error If no chain definition is found for the provided enum value. * * @example * ```typescript * import { getChainByEnum } from '@core/chains' * import { Blockchain } from '@core/chains' * * const ethereum = getChainByEnum(Blockchain.Ethereum) * console.log(ethereum.name) // "Ethereum" * ``` */ declare const getChainByEnum: (blockchain: Blockchain | `${Blockchain}`) => ChainDefinition; /** * Core type definitions for blockchain transaction execution and gas estimation. * * This module provides TypeScript interfaces and types for handling blockchain * transactions across different networks, with a focus on EVM-compatible chains * and gas estimation. * * @module types */ /** * Estimated gas information for a blockchain transaction. * * This interface provides a unified way to represent gas costs across different * blockchain networks, supporting both EVM-style gas calculations and other * fee models. * * @interface EstimatedGas * @category Types * @example * ```typescript * // EVM chain example * const evmGas: EstimatedGas = { * gas: 21000n, * gasPrice: 1000000000n, // 1 Gwei * fee: (21000n * 1000000000n).toString() // Total fee in wei * }; * * // Solana example * const solanaGas: EstimatedGas = { * gas: 5000n, // Lamports for compute units * fee: '5000' // Total fee in Lamports * }; * ``` */ interface EstimatedGas { /** * The amount of gas estimated for the transaction. * For EVM chains, this represents the gas units. * For other chains, this might represent compute units or similar metrics. * * @example 21000n, 5000n */ gas: bigint; /** * The estimated price per unit of gas. * This is primarily used in EVM chains where gas price is a separate metric. * * @example 1000000000n */ gasPrice: bigint; /** * The total estimated fee as a string. * This field is useful for chains where gas/gasPrice isn't the whole story * or when the total fee needs to be represented in a different format. * For EVM chains, this is the total fee in wei (gas * gasPrice). * * @example "21000000000000", "5000" */ fee: string; } /** * Override parameters for EVM gas estimation. * * These parameters allow customization of gas estimation behavior * for EVM-compatible chains. * * @interface EvmEstimateOverrides */ interface EvmEstimateOverrides { /** * The sender's address for the transaction. * @example "0x742d35Cc6634C0532925a3b844Bc454e4438f44e" */ from?: string; /** * The value to be sent with the transaction in wei. * @example 1000000000000000000n // 1 ETH */ value?: bigint; /** * The block tag to use for estimation. * @example "latest", "safe", "finalized" */ blockTag?: 'latest' | 'earliest' | 'pending' | 'safe' | 'finalized'; /** * The maximum gas limit for the transaction. * @example 3000000 */ gasLimit?: number; /** * The maximum fee per gas unit (EIP-1559). * @example 20000000000n // 20 Gwei */ maxFeePerGas?: bigint; /** * The maximum priority fee per gas unit (EIP-1559). * @example 1500000000n // 1.5 Gwei */ maxPriorityFeePerGas?: bigint; } /** * Extended override parameters for EVM transaction execution. * * Includes all estimation overrides plus additional parameters * specific to transaction execution. * * @interface EvmExecuteOverrides * @extends EvmEstimateOverrides */ interface EvmExecuteOverrides extends EvmEstimateOverrides { /** * The nonce to use for the transaction. * If not provided, the current nonce of the sender will be used. * @example 42 */ nonce?: number; } /** * Raw EVM call data tuple for a single contract interaction. * * Represents the minimal data needed to submit an EVM transaction: * the target contract address, the ABI-encoded calldata, and an * optional native token value. Used by EIP-5792 batched execution * to compose multiple calls into a single `wallet_sendCalls` request. * * @interface EvmCallData * * @example * ```typescript * const callData: EvmCallData = { * to: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', * data: '0x095ea7b3000000000000000000000000...', * } * ``` */ interface EvmCallData { /** The target contract address. */ to: `0x${string}`; /** The ABI-encoded function calldata. */ data: `0x${string}`; /** Optional native token value to send with the call. */ value?: bigint | undefined; } /** * Prepared contract execution for EVM chains. * * Represents a prepared contract execution that can be estimated * and executed on EVM-compatible chains. * * @interface EvmPreparedChainRequest */ interface EvmPreparedChainRequest { /** The type of the prepared execution. */ type: 'evm'; /** * Estimate the gas cost for the contract execution. * * @param overrides - Optional parameters to override the default estimation behavior * @param fallback - Optional fallback gas information to use if the estimation fails * @returns A promise that resolves to the estimated gas information * @throws If the estimation fails */ estimate(overrides?: EvmEstimateOverrides, fallback?: EstimatedGas): Promise; /** * Execute the prepared contract call. * * @param overrides - Optional parameters to override the default execution behavior * @returns A promise that resolves to the transaction hash * @throws If the execution fails */ execute(overrides?: EvmExecuteOverrides): Promise; /** * Return the raw call tuple without executing or estimating. * * Expose the `{ to, data, value }` triple that would be sent on-chain so * callers can feed it into EIP-5792 `wallet_sendCalls` or other batching * mechanisms. This method is optional -- adapters that do not support * calldata extraction (e.g. Ethers v6) may omit it. * * @returns The raw EVM call data for this prepared request. * @throws Never — synchronous accessor with no failure path. * @since 2.0.0 * * @example * ```typescript * const prepared = await adapter.prepare(params, ctx) * if (prepared.getCallData) { * const { to, data, value } = prepared.getCallData() * console.log('Target:', to, 'Data:', data) * } * ``` */ getCallData?(): EvmCallData; } /** * Union type for all supported prepared contract executions. * Currently only supports EVM chains, but can be extended for other chains. */ type PreparedChainRequest = EvmPreparedChainRequest | SolanaPreparedChainRequest | NoopPreparedChainRequest; /** * Parameters for preparing an EVM contract execution. */ type EvmPreparedChainRequestParams = { /** The type of the prepared execution. */ type: 'evm'; /** The ABI of the contract. */ abi: Abi | string[]; /** The address of the contract. */ address: `0x${string}`; /** The name of the function to call. */ functionName: string; /** The arguments to pass to the function. */ args: unknown[]; /** * Specific block number to read contract state at (read-only calls only). * Used for historical reads, e.g. checking delegate status at Gateway's * processed height rather than the latest block. Ignored for write * operations (transactions). */ blockNumber?: bigint; } & Partial; /** * Parameters for preparing an EIP-712 typed data signing request (EVM). * When executed, returns the signature hex string. */ interface EvmSignTypedDataPreparedChainRequestParams { type: 'evm-sign-typed-data'; typedData: { types: Record; domain: Record; primaryType: string; message: Record; }; } /** * Solana-specific parameters for preparing a transaction. * * @example * ```typescript * import type { SolanaPreparedChainRequestParams } from '@core/adapter' * * const params: SolanaPreparedChainRequestParams = { * instructions: [transferInstruction], * addressLookupTables: [], * } * ``` */ interface SolanaPreparedChainRequestParams { /** * The array of instructions to include in the transaction. * * @remarks * Used for instruction-based transaction building. Mutually exclusive with * `serializedTransaction`. */ instructions?: TransactionInstruction[]; /** * A pre-serialized transaction as a Uint8Array (e.g., from a service like Jupiter). * * @remarks * Used for executing pre-built transactions from external services. * The transaction may be partially signed. Mutually exclusive with `instructions`. */ serializedTransaction?: Uint8Array; /** * Additional signers besides the Adapter's wallet (e.g. program-derived authorities). */ signers?: Signer[]; /** * Optional override for how many compute units this transaction may consume. * If omitted, the network's default compute budget applies. */ computeUnitLimit?: number; /** * Optional Address Lookup Table accounts for transaction compression. * Used to reduce transaction size by compressing frequently-used addresses. * This is used by @solana/web3.js adapters that have already fetched the ALT data. */ addressLookupTableAccounts?: AddressLookupTableAccount[]; /** * Optional Address Lookup Table addresses for transaction compression. * Used by adapters that need to fetch ALT data themselves (e.g., @solana/kit adapters). * These are base58-encoded addresses of ALT accounts to use for compression. */ addressLookupTableAddresses?: string[]; } /** * Parameters for preparing a message signing request (Solana). * When executed, returns the signature. * * @example * ```typescript * import type { SolanaSignMessagePreparedChainRequestParams } from '@core/adapter' * * const params: SolanaSignMessagePreparedChainRequestParams = { * type: 'solana-sign-message', * message: new TextEncoder().encode('Sign this message'), * } * ``` */ interface SolanaSignMessagePreparedChainRequestParams { type: 'solana-sign-message'; message: Uint8Array; } /** * Solana-specific configuration for transaction estimation. * @interface SolanaEstimateOverrides */ interface SolanaEstimateOverrides { /** Optional compute unit limit for the transaction. */ computeUnitLimit?: number; } /** * Solana-specific configuration for transaction execution. * @interface SolanaExecuteOverrides * @extends SolanaEstimateOverrides */ interface SolanaExecuteOverrides extends SolanaEstimateOverrides { /** The commitment level for the transaction. */ preflightCommitment?: 'processed' | 'confirmed' | 'finalized'; /** The maximum number of retries for the transaction. */ maxRetries?: number; /** Whether to skip the preflight check. */ skipPreflight?: boolean; } /** * Solana-specific prepared chain request. * @interface SolanaPreparedChainRequest */ interface SolanaPreparedChainRequest { /** The type of the chain request. */ type: 'solana'; /** Estimate the compute units and fee for the transaction. */ estimate(overrides?: SolanaEstimateOverrides, fallback?: EstimatedGas): Promise; /** Execute the prepared transaction. */ execute(overrides?: SolanaExecuteOverrides): Promise; } /** * No-op prepared chain request for unsupported operations. * * This interface represents a prepared chain request that performs no operation. * It is returned when an action is not supported by the target chain or when * no actual blockchain interaction is required. * * @remarks * The estimate and execute methods return placeholder values since no actual * transaction is performed. This allows the calling code to handle unsupported * operations gracefully without breaking the expected interface contract. * * @example * ```typescript * const noopRequest: NoopPreparedChainRequest = { * type: 'noop', * estimate: async () => ({ gasLimit: 0n, gasPrice: 0n, totalFee: 0n }), * execute: async () => '0x0000000000000000000000000000000000000000000000000000000000000000' * } * ``` */ interface NoopPreparedChainRequest { /** The type of the prepared request. */ type: 'noop'; /** * Placeholder for the estimate method. * @returns The estimated gas cost. */ estimate: (overrides?: EvmEstimateOverrides | SolanaEstimateOverrides, fallback?: EstimatedGas) => Promise; /** * Placeholder for the execute method. * @returns The transaction hash. */ execute: () => Promise; } /** * Union type for all supported contract execution parameters. * Currently only supports EVM chains, but can be extended for other chains. */ type PreparedChainRequestParams = EvmPreparedChainRequestParams | EvmSignTypedDataPreparedChainRequestParams | SolanaPreparedChainRequestParams | SolanaSignMessagePreparedChainRequestParams; /** * Response from waiting for a transaction to be mined and confirmed on the blockchain. * * @interface WaitForTransactionResponse */ interface WaitForTransactionResponse { /** * The transaction hash identifier. * @example "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" */ txHash: string; /** * The final status of the transaction execution. * Indicates whether the transaction was successfully executed or reverted. * @example "success", "reverted" */ status: 'success' | 'reverted'; /** * The total amount of gas used by all transactions in the block up to and including this transaction. * Represents the cumulative gas consumption within the block. * @example 2100000n */ cumulativeGasUsed?: bigint; /** * The amount of gas actually consumed by this specific transaction. * This value is always less than or equal to the gas limit set for the transaction. * @example 21000n */ gasUsed?: bigint; /** * The block number where the transaction was mined. * Represents the sequential position of the block in the blockchain. * @example 18500000n */ blockNumber?: bigint; /** * The hash of the block containing this transaction. * Provides a unique identifier for the block where the transaction was included. * @example "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890" */ blockHash?: string; /** * The zero-based index position of the transaction within the block. * Indicates the order in which this transaction appears in the block. * @example 5 */ transactionIndex?: number; /** * The actual gas price paid per unit of gas for this transaction. * For EIP-1559 transactions, this reflects the base fee plus priority fee. * @example 15000000000n // 15 Gwei */ effectiveGasPrice?: bigint; } interface WaitForTransactionConfig { /** * The timeout for the transaction to be mined and confirmed on the blockchain. * @example 10000 */ timeout?: number | undefined; /** * The number of confirmations to wait for the transaction to be mined and confirmed on the blockchain. * @example 1 */ confirmations?: number; /** * The maximum supported transaction version for getTransaction. * Defaults to 0 if not provided. * @example 0 */ maxSupportedTransactionVersion?: number; } /** * Type utility to extract the address context from adapter capabilities. * * @typeParam TAdapterCapabilities - The adapter capabilities type * @returns The address context type or never if capabilities are undefined */ type ExtractAddressContext = TAdapterCapabilities extends { addressContext: infer TContext; } ? TContext : never; type AddressField = TAddressContext extends 'user-controlled' ? { /** * ℹ️ Address is forbidden for user-controlled adapters. * * User-controlled adapters (like browser wallets or private key adapters) * automatically resolve the address from the connected wallet or signer. * Providing an explicit address would conflict with this behavior. * * @example * ```typescript * // ℹ️ This will cause a TypeScript error: * const context: AdapterContext<{ addressContext: 'user-controlled' }> = { * adapter: userAdapter, * chain: 'Ethereum', * address: '0x123...' // Error: Address is forbidden for user-controlled adapters * } * ``` */ address?: never; } : TAddressContext extends 'developer-controlled' ? { /** * ℹ️ Address is required for developer-controlled adapters. * * Developer-controlled adapters (like enterprise providers or server-side adapters) * require an explicit address for each operation since they don't have a single * connected wallet. The address must be provided for every operation. * * @example * ```typescript * // ℹ️ This is required: * const context: AdapterContext<{ addressContext: 'developer-controlled' }> = { * adapter: devAdapter, * chain: 'Ethereum', * address: '0x123...' // Required for developer-controlled adapters * } * * // ℹ️ This will cause a TypeScript error: * const context: AdapterContext<{ addressContext: 'developer-controlled' }> = { * adapter: devAdapter, * chain: 'Ethereum' * // Error: Address is required for developer-controlled adapters * } * ``` */ address: string; } : { /** * Address is optional for legacy adapters. * * Legacy adapters without defined capabilities maintain backward compatibility * by allowing optional address specification. */ address?: string; }; /** * Generic operation context for adapter methods with compile-time address validation. * * This type provides compile-time enforcement of address requirements based on the * adapter's capabilities. The address field behavior is determined by the adapter's * address control model: * * - **User-controlled adapters** (default): The `address` field is forbidden (never) because * the address is automatically resolved from the connected wallet or signer. * - **Developer-controlled adapters**: The `address` field is required (string) because * each operation must explicitly specify which address to use. * - **Legacy adapters**: The `address` field remains optional for backward compatibility. * * @typeParam TAdapterCapabilities - The adapter capabilities type to derive address requirements from * * @example * ```typescript * import { OperationContext } from '@core/adapter' * * // User-controlled adapter context (default - address forbidden) * type UserContext = OperationContext<{ addressContext: 'user-controlled', supportedChains: [] }> * const userCtx: UserContext = { * chain: 'Ethereum' * // address: '0x123...' // ❌ TypeScript error: address not allowed * } * * // Developer-controlled adapter context (explicit - address required) * type DevContext = OperationContext<{ addressContext: 'developer-controlled', supportedChains: [] }> * const devCtx: DevContext = { * chain: 'Ethereum', * address: '0x123...' // ✅ Required for developer-controlled * } * ``` */ type OperationContext = { /** * The blockchain network to use for this operation. */ chain: ChainIdentifier$1; } & AddressField>; /** * Fully resolved context for an adapter operation, with concrete chain and address. * * This interface guarantees that both the blockchain network (`chain`) and the account * address (`address`) are present and valid. It is produced by resolving an {@link OperationContext}, * which may have optional or conditional fields, into a form suitable for internal logic and action handlers. * * - `chain`: A fully resolved {@link ChainDefinition}, either explicitly provided or inferred from the adapter. * - `address`: A string representing the resolved account address, determined by the context or adapter, * depending on the address control model (developer- or user-controlled). * * Use this type when an operation requires both the chain and address to be unambiguous and available. * * @example * ```ts * import { ResolvedOperationContext} from "@core/adapter" * import { Solana, ChainDefinition } from '@core/chains'; * * const context: ResolvedOperationContext = { * chain: Solana, * address: '7Gk1v...abc123', // a valid Solana address * }; * * // Use context.chain and context.address in adapter operations * ``` */ interface ResolvedOperationContext { /** * The chain identifier for this operation. * Guaranteed to be defined - either from context or adapter default. */ chain: ChainDefinition; /** * The address for this operation. * Guaranteed to be defined - either specified (developer-controlled) or resolved (user-controlled). */ address: string; } /** * Base interface for all action parameter objects. * * Provide a compile-time marker to explicitly identify objects that represent * action parameters (leaf nodes) versus namespace containers that should be * traversed during type recursion. * * @remarks * This marker property exists only at the type level and is stripped away * during compilation. It serves as a deterministic way to identify action * parameter objects without relying on property name heuristics. * * All action parameter objects must extend this interface to be properly * recognized by the recursive utility types in the action system. */ interface ActionParameters { /** * Compile-time marker identifying this as an action parameter object. * * This property is used by the type system to distinguish between * namespace containers and action parameter definitions. It does not * exist at runtime and is purely for TypeScript's type checking. */ readonly __isActionParams: true; } /** * EIP-2612 permit signature parameters for gasless token approvals. * * Contains the signature components and deadline required for permit-based * token spending authorization without requiring separate approval transactions. * * @example * ```typescript * const permitParams: PermitParams = { * deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now * v: 27, * r: '0x1234567890abcdef...', * s: '0xfedcba0987654321...' * } * ``` */ interface PermitParams { /** * Permit expiration timestamp (Unix timestamp in seconds). * * The permit signature becomes invalid after this timestamp. * Must be greater than the current block timestamp. */ deadline: bigint; /** * Recovery parameter of the ECDSA signature (27 or 28). * * Used to recover the public key from the signature components. */ v: number; /** * R component of the ECDSA signature. * * First 32 bytes of the signature as a hex string. */ r: string; /** * S component of the ECDSA signature. * * Second 32 bytes of the signature as a hex string. */ s: string; } /** * Action map for Circle's Cross-Chain Transfer Protocol (CCTP) version 2 operations. * * Define the parameter schemas for CCTP v2 actions that enable native USDC * transfers between supported blockchain networks. Use Circle's attestation * service to verify and complete cross-chain transactions with cryptographic * proof of burn and mint operations. * * @remarks * CCTP v2 represents Circle's native cross-chain transfer protocol that allows * USDC to move between chains without traditional lock-and-mint bridging. * Instead, USDC is burned on the source chain and minted natively on the * destination chain using cryptographic attestations. * * The protocol supports both "slow" (free) and "fast" (fee-based) transfer * modes, with configurable finality thresholds and destination execution * parameters for advanced use cases. * * @example * ```typescript * import type { CCTPv2ActionMap } from '@core/adapter/actions/cctp/v2' * import { mainnet, polygon } from '@core/chains' * * // Deposit and burn USDC for cross-chain transfer * const burnParams: CCTPv2ActionMap['depositForBurn'] = { * amount: '1000000', // 1 USDC (6 decimals) * mintRecipient: '0x742d35Cc6634C0532925a3b8D8E5e8d8D8e5e8d8D8e5e8', * maxFee: '1000', // 0.001 USDC fast fee * minFinalityThreshold: 65, * fromChain: mainnet, * toChain: polygon * } * * // Receive and mint USDC on destination chain * const receiveParams: CCTPv2ActionMap['receiveMessage'] = { * eventNonce: '0x123abc...', * attestation: '0xdef456...', * message: '0x789012...', * fromChain: mainnet, * toChain: polygon * } * ``` * * @see {@link ChainDefinitionWithCCTPv2} for supported chain definitions */ interface CCTPv2ActionMap { /** * Initiate a cross-chain USDC transfer by depositing and burning tokens on the source chain. * * Burn USDC tokens on the source chain and generate a message for attestation * by Circle's infrastructure. The burned tokens will be minted on the destination * chain once the attestation is obtained and the receive message is executed. * * @remarks * This action represents the first step in a CCTP cross-chain transfer. After * execution, you must wait for Circle's attestation service to observe the burn * event and provide a cryptographic attestation that can be used to mint the * equivalent amount on the destination chain. * * The `maxFee` parameter enables fast transfers through Circle's fast liquidity * network, where liquidity providers can fulfill transfers immediately in exchange * for a fee. Set to "0" for slower, free transfers that wait for full finality. */ depositForBurn: ActionParameters & { /** * Amount of USDC to deposit and burn (in token's smallest unit). * * Specify the amount in the token's atomic units (e.g., for USDC with * 6 decimals, "1000000" represents 1 USDC). This amount will be burned * on the source chain and minted on the destination chain. */ amount: bigint; /** * Address of the recipient who will receive minted tokens on the destination chain. * * Provide the destination address as a 32-byte hex string (bytes32 format). */ mintRecipient: string; /** * Address authorized to call receiveMessage on the destination chain. * * Restrict who can execute the final minting step on the destination chain. * If not specified or set to bytes32(0), any address can call receiveMessage. * Use this for advanced integrations requiring specific execution control. * * @defaultValue bytes32(0) - allows any address to complete the transfer */ destinationCaller?: string; /** * Maximum fee to pay for fast transfer fulfillment. * * Specify the maximum amount (in the same units as `amount`) you're willing * to pay for immediate liquidity. Set to "0" for free transfers that wait * for full chain finality. Higher fees increase the likelihood of fast * fulfillment. */ maxFee: bigint; /** * Minimum finality threshold for attestation eligibility. * * Set the number of confirmations required before Circle's attestation * service will observe and attest to the burn event. Higher values * provide stronger finality guarantees but increase transfer time. * Typical values: 1000 for fast transfers, 2000 for maximum security. */ minFinalityThreshold: number; /** * Source chain definition where tokens will be burned. */ fromChain: ChainDefinitionWithCCTPv2; /** * Destination chain definition where tokens will be minted. */ toChain: ChainDefinitionWithCCTPv2; }; /** * Complete a cross-chain transfer by receiving and processing an attested message. * * Execute the final step of a CCTP transfer by submitting Circle's attestation * and the original message to mint USDC tokens on the destination chain. * This action consumes the attestation and delivers tokens to the specified * recipient from the original burn operation. * * @remarks * This action must be called after obtaining a valid attestation from Circle's * API for a corresponding `depositForBurn` operation. The attestation proves * that tokens were burned on the source chain and authorizes minting the * equivalent amount on the destination chain. * * The message parameter contains the original burn message data, while the * attestation provides the cryptographic proof. Both must match exactly * with Circle's records for the transaction to succeed. */ receiveMessage: ActionParameters & { /** * Unique nonce identifying the specific burn event. * * Provide the event nonce from the MessageSent event emitted by the * depositForBurn transaction. This must be a 0x-prefixed 64-character * hex string representing the 32-byte nonce value. */ readonly eventNonce: string; /** * Cryptographic attestation from Circle's infrastructure. * * Submit the attestation obtained from Circle's API that proves the * corresponding burn event occurred and was observed. This must be * a valid 0x-prefixed hex string containing Circle's signature data. */ readonly attestation: string; /** * Original message bytes from the source chain burn event. * * Provide the raw message data emitted in the MessageSent event from * the depositForBurn transaction. This 0x-prefixed hex string contains * the encoded transfer details that will be verified against the attestation. */ readonly message: string; /** * Source chain definition where the original burn occurred. */ readonly fromChain: ChainDefinitionWithCCTPv2; /** * Destination chain definition where tokens will be minted. */ readonly toChain: ChainDefinitionWithCCTPv2; /** * Optional destination wallet address on the destination chain to receive minted USDC. * * When provided (e.g., for Solana), the mint instruction will derive the * recipient's Associated Token Account (ATA) from this address instead of * the adapter's default address. */ readonly destinationAddress?: string; /** * The mint recipient address from the decoded CCTP message. * * This is the actual address encoded in the burn message where tokens will be minted. * For Solana, this is already the Associated Token Account (ATA) address, not the owner. * For EVM chains, this is the recipient's wallet address. */ readonly mintRecipient?: string; }; /** * Initiate a cross-chain USDC transfer using a custom bridge contract with preapproval funnel. * * This action combines token approval and burning into a single transaction using * a custom bridge contract that supports preapproval functionality. It provides * enhanced gas efficiency by eliminating separate approval transactions while * maintaining the same developer interface as standard CCTP transfers. * * @remarks * This action is only available on chains that support custom bridge contracts, * as determined by `hasCustomContractSupport(chain, 'bridge')`. The custom bridge * handles token approval internally and supports advanced features like protocol * fees and custom routing logic. * * For basic use cases, this provides the same interface as `depositForBurn`. * For advanced use cases, optional protocol fee parameters enable custom fee * collection and revenue sharing models. * * @example * ```typescript * // Basic usage (same as depositForBurn) * await adapter.action('cctp.v2.customBurn', { * amount: BigInt('1000000'), * mintRecipient: '0x...', * maxFee: BigInt('1000'), * minFinalityThreshold: 65 * }) * * // Advanced usage with protocol fees * await adapter.action('cctp.v2.customBurn', { * amount: BigInt('1000000'), * mintRecipient: '0x...', * maxFee: BigInt('1000'), * minFinalityThreshold: 65, * protocolFee: BigInt('100'), * feeRecipient: '0xFeeRecipientAddress' * }) * ``` */ customBurn: ActionParameters & { /** * Amount of USDC to burn (in token's smallest unit). * * Specify the amount in the token's atomic units (e.g., for USDC with * 6 decimals, 1000000n represents 1 USDC). This amount will be burned * on the source chain and minted on the destination chain. */ amount: bigint; /** * Address of the recipient who will receive minted tokens on the destination chain. * * Provide the destination address as a 32-byte hex string (bytes32 format). */ mintRecipient: string; /** * Address authorized to call receiveMessage on the destination chain. * * Restrict who can execute the final minting step on the destination chain. * If not specified or set to bytes32(0), any address can call receiveMessage. * Use this for advanced integrations requiring specific execution control. * * @defaultValue bytes32(0) - allows any address to complete the transfer */ destinationCaller?: string; /** * Maximum fee to pay for fast transfer fulfillment. * * Specify the maximum amount (in the same units as `amount`) you're willing * to pay for immediate liquidity. Set to "0" for free transfers that wait * for full chain finality. Higher fees increase the likelihood of fast * fulfillment. */ maxFee: bigint; /** * Minimum finality threshold for attestation eligibility. * * Set the number of confirmations required before Circle's attestation * service will observe and attest to the burn event. Higher values * provide stronger finality guarantees but increase transfer time. * Typical values: 65 for standard transfers, 2000 for maximum security. */ minFinalityThreshold: number; /** * Protocol fee amount (in token's smallest unit). * * Additional fee charged by the custom bridge for enhanced functionality. * This fee is separate from the Circle fast transfer fee and is paid to * the specified fee recipient. Enables custom fee collection and revenue * sharing models for bridge operators. * * @defaultValue 0n - no protocol fee for basic usage */ protocolFee?: bigint | undefined; /** * Address to receive the protocol fee. * * Wallet address where the protocol fee will be sent. This enables * custom fee collection and revenue sharing models for bridge operators. * Only relevant when protocolFee is greater than 0. * * @defaultValue bridge contract address - safe fallback for zero fees */ feeRecipient?: string | undefined; /** * Source chain definition where tokens will be burned. */ fromChain: ChainDefinitionWithCCTPv2; /** * Destination chain definition where tokens will be minted. */ toChain: ChainDefinitionWithCCTPv2; /** * Permit parameters for the custom bridge contract. */ permitParams?: PermitParams; }; /** * Initiate a cross-chain USDC transfer using a custom bridge contract with hook data for CCTP forwarding. * * This action combines the custom bridge functionality with CCTP forwarding hookData. * It uses either `bridgeWithPreapprovalAndHook` or `bridgeWithPermitAndHook` contract * functions depending on whether permit parameters are provided. * * @remarks * When CCTP forwarding is enabled with custom burn, Circle's relay infrastructure will: * 1. Watch for the burn transaction with forwarding hookData * 2. Fetch the attestation automatically * 3. Submit the destination mint transaction on behalf of the user * 4. Deduct the relay fee from the minted USDC * * The hookData must be formatted with the CCTP forwarding magic bytes prefix * followed by version and length fields. Use the `buildForwardingHookData` * utility to construct properly formatted hookData. * * @example * ```typescript * import { buildForwardingHookData } from '@core/utils' * import { hasCustomContractSupport } from '@core/chains' * * if (hasCustomContractSupport(sourceChain, 'bridge')) { * await adapter.action('cctp.v2.customBurnWithHook', { * amount: BigInt('1000000'), * mintRecipient: '0x...', * maxFee: BigInt('50000'), * minFinalityThreshold: 1000, * fromChain: ethereum, * toChain: base, * hookData: buildForwardingHookData() * }) * } * ``` */ customBurnWithHook: CCTPv2ActionMap['customBurn'] & { /** * Hex-encoded hook data for CCTP forwarding. * * The hookData signals to Circle's Orbit relayer that forwarding is requested. * Must be formatted with the CCTP forwarding magic bytes prefix ("cctp-forward" * right-padded to 24 bytes) followed by uint32 version and uint32 length fields. * * Use the `buildForwardingHookData` utility to construct properly formatted hookData. */ hookData: string; }; /** * Initiate a cross-chain USDC transfer with hook data for CCTP forwarding. * * This action extends the standard `depositForBurn` with an additional `hookData` * parameter that signals to Circle's Orbit relayer that the user wants automated * attestation fetching and destination mint execution. * * @remarks * When CCTP forwarding is enabled, Circle's relay infrastructure will: * 1. Watch for the burn transaction with forwarding hookData * 2. Fetch the attestation automatically * 3. Submit the destination mint transaction on behalf of the user * 4. Deduct the relay fee from the minted USDC * * The hookData must be formatted with the CCTP forwarding magic bytes prefix * followed by version and length fields. Use the `buildForwardingHookData` * utility to construct properly formatted hookData. * * @example * ```typescript * import { buildForwardingHookData } from '@core/utils' * * await adapter.action('cctp.v2.depositForBurnWithHook', { * amount: BigInt('1000000'), * mintRecipient: '0x...', * maxFee: BigInt('50000'), // Must cover burn fee + forwarding fee * minFinalityThreshold: 1000, * fromChain: ethereum, * toChain: base, * hookData: buildForwardingHookData() * }) * ``` */ depositForBurnWithHook: CCTPv2ActionMap['depositForBurn'] & { /** * Hex-encoded hook data for CCTP forwarding. * * The hookData signals to Circle's Orbit relayer that forwarding is requested. * Must be formatted with the CCTP forwarding magic bytes prefix ("cctp-forward" * right-padded to 24 bytes) followed by uint32 version and uint32 length fields. * * Use the `buildForwardingHookData` utility to construct properly formatted hookData. */ hookData: string; }; } /** * Central registry for Cross-Chain Transfer Protocol (CCTP) action namespaces. * * Define versioned action maps for CCTP operations across different protocol * versions. Each version key represents a specific CCTP implementation with * its own parameter schemas and operational requirements. * * @remarks * CCTP actions enable cross-chain USDC transfers through Circle's native * bridging protocol. Each version namespace contains actions specific to * that protocol iteration, allowing for protocol upgrades while maintaining * backward compatibility in the action system. * * This interface follows the same pattern as other action namespaces but * is organized by protocol version rather than token type. * * @see {@link CCTPv2ActionMap} for version 2 action definitions */ interface CCTPActionMap { /** CCTP version 2 operations for cross-chain USDC transfers. */ readonly v2: CCTPv2ActionMap; } /** * Permit signature standards for gasless token approvals. * * Defines the permit types that can be used to approve token spending * without requiring a separate approval transaction. * * @remarks * - NONE: No permit, tokens must be pre-approved via separate transaction * - EIP2612: Standard ERC-20 permit (USDC, DAI v2, and most modern tokens) */ declare enum PermitType { /** No permit required - tokens must be pre-approved */ NONE = 0, /** EIP-2612 standard permit */ EIP2612 = 1 } /** * Token input with permit signature for gasless approval. * * The Adapter Contract uses this to pull tokens from the user's wallet * using permit signatures instead of requiring separate approval transactions. * * Shared by the `swap.*` and `earn.*` action namespaces because both forward * `tokenInputs` unchanged to the adapter contract's `execute` call. * * @example * ```typescript * const tokenInput: TokenInput = { * permitType: PermitType.EIP2612, * token: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC * amount: 1000000n, // 1 USDC * permitCalldata: '0x...' // Encoded permit(value, deadline, v, r, s) * } * ``` */ interface TokenInput { /** * Type of permit to execute. */ permitType: PermitType; /** * Token contract address to pull from user. */ token: `0x${string}`; /** * Amount of tokens to pull via permit. */ amount: bigint; /** * ABI-encoded permit calldata. * * For EIP-2612: encode(value, deadline, v, r, s) * * @example '0x0000000000000000000000000000000000000000000000000000000000989680...' */ permitCalldata: `0x${string}`; } /** * Parameters for executing a service-signed earn operation via the Adapter * smart contract on EVM chains. * * Shared across earn action keys: `earn.deposit`, `earn.withdraw`, and * `earn.claimRewards`. Each operation forwards the same `executeParams`, * `tokenInputs`, and `signature` triple to the adapter contract's `execute` * function. The service signs `executeParams` off-chain; the contract verifies * the signature on-chain. * * @example * ```typescript * import type { ActionPayload } from '@core/adapter' * * const params: ActionPayload<'earn.deposit'> = { * executeParams: { instructions: [], tokens: [], execId: 1n, deadline: 0n, metadata: '0x' }, * tokenInputs: [], * signature: '0x...', * } * * const prepared = await adapter.prepareAction('earn.deposit', params, { chain, address }) * const txHash = await prepared.execute() * ``` */ interface ExecuteEarnEVMParams extends ActionParameters { /** * Execution parameters returned by the earn service. * * Kept as an opaque record so the adapter forwards the service-signed struct * unchanged. The adapter contract ABI decodes it on-chain. */ executeParams: Record; /** * Token inputs with permit signatures for gasless approvals. * * Populated by the earn provider after it decides how token spending is * authorised. Today deposit uses a separate `token.approve` transaction and * passes `PermitType.NONE`; a future permit-enabled path can populate this * field without a breaking change. */ tokenInputs: TokenInput[]; /** * EIP-712 signature from the earn service proxy. * * The adapter contract verifies this signature on-chain. Passed through * unchanged. */ signature: `0x${string}`; } /** * Parameters for earn execute actions across supported ecosystems. * * EVM-only today; becomes a union when a non-EVM adapter implementation * lands. Action handlers narrow via a property-based type guard, same * pattern as {@link ExecuteSwapParams}. */ type ExecuteEarnParams = ExecuteEarnEVMParams; /** * Action map for earn operations. * * Each action key forwards the same `(executeParams, tokenInputs, signature)` * triple to the adapter contract. Provider-side orchestration performs any * required token approval; this action only prepares the adapter execute call. */ interface EarnActionMap { /** * Execute a service-signed deposit against the adapter contract. */ readonly deposit: ExecuteEarnParams; /** * Execute a service-signed withdraw against the adapter contract. */ readonly withdraw: ExecuteEarnParams; /** * Execute a service-signed claim rewards against the adapter contract. */ readonly claimRewards: ExecuteEarnParams; } /** * Single instruction to execute within the Adapter Contract. * * Each instruction represents a contract call (swap, fee collection, etc.) * with pre-execution approval and post-execution validation. * * @example * ```typescript * const swapInstruction: Instruction = { * target: '0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE', // LiFi Diamond * data: '0x...', // LiFi swap calldata * value: 0n, * tokenIn: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC * amountToApprove: 1000000000n, // 1000 USDC to approve * tokenOut: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT * minTokenOut: 995000000n // 995 USDT minimum (0.5% slippage) * } * ``` */ interface Instruction { /** * Target contract address to call. * * Can be a DEX router, fee taker contract, or token contract. */ target: `0x${string}`; /** * ABI-encoded calldata for the target contract. */ data: `0x${string}`; /** * ETH value to send with the call (for native token operations). * * @defaultValue 0n */ value: bigint; /** * Token to approve to target before executing instruction. * * Set to zero address (0x00...00) to disable pre-approval. */ tokenIn: `0x${string}`; /** * Amount of tokenIn to approve to target before executing instruction. * * @remarks * Field name matches the adapter contract's `amountToApprove` parameter exactly. * * @defaultValue 0n if tokenIn is zero address */ amountToApprove: bigint; /** * Token to validate minimum balance after instruction. * * Set to zero address (0x00...00) to disable post-validation. */ tokenOut: `0x${string}`; /** * Minimum required balance of tokenOut after instruction. * * @defaultValue 0n if tokenOut is zero address */ minTokenOut: bigint; } /** * Token recipient for residual sweep. * * After all instructions complete, the Adapter Contract sweeps * any remaining balances to the specified beneficiaries. */ interface TokenRecipient { /** * Token contract address to sweep. */ token: `0x${string}`; /** * Address to receive swept tokens. */ beneficiary: `0x${string}`; } /** * Execution parameters for the Adapter Contract. * * This struct is signed via EIP-712 by the Circle proxy and verified * on-chain to ensure the execution is authorized. * * @remarks * The executeParams are provided by the stablecoin-service and must be * passed to the Adapter Contract exactly as received (no modification). * * @example * ```typescript * const executeParams: ExecuteParams = { * instructions: [ * { target: dexRouter, data: swapCalldata, ... } * ], * tokens: [ * { token: USDC, beneficiary: userAddress }, * { token: USDT, beneficiary: userAddress } * ], * execId: 123456789n, * deadline: BigInt(Math.floor(Date.now() / 1000) + 1800), * metadata: '0x' * } * ``` */ interface ExecuteParams { /** * Array of instructions to execute sequentially. * * Each instruction can be a swap, fee collection, or other contract call. */ instructions: Instruction[]; /** * Token recipients for residual sweep. * * Typically a 2-tuple: [tokenIn recipient, tokenOut recipient] */ tokens: TokenRecipient[]; /** * Unique execution identifier for replay protection. * * Must be globally unique and is marked as used after execution. */ execId: bigint; /** * Execution deadline timestamp (Unix seconds). * * Transaction reverts if block.timestamp is greater than deadline. */ deadline: bigint; /** * Optional metadata for tracking and analytics. */ metadata: `0x${string}`; } /** * Parameters for executing a swap transaction via the Adapter smart contract. * * This action executes swap transactions through the Adapter Contract, which * handles token approvals via permits (EIP-2612, Permit2, etc.) and executes * multi-step swap instructions atomically on-chain. * * @remarks * The swap flow uses the Adapter Contract pattern: * 1. Service provides `executeParams` and `signature` (proxy-signed EIP-712) * 2. SDK builds `tokenInputs` with permit signatures for gasless approvals * 3. SDK calls AdapterContract.execute(executeParams, tokenInputs, signature) * 4. Adapter Contract pulls tokens via permits, executes swaps, validates outputs * * This enables: * - Single atomic transaction (permit + swap in one tx) * - Gasless approvals via EIP-2612/Permit2 * - Slippage protection enforced on-chain * - Multi-step instructions (swap + fees) atomically * * **Permit Support**: The SDK constructs `TokenInput` with `permitCalldata` * containing the encoded permit signature. The Adapter Contract executes * the permit on-chain before pulling tokens. * * @example * ```typescript * import type { ExecuteSwapParams } from '@core/adapter' * import { createSwap } from '@core/service-client' * import { PermitType } from '@core/adapter' * * // Get swap transaction from service * const swapResponse = await createSwap({ * tokenInAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', * tokenOutAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7', * tokenInChain: 'Ethereum', * fromAddress: '0x...', * toAddress: '0x...', * amount: '1000000', * apiKey: 'KIT_KEY:...', * }) * * // Build token inputs with permit * const tokenInputs: TokenInput[] = [{ * permitType: PermitType.EIP2612, * token: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', * amount: 1000000n, * permitCalldata: '0x...' // Encoded permit signature * }] * * // Prepare action parameters * const params: ExecuteSwapParams = { * executeParams: swapResponse.transaction.executeParams, * tokenInputs, * signature: swapResponse.transaction.signature, * inputAmount: BigInt(swapResponse.amount), * tokenInAddress: swapResponse.tokenInAddress as `0x${string}` * } * ``` */ interface ExecuteSwapEVMParams extends ActionParameters { /** * Execution parameters from the stablecoin-service. * * Contains instructions, token recipients, execution ID, deadline, and metadata. * This is an EIP-712 signed struct that the Adapter Contract validates. * * Provided by the service - do not modify. */ executeParams: ExecuteParams; /** * Token inputs with permit signatures for gasless approvals. * * The SDK constructs this array with permit data for each token that needs * to be pulled from the user's wallet. The Adapter Contract executes these * permits on-chain before executing swap instructions. * * @remarks * For EIP-2612 permits, the SDK must: * 1. Build typed data with token, spender (Adapter), amount, nonce, deadline * 2. Get user signature via `adapter.signTypedData()` * 3. Encode as permitCalldata: encode(value, deadline, v, r, s) * * @example * ```typescript * [{ * permitType: PermitType.EIP2612, * token: '0xUSDC', * amount: 1000000n, * permitCalldata: '0x...' * }] * ``` */ tokenInputs: TokenInput[]; /** * EIP-712 signature from the Circle proxy service. * * The service signs the executeParams to authorize the execution. * The Adapter Contract verifies this signature on-chain. * * Provided by the service - do not modify. */ signature: `0x${string}`; /** * Swap input amount in base units. * * @remarks * The amount of tokens being swapped, provided in the token's base units (e.g., wei for ETH, * smallest denomination for ERC20 tokens). This value should be extracted from the service * response, as it represents the authoritative swap amount for the operation. * * For native currency swaps (ETH → USDC), this amount is sent as the transaction `value`. * For ERC20 swaps (USDC → USDT), this amount determines the permit or approval quantity. * * @see CreateSwapResponse.amount - Service response field containing this value * * @example * ```typescript * import { createSwap } from '@core/service-client' * * // Get swap transaction from service * const response = await createSwap({ * tokenInAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', * amount: '1000000', // 1 USDC (6 decimals) * ... * }) * * // Prepare swap execution using service response amount * await adapter.prepareAction('swap.execute', { * executeParams: response.transaction.executeParams, * tokenInputs, * signature: response.transaction.signature, * inputAmount: BigInt(response.amount), * tokenInAddress: response.tokenInAddress, * }, context) * ``` */ inputAmount: bigint; /** * Token address being swapped from. * * @remarks * Used to determine if the swap involves native currency (ETH, MATIC, etc.) or ERC20 tokens. * When tokenInAddress is NATIVE_TOKEN_ADDRESS (0xEeee...), the inputAmount is sent as tx.value. * * @see CreateSwapResponse.tokenInAddress - Service response field containing this value * * @example '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' for ETH * @example '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' for USDC */ tokenInAddress: `0x${string}`; } /** * Parameters for executing a swap transaction on Solana. * * This action executes swap transactions on Solana chains by deserializing * and executing a pre-built transaction provided by the stablecoin-service. * * @remarks * Unlike EVM chains that use the Adapter Contract pattern, Solana swaps * execute a fully serialized transaction provided by the service. The * transaction is base64-encoded and contains all necessary instructions * for the swap operation. * * The service handles: * - DEX aggregator routing (Jupiter, etc.) * - Fee collection * - Slippage protection * - Token account management * * @example * ```typescript * import type { ExecuteSwapSolanaParams } from '@core/adapter' * import { createSwap } from '@core/service-client' * * // Get swap transaction from service * const swapResponse = await createSwap({ * tokenInAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', * tokenOutAddress: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr', * tokenInChain: 'Solana', * fromAddress: 'YubQzu18FDqJRyNfG8JqHmsdbxhnoQqcKUHBdUkN6tP', * toAddress: 'YubQzu18FDqJRyNfG8JqHmsdbxhnoQqcKUHBdUkN6tP', * amount: '1000000', * apiKey: 'KIT_KEY:...', * }) * * // Prepare action parameters * const params: ExecuteSwapSolanaParams = { * serializedTransaction: swapResponse.transaction.data * } * ``` */ interface ExecuteSwapSolanaParams extends ActionParameters { /** * Base64-encoded serialized Solana transaction. * * This transaction is fully constructed by the stablecoin-service and * contains all swap instructions, fee payments, and token account setup. * The transaction must be deserialized, signed, and submitted to the network. * * Provided by the service - do not modify. * * @example 'AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAJFQg...' */ serializedTransaction: string; } /** * Parameters accepted by the swap.execute action, supporting both EVM and Solana chains. * * @remarks * This union type covers all chain-specific swap execution parameter interfaces * currently supported by the App Kit. Extend this union to support * additional blockchains as needed. Each member provides all fields required * to prepare and execute a pre-built swap transaction on its respective chain. * * **Type Narrowing**: The correct parameter type is inferred from the chain type * in the `OperationContext` passed to `adapter.prepareAction()`. Action handlers * use property-based type guards (checking for `executeParams`/`tokenInputs` for EVM * or `serializedTransaction` for Solana) to narrow the union type at runtime. * * - {@link ExecuteSwapEVMParams} - For EVM chains (has `executeParams` and `tokenInputs`) * - {@link ExecuteSwapSolanaParams} - For Solana chains (has `serializedTransaction`) */ type ExecuteSwapParams = ExecuteSwapEVMParams | ExecuteSwapSolanaParams; /** * Action map for swap operations on EVM chains. * * This namespace contains actions related to token swapping operations. * These actions handle the execution of pre-built swap transactions from * DEX aggregators and routing services. * * @remarks * The swap namespace is designed to be extensible for future swap-related * operations such as multi-hop swaps, batched swaps, or swap-and-bridge * compositions. */ interface SwapActionMap { /** * Execute a pre-built swap transaction. * * This action prepares and executes swap transactions constructed by the * stablecoin-service API. It accepts transaction parameters (to, data, value) * and returns a prepared chain request suitable for gas estimation or execution. */ readonly execute: ExecuteSwapParams; } interface TokenActionMap { /** * Set an allowance for a delegate to spend tokens on behalf of the wallet. * * On chains without native allowance support, this may return a noop result * indicating the step can be safely skipped. */ approve: ActionParameters & { /** * The contract address of the token. */ tokenAddress: string; /** * The address that will be approved to spend the tokens. */ delegate: string; /** * The amount of tokens to approve for spending (in token's smallest unit). */ amount: bigint; }; /** * Check the current allowance between an owner and spender for any token. * * On chains without allowance support, this typically returns the maximum * possible value to indicate unlimited spending capability. */ allowance: ActionParameters & { /** * The contract address of the token. */ tokenAddress: string; /** * The address of the wallet that owns the tokens. If not provided, it will be * automatically derived from the adapter context. */ walletAddress?: string | undefined; /** * The address to check the allowance for. */ delegate: string; }; /** * Transfer tokens directly from the wallet to another address. */ transfer: ActionParameters & { /** * The contract address of the token. */ tokenAddress: string; /** * The address to send the tokens to. */ to: string; /** * The amount of tokens to transfer (in token's smallest unit). */ amount: bigint; }; /** * Transfer tokens from one address to another using a pre-approved allowance. * * On chains without allowance support, this may behave differently or throw * an error if the operation is not supported. */ transferFrom: ActionParameters & { /** * The contract address of the token. */ tokenAddress: string; /** * The address to transfer tokens from (must have given allowance to the caller). */ from: string; /** * The address to send the tokens to. */ to: string; /** * The amount of tokens to transfer (in token's smallest unit). */ amount: bigint; }; /** * Get the current token balance for a wallet address. */ balanceOf: ActionParameters & { /** * The contract address of the token. */ tokenAddress: string; /** * The address to check the balance for. If not provided, it will be * automatically derived from the adapter context. */ walletAddress?: string | undefined; }; } /** * USDC-specific operations that automatically resolve the token address. * * These include all standard ERC20 operations plus additional safety functions * that USDC supports. The interface provides the same core operations as * {@link TokenActionMap} but without requiring a `tokenAddress` parameter, * plus additional USDC-specific extensions. * * @example * ```typescript * // USDC operations (address auto-resolved) * await adapter.action('usdc.approve', { * delegate: '0x1234...', * amount: '1000000' // 1 USDC * }) * * // USDC-specific safe allowance functions * await adapter.action('usdc.increaseAllowance', { * delegate: '0x1234...', * amount: '500000' // increase by 0.5 USDC * }) * * // vs. general token operations (address required) * await adapter.action('token.approve', { * tokenAddress: '0xA0b86a33E6441c8C1c7C16e4c5e3e5b5e4c5e3e5b5e4c5e', * delegate: '0x1234...', * amount: '1000000' * }) * ``` */ type BaseUSDCActions = { [K in keyof TokenActionMap]: Omit; }; /** * USDC action map with both standard ERC20 operations and USDC-specific extensions. * * This provides all standard token operations plus additional safety functions * that USDC implements beyond the base ERC20 standard. */ interface USDCActionMap { /** * Set an allowance for a delegate to spend USDC tokens on behalf of the wallet. * * Automatically uses the USDC contract address for the current chain. * On chains without native allowance support, this may return a noop result. */ approve: BaseUSDCActions['approve']; /** * Check the current allowance between an owner and spender for USDC tokens. * * Automatically uses the USDC contract address for the current chain. * This is a read-only operation. */ allowance: BaseUSDCActions['allowance']; /** * Safely increase the allowance for a delegate to spend USDC tokens. * * This is a USDC-specific function that provides safer allowance management * compared to direct approve() calls. Automatically uses the USDC contract * address for the current chain. */ increaseAllowance: ActionParameters & { /** * The address that will have their allowance increased. */ delegate: string; /** * The amount to increase the allowance by (in USDC's smallest unit). */ amount: bigint; /** * The chain definition for the current chain. */ chain?: ChainDefinition; }; /** * Safely decrease the allowance for a delegate to spend USDC tokens. * * This is a USDC-specific function that provides safer allowance management. * Automatically uses the USDC contract address for the current chain. */ decreaseAllowance: ActionParameters & { /** * The address that will have their allowance decreased. */ delegate: string; /** * The amount to decrease the allowance by (in USDC's smallest unit). */ amount: bigint; }; /** * Transfer USDC tokens directly from the wallet to another address. * * Automatically uses the USDC contract address for the current chain. */ transfer: BaseUSDCActions['transfer']; /** * Transfer USDC tokens from one address to another using a pre-approved allowance. * * Automatically uses the USDC contract address for the current chain. * The caller must have sufficient allowance from the 'from' address. */ transferFrom: BaseUSDCActions['transferFrom']; /** * Get the current USDC balance for a wallet address. * * Automatically uses the USDC contract address for the current chain. * This is a read-only operation. */ balanceOf: Omit; /** * Get the EIP-712 domain name of the USDC contract on the current chain. * * Automatically uses the USDC contract address for the current chain. * This is a read-only operation with no parameters. */ name: ActionParameters & { /** * Optional chain override; defaults to the operation context chain. */ chain?: ChainDefinition; }; } /** * USDT-specific operations that automatically resolve the token address. * * These include standard ERC20 operations. The interface provides the same core * operations as {@link TokenActionMap} but without requiring a `tokenAddress` * parameter. * * @example * ```typescript * // USDT operations (address auto-resolved) * await adapter.action('usdt.transfer', { * to: '0x1234...', * amount: '1000000' // 1 USDT * }) * * // vs. general token operations (address required) * await adapter.action('token.transfer', { * tokenAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7', * to: '0x1234...', * amount: '1000000' * }) * ``` */ type BaseUSDTActions = { [K in keyof TokenActionMap]: Omit; }; /** * USDT action map with standard ERC20 operations. * * This provides standard token operations for USDT transfers. */ interface USDTActionMap { /** * Transfer USDT tokens directly from the wallet to another address. * * Automatically uses the USDT contract address for the current chain. */ transfer: BaseUSDTActions['transfer']; } /** * Versioned wrapper for Gateway action namespaces. * * Follows the same pattern as {@link CCTPActionMap}: each version is a * nested namespace so that action keys read `gateway.v1.deposit`, etc. * * @see {@link GatewayV1ActionMap} for v1 action definitions */ interface GatewayActionMap { /** Gateway protocol v1 operations. */ readonly v1: GatewayV1ActionMap; } /** * Action map for Circle Gateway Wallet v1 contract operations. * * Mirrors the GatewayWallet interface: deposit variants, delegate management, * and balance queries. * * @see https://developers.circle.com/gateway/references/contract-interfaces-and-events * @see https://developers.circle.com/gateway/references/solana-programs */ interface GatewayV1ActionMap { /** * Deposit tokens after approving the Gateway contract. Balance is credited to the caller. * * Corresponds to `deposit(address token, uint256 value)`. */ deposit: ActionParameters & { /** Token contract address (e.g. USDC). */ token: string; /** Amount in token's smallest unit. */ value: bigint; /** Chain with Gateway v1 (optional; defaults to operation context chain). */ chain?: ChainDefinition; }; /** * Deposit tokens on behalf of another address after approving. Balance is credited to `depositor`. * * Corresponds to `depositFor(address token, address depositor, uint256 value)`. */ depositFor: ActionParameters & { /** Token contract address. */ token: string; /** Address that will own the resulting balance. */ depositor: string; /** Amount in token's smallest unit. */ value: bigint; /** Chain with Gateway v1 (optional; defaults to operation context chain). */ chain?: ChainDefinition; }; /** * Deposit with EIP-2612 permit (gasless approval via signature). * * Corresponds to `depositWithPermit(token, owner, value, deadline, signature)` (bytes) * or the overload with (v, r, s). Use `signature` for EIP-7597 (SCA); use (v, r, s) for EOA. */ depositWithPermit: ActionParameters & { /** Token contract address. */ token: string; /** Depositor's address (owner in permit). */ owner: string; /** Amount in token's smallest unit. */ value: bigint; /** Permit deadline (Unix timestamp) or max uint256 for no expiration. */ deadline: bigint; /** Signature as bytes (EIP-7597) or omit and use v, r, s. */ signature?: `0x${string}`; /** ECDSA v (when not using signature bytes). */ v?: number; /** ECDSA r (when not using signature bytes). */ r?: `0x${string}`; /** ECDSA s (when not using signature bytes). */ s?: `0x${string}`; /** Chain with Gateway v1 (optional; defaults to operation context chain). */ chain?: ChainDefinition; }; /** * Deposit with EIP-3009 transferWithAuthorization (receiveWithAuthorization). * * Corresponds to `depositWithAuthorization(token, from, value, validAfter, validBefore, nonce, signature)` * or the overload with (v, r, s). */ depositWithAuthorization: ActionParameters & { /** Token contract address. */ token: string; /** Depositor's address (from in authorization). */ from: string; /** Amount in token's smallest unit. */ value: bigint; /** Unix timestamp after which the authorization is valid. */ validAfter: bigint; /** Unix timestamp before which the authorization is valid. */ validBefore: bigint; /** Unique nonce (bytes32). */ nonce: `0x${string}`; /** Signature as bytes (EIP-7598) or omit and use v, r, s. */ signature?: `0x${string}`; /** ECDSA v (when not using signature bytes). */ v?: number; /** ECDSA r (when not using signature bytes). */ r?: `0x${string}`; /** ECDSA s (when not using signature bytes). */ s?: `0x${string}`; /** Chain with Gateway v1 (optional; defaults to operation context chain). */ chain?: ChainDefinition; }; /** * Grant spending rights to a delegate on the caller's Gateway account. * * Corresponds to `addDelegate(address token, address delegate)`. */ addDelegate: ActionParameters & { /** Token contract address (e.g. USDC). */ token: string; /** Address to authorize as a delegate. */ delegate: string; /** Chain with Gateway v1 (optional; defaults to operation context chain). */ chain?: ChainDefinition; }; /** * Revoke spending rights from a delegate on the caller's Gateway account. * * Corresponds to `removeDelegate(address token, address delegate)`. */ removeDelegate: ActionParameters & { /** Token contract address (e.g. USDC). */ token: string; /** Address to revoke as a delegate. */ delegate: string; /** Chain with Gateway v1 (optional; defaults to operation context chain). */ chain?: ChainDefinition; }; /** * Check whether an address is authorized as a delegate for a depositor's balance. * * Corresponds to `isAuthorizedForBalance(address token, address depositor, address addr)`. */ isDelegate: ActionParameters & { /** Token contract address (e.g. USDC). */ token: string; /** The depositor (balance owner) address. */ depositor: string; /** The address to check for delegate status. */ delegate: string; /** Chain with Gateway v1 (optional; defaults to operation context chain). */ chain?: ChainDefinition; /** EVM: specific block number to read state at (for finality-aware checks). */ blockNumber?: bigint; /** Solana: commitment level for the account read. */ commitment?: 'confirmed' | 'finalized'; }; /** * Start a delayed fund removal from a Gateway account. * * Corresponds to `initiateWithdrawal(address token, uint256 value)` (EVM) * or the `initiate_withdrawal` instruction (Solana). */ initiateWithdrawal: ActionParameters & { /** Token contract address (e.g. USDC). */ token: string; /** Amount in token's smallest unit. */ value: bigint; /** Chain with Gateway v1 (optional; defaults to operation context chain). */ chain?: ChainDefinition; }; /** * Complete a fund removal after the withdrawal delay has elapsed. * * Corresponds to `withdraw(address token)` (EVM) or the `withdraw` * instruction (Solana). No amount parameter -- the contract returns the * full pending withdrawal balance. */ withdraw: ActionParameters & { /** Token contract address (e.g. USDC). */ token: string; /** Chain with Gateway v1 (optional; defaults to operation context chain). */ chain?: ChainDefinition; }; /** * Read the pending withdrawal balance for a depositor. * * Corresponds to `withdrawingBalance(address token, address depositor)` (EVM) * or reading `withdrawing_amount` from the `GatewayDeposit` PDA (Solana). */ withdrawingBalance: ActionParameters & { /** Token contract address (e.g. USDC). */ token: string; /** The depositor whose pending withdrawal to query. */ depositor: string; /** Chain with Gateway v1 (optional; defaults to operation context chain). */ chain?: ChainDefinition; }; /** * Read the block number at which a pending withdrawal can be completed. * * Corresponds to `withdrawalBlock(address token, address depositor)` (EVM) * or reading `withdrawal_block` from the `GatewayDeposit` PDA (Solana). */ withdrawalBlock: ActionParameters & { /** Token contract address (e.g. USDC). */ token: string; /** The depositor whose withdrawal block to query. */ depositor: string; /** Chain with Gateway v1 (optional; defaults to operation context chain). */ chain?: ChainDefinition; }; /** * Execute gatewayBurn on the Gateway Wallet contract. * Burns tokens from a source chain as part of a cross-chain spend. * * Corresponds to `gatewayBurn(bytes calldataBytes, bytes signature)`. */ gatewayBurn: ActionParameters & { /** ABI-encoded burn intent calldata. */ calldataBytes: `0x${string}`; /** Signature over the burn intent(s). */ signature: `0x${string}`; /** Chain with Gateway v1 (optional; defaults to operation context chain). */ chain?: ChainDefinition; }; /** * Execute gatewayMint on the Gateway Minter contract. * Mints tokens on the destination chain to complete a cross-chain spend. * * Corresponds to `gatewayMint(bytes attestationPayload, bytes signature)`. */ gatewayMint: ActionParameters & { /** Attestation payload from the Gateway API. */ attestation: `0x${string}`; /** Signature over the attestation. */ signature: `0x${string}`; /** Chain with Gateway v1 (optional; defaults to operation context chain). */ chain?: ChainDefinition; }; /** * Sign burn intents using EIP-712 typed data (EVM) or binary encoding (Solana). * Returns the signature needed for the Gateway API transfer call. */ signBurnIntents: ActionParameters & { /** EIP-712 typed data for EVM, or binary-encoded data for Solana. */ typedData: unknown; /** Chain with Gateway v1 (optional; defaults to operation context chain). */ chain?: ChainDefinition; }; } /** * Native token-related action maps for the bridge kit. * * This module provides action definitions for native token operations. */ interface NativeActionMap { /** * Transfer native tokens directly from the wallet to another address. */ transfer: ActionParameters & { /** * The chain to transfer the native tokens on. */ chain?: ChainIdentifier$1; /** * The address to send the native tokens to. */ to: string; /** * The amount of native tokens to transfer. */ amount: bigint; }; /** * Get the native token balance (SOL, ETH, etc.) for a wallet address. */ balanceOf: ActionParameters & { /** * The address to check the native balance for. If not provided, it will be * automatically derived from the adapter context. */ walletAddress?: string | undefined; }; } /** * Central registry of all available action namespaces and their operations. * * Define the complete action map structure used throughout the bridge kit. * Each top-level key represents a namespace (e.g., 'token', 'usdc') containing * related operations. The structure supports arbitrary nesting depth through * the recursive utility types provided in this module. * * @remarks * This interface serves as the foundation for type-safe action dispatching * and provides compile-time validation of action keys and payload types. * All action-related utility types derive from this central definition. * * @see {@link ActionKeys} for dot-notation action paths * @see {@link ActionPayload} for extracting payload types */ interface ActionMap { /** CCTP-specific operations with automatic address resolution. */ readonly cctp: CCTPActionMap; /** Gateway Wallet operations, versioned (e.g. gateway.v1.deposit). */ readonly gateway: GatewayActionMap; /** Native token operations (ETH, SOL, MATIC, etc.). */ readonly native: NativeActionMap; /** General token operations requiring explicit token addresses. */ readonly token: TokenActionMap; /** USDC-specific operations with automatic address resolution. */ readonly usdc: USDCActionMap; /** USDT-specific operations with automatic address resolution. */ readonly usdt: USDTActionMap; /** Swap operations for DEX aggregator integrations. */ readonly swap: SwapActionMap; /** Earn operations that execute service-signed payloads via the adapter contract. */ readonly earn: EarnActionMap; } /** * Determine if a type represents an action parameter object (leaf node). * * Check whether a type extends the ActionParameters interface, which provides * an explicit marker for identifying action parameter objects versus namespace * containers that should be traversed during type recursion. * * @typeParam T - The type to examine for parameter object characteristics * * @remarks * This utility type provides deterministic leaf detection for the recursive * type system. By requiring all action parameter objects to extend the * ActionParameters interface, we eliminate the need for property name * heuristics and make the system more maintainable. * * @see {@link ActionParameters} for the base interface * @see {@link NestedKeys} for usage in path extraction */ type IsActionParameterObject = T extends ActionParameters ? true : false; /** * Recursively extract all nested keys from an object type as dot-notation string literals. * * Traverse object structures of arbitrary depth and generate string literal * types representing all possible paths through the structure using dot * notation. Stop recursion when encountering action parameter objects (leaves). * * @typeParam T - The object type to extract nested keys from * * @remarks * This type is the foundation for generating type-safe action paths in * dot notation. It automatically adapts to changes in the ActionMap * structure and supports unlimited nesting depth for future extensibility. * * The recursion stops when it encounters objects that match the * {@link IsActionParameterObject} criteria, ensuring that only valid * action paths are generated. * * @see {@link ActionKeys} for ActionMap-specific paths * @see {@link NestedValue} for extracting types at specific paths * @see {@link IsActionParameterObject} for leaf detection logic */ type NestedKeys = { [K in Extract]: IsActionParameterObject extends true ? K : T[K] extends object ? `${K}.${NestedKeys}` : never; }[Extract]; /** * Recursively extract the value type at a given dot-notation path. * * Navigate through nested object types using a dot-notation string path * and return the type of the value at that location. Parse the path * recursively by splitting on dots and traversing the object structure. * * @typeParam T - The object type to navigate through * @typeParam K - The dot-notation path as a string literal type * * @remarks * This utility type enables type-safe access to deeply nested object * properties using dot notation paths. It forms the foundation for * extracting payload types from action paths in the ActionMap. * * @see {@link ActionPayload} for ActionMap-specific value extraction * @see {@link NestedKeys} for generating valid path types */ type NestedValue = K extends `${infer First}.${infer Rest}` ? First extends keyof T ? NestedValue : never : K extends keyof T ? T[K] : never; /** * Union type of all nested action keys in dot notation. * * Generate string literal types for all possible action paths in the * ActionMap structure. Automatically adapt to changes in the ActionMap * and support arbitrary levels of nesting for future extensibility. * * @remarks * This type serves as the canonical source for all valid action identifiers * in the bridge kit. It ensures compile-time validation of action keys * and enables type-safe action dispatching throughout the application. * * @see {@link ActionPayload} for extracting parameter types * @see {@link NamespaceActions} for namespace-specific actions * @see {@link ActionMap} for the underlying structure */ type ActionKeys = NestedKeys; /** * Extract the payload type for a specific action based on its dot-notation key. * * Resolve the parameter type for any action by providing its complete path * in dot notation. Leverage the recursive NestedValue type to navigate to * the correct payload type regardless of nesting depth. The internal * ActionParameters marker is automatically removed from the result. * * @typeParam T - The action key in dot notation (must extend ActionKeys) * * @remarks * This utility type enables type-safe parameter passing for action * dispatching. It automatically infers the correct parameter shape * based on the action key, providing compile-time validation and * excellent IntelliSense support. * * The internal `__isActionParams` marker used for type system recursion * is automatically omitted from the resulting type, providing clean * parameter objects for consumers. * * @see {@link ActionKeys} for available action identifiers * @see {@link NestedValue} for the underlying path resolution logic */ type ActionPayload = Omit, '__isActionParams'>; /** * Type-safe action handler function signature for specific action types. * * Defines the contract for functions that process action payloads and return * prepared chain requests. Each handler is strongly typed to accept only the * payload structure corresponding to its specific action key. * * @typeParam TActionKey - The specific action key this handler processes. * @param params - The action payload matching the specified action key. * @param context - The resolved operation context with concrete chain and address values. * @returns A promise resolving to a prepared chain request. * * @example * ```typescript * import type { ActionHandler } from '@core/adapter' * * const depositHandler: ActionHandler<'cctp.v2.depositForBurn'> = async (params, context) => { * // context is always defined and has concrete chain and address values * console.log(context.chain.name); * console.log(context.address); * // ... handler logic ... * return preparedRequest; * } * ``` */ type ActionHandler = (params: ActionPayload, context: ResolvedOperationContext) => Promise; /** * Type-safe mapping of all available action keys to their corresponding handlers. * * This type defines a registry object where each key is a valid action key * (as defined by {@link ActionKeys}) and each value is an {@link ActionHandler} * capable of processing the payload for that action. This enables strongly-typed * handler registration and lookup for all supported actions in the App Kits. * * @remarks * Each handler is typed as {@link ActionHandler}, which means the handler * must accept the payload type for the specific action key it is registered under. * This provides type safety for handler registration and execution, but does not * enforce per-key handler parameterization at the type level. For stricter per-key * typing, consider using mapped types or generic registry patterns. * * @example * ```typescript * import type { ActionHandlers } from '@core/adapter' * import type { ActionHandler } from '@core/adapter' * * const handlers: ActionHandlers = { * 'cctp.v2.depositForBurn': async (params, resolved) => { * // params is correctly typed for 'cctp.v2.depositForBurn' * // resolved has concrete chain and address values * // ...handler logic... * }, * 'usdc.approve': async (params, resolved) => { * // params is correctly typed for 'usdc.approve' * // resolved has concrete chain and address values * // ...handler logic... * } * } * ``` */ type ActionHandlers = { [K in ActionKeys]?: ActionHandler; }; /** * Type-safe registry for managing and executing blockchain action handlers. * * Provides a centralized system for registering action handlers with full * TypeScript type safety, ensuring that handlers can only be registered * with compatible action keys and payload types. Supports both individual * handler registration and batch registration operations. * * @remarks * The registry uses a Map internally for O(1) lookups and maintains type * safety through generic constraints and careful type assertions. All * type assertions are validated at registration time to ensure runtime * type safety matches compile-time guarantees. */ declare class ActionRegistry { readonly actionHandlers: Map>; /** * Register a type-safe action handler for a specific action key. * * Associates an action handler function with its corresponding action key, * ensuring compile-time type safety between the action and its expected * payload structure. The handler will be available for execution via * {@link executeAction}. * * @typeParam TActionKey - The specific action key being registered. * @param action - The action key to register the handler for. * @param handler - The handler function for processing this action type. * @returns Void. * * @throws Error When action parameter is not a valid string. * @throws TypeError When handler parameter is not a function. * * @example * ```typescript * import { ActionRegistry } from '@core/adapter' * import type { ActionHandler } from '@core/adapter' * * const registry = new ActionRegistry() * * // Register a CCTP deposit handler * const depositHandler: ActionHandler<'cctp.v2.depositForBurn'> = async (params, resolved) => { * console.log('Processing deposit:', params.amount) * return { * chainId: params.chainId, * data: '0x...', * to: '0x...', * value: '0' * } * } * * registry.registerHandler('cctp.v2.depositForBurn', depositHandler) * ``` */ registerHandler(action: TActionKey, handler: ActionHandler): void; /** * Register multiple action handlers in a single operation. * * Efficiently register multiple handlers from a record object, where keys * are action identifiers and values are their corresponding handler * functions. Provides a convenient way to bulk-register handlers while * maintaining type safety. * * @param handlers - A record mapping action keys to their handler functions. * @returns Void. * * @throws {Error} When handlers parameter is not a valid object. * @throws {Error} When any individual handler registration fails. * * @example * ```typescript * import { ActionRegistry } from '@core/adapter' * import type { ActionHandler, ActionHandlers } from '@core/adapter' * * const registry = new ActionRegistry() * * // Register multiple handlers at once * const tokenHandlers: ActionHandlers = { * 'token.approve': async (params, resolved) => ({ * chainId: resolved.chain, * data: '0x095ea7b3...', * to: params.tokenAddress, * value: '0' * }), * 'token.transfer': async (params, resolved) => ({ * chainId: resolved.chain, * data: '0xa9059cbb...', * to: params.tokenAddress, * value: '0' * }) * } * * registry.registerHandlers(tokenHandlers) * console.log('Registered multiple token handlers') * ``` */ registerHandlers(handlers: ActionHandlers): void; /** * Check whether a specific action is supported by this registry. * * Determine if a handler has been registered for the given action key. * Use this method to conditionally execute actions or provide appropriate * error messages when actions are not available. * * @param action - The action key to check for support. * @returns True if the action is supported, false otherwise. * * @throws {Error} When action parameter is not a valid string. * * @example * ```typescript * import { ActionRegistry } from '@core/adapter' * * const registry = new ActionRegistry() * * // Check if actions are supported before attempting to use them * if (registry.supportsAction('token.approve')) { * console.log('Token approval is supported') * } else { * console.log('Token approval not available') * } * * // Conditional logic based on support * const action = 'cctp.v2.depositForBurn' * if (registry.supportsAction(action)) { * // Safe to execute * console.log(`${action} is available`) * } else { * console.warn(`${action} is not registered`) * } * ``` */ supportsAction(action: ActionKeys): boolean; /** * Execute a registered action handler with type-safe parameters. * * Look up and execute the handler associated with the given action key, * passing the provided parameters and context, returning the resulting prepared * chain request. TypeScript ensures the parameters match the expected * structure for the specified action. * * @typeParam TActionKey - The specific action key being executed. * @param action - The action key identifying which handler to execute. * @param params - The parameters to pass to the action handler. * @param context - The resolved operation context with concrete chain and address values. * @returns A promise resolving to the prepared chain request. * @throws {KitError} When the handler execution fails with a structured error. * @throws {Error} When no handler is registered for the specified action. * @throws {Error} When the handler execution fails with an unstructured error. * * @example * ```typescript * import { ActionRegistry } from '@core/adapter' * import type { ChainEnum } from '@core/chains' * * const registry = new ActionRegistry() * * // First register a handler * registry.registerHandler('token.approve', async (params, context) => ({ * chainId: context.chain, // Always defined * data: '0x095ea7b3...', * to: params.tokenAddress, * value: '0' * })) * * // Execute the action with resolved context (typically called from adapter.prepareAction) * const resolvedContext = { chain: 'Base', address: '0x123...' } * const result = await registry.executeAction('token.approve', { * chainId: ChainEnum.Ethereum, * tokenAddress: '0xA0b86a33E6441c8C1c7C16e4c5e3e5b5e4c5e3e5b5e4c5e', * delegate: '0x1234567890123456789012345678901234567890', * amount: '1000000' * }, resolvedContext) * * console.log('Transaction prepared:', result.data) * ``` */ executeAction(action: TActionKey, params: ActionPayload, context: ResolvedOperationContext): Promise; } /** * Defines the capabilities of an adapter, including address handling patterns and supported chains. * * @interface TAdapterCapabilities * @category Types * @description * This interface specifies how an adapter manages address control and which blockchain networks it supports. * It is used for capability discovery, validation, and to inform consumers about the adapter's operational model. * * The `addressContext` property determines both address selection behavior and bridge API requirements: * - `'user-controlled'`: User controls addresses through wallet UI, address optional in operations * - `'developer-controlled'`: Service manages addresses programmatically, address required in operations * * @example * ```typescript * // Browser wallet adapter (user-controlled) * const capabilities: AdapterCapabilities = { * addressContext: 'user-controlled', // User selects address in wallet UI * supportedChains: [Ethereum, Base, Polygon] * } * * // Enterprise provider adapter (developer-controlled) * const capabilities: AdapterCapabilities = { * addressContext: 'developer-controlled', // Address must be specified per operation * supportedChains: [Ethereum, Base, Solana] * } * ``` */ interface AdapterCapabilities { /** * Defines who controls address selection for wallet operations. * * - `'user-controlled'`: User controls addresses through wallet UI (browser wallets, hardware wallets) * - Address is implicit in bridge operations (uses wallet's current address) * - Adapter may listen for accountsChanged/chainChanged events * - Suitable for MetaMask, Coinbase Wallet, WalletConnect, private keys, etc. * * - `'developer-controlled'`: Service manages addresses programmatically (enterprise providers) * - Address must be explicitly provided in bridge operations * - No event listening (addresses controlled programmatically) * - Suitable for Fireblocks, Circle Wallets, institutional custody, etc. */ addressContext: 'user-controlled' | 'developer-controlled'; /** * The set of blockchain networks this adapter supports. * Used for validation, capability discovery, and to restrict operations to supported chains. * * @remarks * Typed `readonly` to match the `@core/adapter-base` `AdapterCapabilities` * shape, so the /next adapters (which preserve `readonly` capabilities per * PR #853 A1) remain structurally assignable to this legacy `Adapter` * contract. The collection is only ever read, never mutated. */ supportedChains: readonly ChainDefinition[]; } /** * Abstract class defining the standard interface for an adapter that interacts with a specific blockchain. * * An `Adapter` is responsible for encapsulating chain-specific logic necessary to * perform operations like sending transactions, querying balances, or interacting with smart contracts. * Implementations of this class will provide concrete logic for a particular blockchain protocol. * * This abstraction allows the App Kit to work with multiple blockchains in a uniform way. * * @typeParam TAdapterCapabilities - The adapter capabilities type for compile-time address validation. * When provided, enables strict typing of operation context based on the adapter's address control model. */ declare abstract class Adapter { /** * The type of the chain for this adapter. * * - For concrete adapters, this should be a real chain type (e.g., `'evm'`, `'solana'`, etc.) from the ChainType union. * - For hybrid adapters (adapters that route to concrete adapters supporting multiple ecosystems), * set this property to the string literal `'hybrid'`. * * Note: `'hybrid'` is not a legal ChainType and should only be used as a marker for multi-ecosystem adapters. * Hybrid adapters do not interact directly with any chain, but instead route requests to a concrete underlying adapter. * * @example * // For an EVM-only adapter: * chainType = 'evm' * * // For a hybrid adapter: * chainType = 'hybrid' */ abstract chainType: ChainType | 'hybrid'; /** * Capabilities of this adapter, defining address control model and supported chains. * * This property determines how the adapter behaves, especially for address selection * and bridge API requirements. The `addressContext` must match the adapter's type parameter. * * @remarks * The `addressContext` value must align with the adapter's generic type parameter for proper * type safety in bridge operations. * * @example * ```typescript * // User-controlled adapter (private key, browser wallet) * capabilities = { * addressContext: 'user-controlled', // Address implicit in bridge operations * supportedChains: [Ethereum, Base, Polygon] * } * * // Developer-controlled adapter (enterprise provider) * capabilities = { * addressContext: 'developer-controlled', // Address required in bridge operations * supportedChains: [Ethereum, Base, Solana] * } * ``` */ capabilities?: TAdapterCapabilities; /** * Registry of available actions for this adapter. * * The {@link ActionRegistry} provides a catalog of supported operations * (such as token transfers, approvals, etc.) that can be performed by this adapter * on the connected blockchain. This enables dynamic discovery and invocation * of chain-specific or cross-chain actions in a type-safe manner. * * @readonly */ readonly actionRegistry: ActionRegistry; /** * Prepares (but does not execute) an action for the connected blockchain. * * This method looks up the appropriate action handler for the given action key * and prepares the transaction request using the provided parameters. The returned * {@link PreparedChainRequest} allows developers to estimate gas costs and execute * the transaction at a later time, enabling pre-flight simulation and deferred execution. * * **Compile-time Address Validation**: When used with typed adapters that have capabilities, * this method enforces address requirements at compile time: * - **User-controlled adapters**: The `address` field is forbidden in the context * - **Developer-controlled adapters**: The `address` field is required in the context * - **Legacy adapters**: The `address` field remains optional for backward compatibility * * @remarks * This method does not send any transaction to the network. Instead, it returns a * prepared request object with `estimate()` and `execute()` methods, allowing * developers to inspect, simulate, or submit the transaction as needed. * * @param action - The action key identifying which handler to use for preparation. * @param params - The parameters to pass to the action handler. * @param ctx - Operation context with compile-time validated address requirements based on adapter capabilities. * @returns A promise that resolves to a {@link PreparedChainRequest} for estimation and execution. * @throws Error If the specified action key does not correspond to a registered handler. * @throws Error If the provided parameters are invalid for the action. * @throws Error If the operation context cannot be resolved. * * @example * ```typescript * // User-controlled adapter (address forbidden) * const userAdapter: Adapter<{ addressContext: 'user-controlled', supportedChains: [] }> * await userAdapter.prepareAction('token.approve', params, { * chain: 'Ethereum' * // address: '0x123...' // ❌ TypeScript error: address not allowed * }) * * // Developer-controlled adapter (address required) * const devAdapter: Adapter<{ addressContext: 'developer-controlled', supportedChains: [] }> * await devAdapter.prepareAction('token.approve', params, { * chain: 'Ethereum', * address: '0x123...' // ✅ Required for developer-controlled * }) * ``` */ prepareAction(action: TActionKey, params: ActionPayload, ctx: OperationContext): Promise; /** * Prepares a transaction for future gas estimation and execution. * * This method should handle any preliminary steps required before a transaction * can be estimated or sent. This might include things like serializing transaction * data, but it should NOT yet send anything to the network. * * The returned object contains two functions: * - `estimate()`: Asynchronously calculates and returns the {@link EstimatedGas} for the prepared transaction. * - `execute()`: Asynchronously executes the prepared transaction and returns a promise that resolves * with the transaction result (e.g., a transaction hash, receipt, or other chain-specific response). * * **Compile-time Address Validation**: When used with typed adapters that have capabilities, * this method enforces address requirements at compile time: * - **User-controlled adapters**: The `address` field is forbidden in the context * - **Developer-controlled adapters**: The `address` field is required in the context * - **Legacy adapters**: The `address` field remains optional for backward compatibility * * @remarks * The specific parameters for `prepare` might vary greatly between chain implementations. * Consider defining a generic type or a base type for `transactionRequest` if common patterns emerge, * or allow `...args: any[]` if extreme flexibility is needed by implementers. * For this abstract definition, we keep it parameter-less, assuming implementations will add specific * parameters as needed for their `prepare` method (e.g. `prepare(txDetails: MyChainTxDetails)`). * * @param params - The prepared chain request parameters for the specific blockchain. * @param ctx - Operation context with compile-time validated address requirements based on adapter capabilities. * @returns An object containing `estimate` and `execute` methods for the prepared transaction. * * @example * ```typescript * // User-controlled adapter (address forbidden) * const userAdapter: Adapter<{ addressContext: 'user-controlled', supportedChains: [] }> * await userAdapter.prepare(params, { * chain: 'Ethereum' * // address: '0x123...' // ❌ TypeScript error: address not allowed * }) * * // Developer-controlled adapter (address required) * const devAdapter: Adapter<{ addressContext: 'developer-controlled', supportedChains: [] }> * await devAdapter.prepare(params, { * chain: 'Ethereum', * address: '0x123...' // ✅ Required for developer-controlled * }) * ``` */ abstract prepare(params: PreparedChainRequestParams, ctx: OperationContext): Promise; /** * Retrieves the public address of the connected wallet. * * This address is used as the default sender for transactions * and interactions initiated by this adapter. * * @param chain - The chain to use for address resolution. * @returns A promise that resolves to the blockchain address as a string. */ abstract getAddress(chain: ChainDefinition): Promise; /** * Switches the adapter to operate on the specified chain. * * This abstract method must be implemented by concrete adapters to handle their specific * chain switching logic. The behavior varies by adapter type: * - **Private key adapters**: Recreate clients with new RPC endpoints * - **Browser wallet adapters**: Request chain switch via EIP-1193 or equivalent * - **Multi-entity adapters**: Typically a no-op (operations are contextual) * * @param chain - The target chain to switch to. * @returns A promise that resolves when the chain switch is complete. * @throws When the chain switching fails or is not supported. * * @remarks * This method is called by `ensureChain()` after validation is complete. * Implementations should focus only on the actual switching logic, not validation. * * @example * ```typescript * // EVM adapter implementation * protected async switchToChain(chain: ChainDefinition): Promise { * if (chain.type !== 'evm') { * throw new Error('Only EVM chains supported') * } * await this.recreateWalletClient(chain) * } * * // Multi-entity adapter implementation * protected async switchToChain(chain: ChainDefinition): Promise { * // No-op - operations are contextual * return * } * ``` */ abstract switchToChain(chain: ChainDefinition): Promise; /** * Ensures the adapter is operating on the specified chain, switching if necessary. * * This method provides a unified interface for establishing chain preconditions across different adapter types. * The behavior varies based on the adapter's capabilities: * - **Private key adapters**: Recreate clients with new RPC endpoints * - **Browser wallet adapters**: Request chain switch via EIP-1193 or equivalent * - **Multi-entity adapters**: Validate chain support (operations are contextual) * * @param chain - The target chain for operations. * @returns A promise that resolves when the adapter is operating on the specified chain. * @throws When the target chain is not supported or chain switching fails. * * @remarks * This method always calls `switchToChain()` to ensure consistency across all adapter types. * The underlying implementations handle idempotent switching efficiently (e.g., browser wallets * gracefully handle switching to the current chain, private key adapters recreate lightweight clients). * * @example * ```typescript * // Private key adapter - switches chains seamlessly * await privateKeyAdapter.ensureChain(Base) * * // Browser wallet - requests user to switch chains * await metamaskAdapter.ensureChain(Polygon) * * // Multi-entity adapter - validates chain is supported * await circleWalletsAdapter.ensureChain(Ethereum) * ``` */ ensureChain(targetChain: ChainDefinition): Promise; /** * Validate that the target chain is supported by this adapter. * * @param targetChain - The chain to validate. * @throws KitError with INVALID_CHAIN code if the chain is not supported by this adapter. */ validateChainSupport(targetChain: ChainDefinition): void; /** * Waits for a transaction to be mined and confirmed on the blockchain. * * This method should block until the transaction is confirmed on the blockchain. * The response includes comprehensive transaction details for the confirmed transaction. * * @param txHash - The hash of the transaction to wait for. * @param config - Optional configuration for waiting behavior including timeout and confirmations. * @param chain - The chain definition where the transaction was submitted. * @returns Promise resolving to comprehensive transaction details. */ abstract waitForTransaction(txHash: string, config: WaitForTransactionConfig | undefined, chain: ChainDefinition): Promise; /** * Calculate the total transaction fee including compute cost and buffer for the configured chain. * * This method computes the fee by multiplying the base compute units by the current * fee rate, then adds a configurable buffer to account for fee fluctuations and ensure * transaction success. The buffer is specified in basis points (1 basis point = 0.01%). * * @param baseComputeUnits - The base compute units for the transaction (gas for EVM, compute units for Solana, etc.). * @param bufferBasisPoints - The buffer to add as basis points (e.g., 500 = 5%). Defaults to implementation-specific value. * @param chain - The chain definition to calculate fees for. * @returns A promise that resolves to the total transaction fee as a bigint. */ abstract calculateTransactionFee(baseComputeUnits: bigint, bufferBasisPoints: bigint | undefined, chain: ChainDefinition): Promise; /** * Get the decimal places for a token address on a given chain. * * This method fetches the number of decimal places from a token contract. * Different chain types implement this differently: * - EVM: Calls the `decimals()` function on ERC-20 contracts * - Solana: Reads the `decimals` field from the SPL token mint account * * @param tokenAddress - The token contract address (EVM) or mint address (Solana) * @param chain - The chain definition where the token is deployed * @returns Promise resolving to the number of decimal places for the token * @throws Error when the token contract doesn't exist or decimals cannot be fetched * * @example * ```typescript * import { EthersAdapter } from '@circle-fin/adapter-ethers-v6' * import { Ethereum } from '@core/chains' * * const adapter = new EthersAdapter({ signer }) * * // Fetch decimals for DAI token * const decimals = await adapter.getTokenDecimals( * '0x6B175474E89094C44Da98b954EedeAC495271d0F', * Ethereum * ) * console.log(decimals) // 18 * ``` */ abstract getTokenDecimals(tokenAddress: string, chain: ChainDefinition): Promise; } /** * Set an application-level identifier prefix for all HTTP requests. * * This allows applications to identify themselves in the user agent string, * which is useful for tracking and analytics at the application level. * * @param prefix - Application identifier with version, e.g., "my-app/1.0.0" * * @example * ```typescript * import { setExternalPrefix } from '\@circle-fin/bridge-kit' * * setExternalPrefix('my-dapp/2.1.0') * // All subsequent HTTP requests will include this prefix * ``` */ declare const setExternalPrefix: (prefix: string) => void; /** * Module augmentation to register known token symbols. * * @remarks * This file augments the `TokenSymbolRegistry` interface to provide * type-safe autocomplete for built-in tokens. * * When imported, TypeScript will recognize 'USDC' as a valid * `TokenSymbol` value with autocomplete support. * * Other packages or applications can create their own augmentations * to add additional tokens. * * @example * ```typescript * import '@core/tokens' // Automatically includes this augmentation * * const symbol: TokenSymbol = 'USDC' // ✓ Autocomplete shows USDC * ``` */ declare module './types' { /** * Module augmentation: Adds known token symbols as valid keys * to the TokenSymbolRegistry interface. * * Keys are explicitly listed to ensure IDE autocomplete works properly. */ interface TokenSymbolRegistry { USDC: true; USDT: true; EURC: true; DAI: true; USDE: true; PYUSD: true; WETH: true; WBTC: true; WSOL: true; WAVAX: true; WPOL: true; ETH: true; POL: true; PLUME: true; MON: true; cirBTC: true; } } /** * Module augmentation to register Blockchain enum values as ChainIdentifiers. * * @remarks * This file augments the `ChainRegistry` interface to provide type-safe * autocomplete for all `Blockchain` enum values from `@core/chains`. * * When this augmentation is imported (via `@core/tokens`), TypeScript will * recognize all blockchain identifiers as valid `ChainIdentifier` values * with IDE autocomplete support. * * The `Blockchain` enum values are converted to their string representations, * enabling both enum values and string literals to be accepted as chain identifiers. * * @example * ```typescript * import { Blockchain } from '@core/chains' * import type { ChainIdentifier } from '@core/tokens' * * // Using enum value * const chain1: ChainIdentifier = Blockchain.Ethereum * * // Using string literal (with autocomplete!) * const chain2: ChainIdentifier = 'Base' * * // Arbitrary strings also work (escape hatch for custom chains) * const chain3: ChainIdentifier = 'my-custom-chain' * ``` */ declare module './types' { /** * Module augmentation: Adds all Blockchain enum values as valid keys * to the ChainRegistry interface for type-safe chain identifier support. * * This ensures both enum property access (e.g., Blockchain.Ethereum) and plain * string literals (e.g., 'Ethereum') are accepted by TypeScript as chain keys, * providing robust autocomplete and error checking. * * NOTE: * - This interface intentionally has no body. It merges a mapped Record type * into ChainRegistry solely for type augmentation. * - This empty-body construct is a necessary TypeScript idiom for module * augmentation with Record types—directly listing mapped keys is not * feasible in interface extensions. * * eslint-disable-next-line directives below suppress linter complaints about * the empty interface/mapping, which are benign and required for this pattern. */ interface ChainRegistry extends Record<`${Blockchain}`, true> { } } /** * Creates a union type that preserves IDE autocomplete for known literals * while still accepting any string at runtime. * * @remarks * This pattern uses `Record` (an empty record type) to prevent * TypeScript from widening string literals to just `string`. This gives us * the best of both worlds: autocomplete for known values and flexibility * for arbitrary strings. * * @typeParam T - The known string literal union to preserve. */ type LiteralUnion = T | (string & Record); /** * Registry for known chain identifiers (augmentation target). * * @remarks * This empty interface exists solely for module augmentation. Extend it to * register chain identifiers for type-safe token definitions. * * **Why an interface?** TypeScript only allows module augmentation on * interfaces, not type aliases. * * **Note:** This is NOT the EVM "chain ID" (numeric like 1 for Ethereum). * It's a human-readable identifier like "Ethereum", "Solana", "Base". * * **Usage** * * Without augmentation, `ChainIdentifier` defaults to `string`. * With `@core/chains` imported, you get autocomplete for all `Blockchain` values. * * **Custom Chains** * * ```typescript * declare module '@core/tokens' { * interface ChainRegistry { * MyChain: true * MyTestnet: true * } * } * // Now ChainIdentifier includes 'MyChain' | 'MyTestnet' | ... * ``` * * The value (`true`) is a placeholder—only the keys matter. * * NOTE: The eslint-disable below suppresses warnings about empty interfaces. * This is intentional—the interface exists solely as an augmentation target. */ interface ChainRegistry { } /** * Union of all registered chain identifiers. * * @remarks * Derived from `ChainRegistry` keys: * - Without augmentation: `string` * - With `@core/chains`: `'Ethereum' | 'Solana' | ...` plus any string * * Uses `LiteralUnion` to preserve IDE autocomplete while allowing * arbitrary strings at runtime. * * @example * ```typescript * const chain: ChainIdentifier = 'Ethereum' // Autocomplete works * const custom: ChainIdentifier = 'my-chain' // Also valid * ``` */ type ChainIdentifier = keyof ChainRegistry extends never ? string : LiteralUnion>; /** * Maps chain identifiers to their token locators. * * @remarks * The key is a chain identifier (type-safe when `KnownChainIdentifiers` is * augmented). This enables a single token definition to work across chains. * * When `@core/chains` is imported, you get autocomplete for known chains * like `Ethereum`, `Base`, `Solana`, etc. * * @example * ```typescript * import { Blockchain } from '@core/chains' * * const usdcLocators: ChainLocatorMap = { * [Blockchain.Ethereum]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', * [Blockchain.Solana]: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', * [Blockchain.Base]: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', * } * * // Or with string keys (always works) * const locators: ChainLocatorMap = { * 'ethereum': '0xa0b86991...', * 'my-custom-chain': '0x1234...', * } * ``` */ type ChainLocatorMap = Record; /** * Complete definition of a token including metadata and chain locators. * * @remarks * This is the canonical representation of a token in the registry. * It includes the symbol, decimals, and chain-specific locators. * * @example * ```typescript * const usdc: TokenDefinition = { * symbol: 'USDC', * decimals: 6, * locators: { * [Blockchain.Ethereum]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', * [Blockchain.Solana]: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', * }, * } * ``` */ interface TokenDefinition { /** * The token symbol (e.g., "USDC", "EURC"). */ readonly symbol: string; /** * The default number of decimal places for the token. * Used when no chain-specific override exists in {@link chainDecimals}. * @example 6 for USDC, 18 for most ERC20 tokens */ readonly decimals: number; /** * Chain-specific locators for the token. * Keys are chain identifiers, values are the token address/locator on that chain. * Not all chains need to be present - tokens may only exist on a subset of chains. */ readonly locators: Partial; /** * Optional per-chain decimal overrides. * * Some tokens have different decimal places on different chains * (e.g., USDe is 18 decimals on EVM but 9 decimals on Solana). * When present, the value for a specific chain takes precedence * over the default {@link decimals}. */ readonly chainDecimals?: Partial>; } /** * A raw token locator selector with explicit decimals. * * @remarks * Use this form when working with arbitrary tokens not in the registry. * The `locator` is the chain-specific address, and `decimals` is required * unless using lenient mode. * * @example * ```typescript * // Selecting a custom token by address * const selector: RawTokenSelector = { * locator: '0x1234567890abcdef1234567890abcdef12345678', * decimals: 18, * } * ``` */ interface RawTokenSelector { /** * The chain-specific token locator (address, program ID, etc.). */ readonly locator: string; /** * The number of decimal places. * Required in strict mode, optional in lenient mode. */ readonly decimals?: number; } /** * Registry for known token symbols (augmentation target). * * @remarks * This empty interface exists solely for module augmentation. Extend it to * register token symbols for type-safe selection. * * **Why an interface?** TypeScript only allows module augmentation on * interfaces, not type aliases. * * **Usage** * * Without augmentation, `TokenSymbol` defaults to `string`. * * ```typescript * declare module '@core/tokens' { * interface TokenSymbolRegistry { * USDC: true * EURC: true * } * } * // Now TokenSymbol includes 'USDC' | 'EURC' | ... * ``` * * NOTE: The eslint-disable below suppresses warnings about empty interfaces. * This is intentional—the interface exists solely as an augmentation target. */ interface TokenSymbolRegistry { } /** * Union type of all registered token symbols. * * @remarks * This type is derived from the keys of `TokenSymbolRegistry`: * - **Without augmentation** — Simply `string` (any value) * - **With augmentation** — `'USDC' | 'USDT' | ...` plus any string * * Uses `LiteralUnion` to preserve autocomplete for known values while * still accepting any string at runtime. * * @example * ```typescript * // With symbols.augment imported - autocomplete works * const symbol: TokenSymbol = 'USDC' * * // Custom strings still accepted * const symbol: TokenSymbol = 'MY_TOKEN' * ``` */ type TokenSymbol = keyof TokenSymbolRegistry extends never ? string : LiteralUnion>; /** * Selector for identifying a token. * * @remarks * Can be one of: * - A symbol string (e.g., "USDC") - resolves from registry * - A raw locator object with explicit decimals - for arbitrary tokens * * Using a symbol is preferred when the token is in the registry, as it * automatically resolves decimals and the correct chain address. * * @example * ```typescript * // By symbol (preferred for known tokens) * const selector1: TokenSelector = 'USDC' * * // By raw locator (for arbitrary tokens) * const selector2: TokenSelector = { * locator: '0x1234...', * decimals: 18, * } * ``` */ type TokenSelector = TokenSymbol | RawTokenSelector; /** * The resolved token information after registry lookup. * * @remarks * This is the result of resolving a `TokenSelector` against a chain. * It always contains the locator and decimals, and optionally the symbol * if the token was resolved from the registry. */ interface ResolvedToken { /** * The token symbol, if known. * Present when resolved from registry, absent for raw locators. */ readonly symbol?: string; /** * The number of decimal places for the token. */ readonly decimals: number; /** * The chain-specific token locator (address, program ID, etc.). */ readonly locator: string; } /** * Interface for the token registry. * * @remarks * The registry is the sole source of truth for token information. * It supports both symbol-based and raw locator-based token selection. * * @example * ```typescript * import { createTokenRegistry } from '@core/tokens' * * // Create registry (includes built-in tokens like USDC) * const registry = createTokenRegistry() * * // Resolve by symbol * const usdc = registry.resolve('USDC', 'Ethereum') * console.log(usdc.locator) // '0xa0b86991...' * * // Resolve raw locator * const custom = registry.resolve({ locator: '0x...', decimals: 18 }, 'Ethereum') * ``` */ interface TokenRegistry { /** * Resolve a token selector to concrete token information for a chain. * * @param selector - The token to resolve (symbol or raw locator). * @param chainId - The chain identifier to resolve for. * @returns The resolved token information. * @throws When the token cannot be resolved (unknown symbol, missing decimals, etc.). */ resolve(selector: TokenSelector, chainId: ChainIdentifier): ResolvedToken; /** * Resolve a token by chain-specific locator (address/program ID). * * @param address - The token locator to resolve. * @param chainId - The chain identifier to resolve for. * @returns The resolved token information. * @throws When no registry token matches the locator on the chain. */ resolveByAddress(address: string, chainId: ChainIdentifier): ResolvedToken; /** * Get a token definition by symbol. * * @param symbol - The token symbol (e.g., "USDC"). * @returns The token definition, or undefined if not found. */ get(symbol: string): TokenDefinition | undefined; /** * Check if a symbol is registered. * * @param symbol - The token symbol to check. * @returns True if the symbol is in the registry. */ has(symbol: string): boolean; /** * Get all registered token symbols. * * @returns An array of registered symbol strings. */ symbols(): string[]; /** * Get all registered token definitions. * * @returns An array of all TokenDefinition objects in the registry. */ entries(): TokenDefinition[]; } /** * Valid recoverability values for error handling strategies. * * - FATAL errors are thrown immediately (invalid inputs, insufficient funds) * - RETRYABLE errors are returned when a flow fails to start but could work later * - RESUMABLE errors are returned when a flow fails mid-execution but can be continued */ declare const RECOVERABILITY_VALUES: readonly ["RETRYABLE", "RESUMABLE", "FATAL"]; /** * Error handling strategy for different types of failures. * * - FATAL errors are thrown immediately (invalid inputs, insufficient funds) * - RETRYABLE errors are returned when a flow fails to start but could work later * - RESUMABLE errors are returned when a flow fails mid-execution but can be continued */ type Recoverability = (typeof RECOVERABILITY_VALUES)[number]; /** * Array of valid error type values for validation. * Derived from ERROR_TYPES const object. */ declare const ERROR_TYPE_VALUES: ("INPUT" | "BALANCE" | "ONCHAIN" | "RPC" | "NETWORK" | "RATE_LIMIT" | "SERVICE" | "LIQUIDITY" | "UNKNOWN")[]; /** * Error type indicating the category of the error. */ type ErrorType = (typeof ERROR_TYPE_VALUES)[number]; /** * Structured error details with consistent properties for programmatic handling. * * This interface provides a standardized format for all errors in the * App Kits system, enabling developers to handle different error * types consistently and provide appropriate user feedback. * * @example * ```typescript * const error: ErrorDetails = { * code: 1001, * name: "INPUT_NETWORK_MISMATCH", * type: "INPUT", * recoverability: "FATAL", * message: "Source and destination networks must be different", * cause: { * trace: { sourceChain: "ethereum", destChain: "ethereum" } * } * } * ``` * * @example * ```typescript * const error: ErrorDetails = { * code: 9001, * name: "BALANCE_INSUFFICIENT_TOKEN", * type: "BALANCE", * recoverability: "FATAL", * message: "Insufficient USDC balance on Ethereum", * cause: { * trace: { token: "USDC", chain: "Ethereum" } * } * } * ``` */ interface ErrorDetails { /** Numeric identifier following standardized ranges (see error code registry) */ code: number; /** Human-readable ID (e.g., "INPUT_NETWORK_MISMATCH", "BALANCE_INSUFFICIENT_TOKEN") */ name: string; /** Error category indicating where the error originated */ type: ErrorType; /** Error handling strategy */ recoverability: Recoverability; /** User-friendly explanation with context */ message: string; /** Raw error details, context, or the original error that caused this one. */ cause?: { /** * Free-form error payload from the underlying system. * * The shape is **not uniform across error codes**: most codes set `trace` * to the raw underlying error, while a few set a structured wrapper object * `{ rawError, ...extras }` (e.g. `INPUT_AMOUNT_OUT_OF_RANGE` and * `LIQUIDITY_INSUFFICIENT` add `minAmount` / `maxAmount` / `token`). * Consumers must branch on `error.code` before reading structured fields off * `trace`, and should treat the raw error as the fallback for all other codes. */ trace?: unknown; }; } declare class KitError extends Error implements ErrorDetails { /** Numeric identifier following standardized ranges (1000+ for INPUT errors) */ readonly code: number; /** Human-readable ID (e.g., "NETWORK_MISMATCH") */ readonly name: string; /** Error category indicating where the error originated */ readonly type: ErrorType; /** Error handling strategy */ readonly recoverability: Recoverability; /** Raw error details, context, or the original error that caused this one. */ readonly cause?: { /** Free-form error payload from underlying system */ trace?: unknown; }; /** * Create a new KitError instance. * * @param details - The error details object containing all required properties. * @throws \{TypeError\} When details parameter is missing or invalid. */ constructor(details: ErrorDetails); } /** * Type guard to check if an error is a KitError instance. * * This guard enables TypeScript to narrow the type from `unknown` to * `KitError`, providing access to structured error properties like * code, name, and recoverability. * * @remarks * **Cross-bundle safety.** Each `dist/*` bundle that depends on * `@core/errors` ships its own compiled `KitError` class, so a bare * `instanceof KitError` check returns `false` for errors thrown by * code in a *different* bundle even though both classes are * structurally identical. This guard works across bundles by * checking the registry-symbol brand * (`Symbol.for('circle.KitError')`) the canonical * {@link KitError} constructor stamps onto every instance. * `instanceof` is kept as a fast first check for the common * single-bundle case. * * @param error - Unknown error to check * @returns True if error is KitError with proper type narrowing * * @example * ```typescript * import { isKitError } from '@core/errors' * * try { * await kit.bridge(params) * } catch (error) { * if (isKitError(error)) { * // TypeScript knows this is KitError * console.log(`Structured error: ${error.name} (${error.code})`) * } else { * console.log('Regular error:', error) * } * } * ``` */ declare function isKitError(error: unknown): error is KitError; /** * Checks if an error is a KitError with FATAL recoverability. * * FATAL errors indicate issues that cannot be resolved through retries, * such as invalid inputs, configuration problems, or business rule * violations. These errors require user intervention to fix. * * @param error - Unknown error to check * @returns True if error is a KitError with FATAL recoverability * * @example * ```typescript * import { isFatalError } from '@core/errors' * * try { * await kit.bridge(params) * } catch (error) { * if (isFatalError(error)) { * // Show user-friendly error message - don't retry * showUserError(error.message) * } * } * ``` */ declare function isFatalError(error: unknown): boolean; /** * Checks if an error is retryable. * * @remarks * Check order for KitError instances: * 1. If `recoverability === 'RETRYABLE'` or `recoverability === 'RESUMABLE'`, * return `true` immediately (priority check). * 2. Otherwise, check if `error.code` is in `DEFAULT_RETRYABLE_ERROR_CODES` (fallback check). * 3. Non-KitError instances always return `false`. * * This two-tier approach allows both explicit recoverability control and * backward-compatible code-based retry logic. * * RETRYABLE errors indicate transient failures that may succeed on * subsequent attempts, such as network timeouts or temporary service * unavailability. These errors are safe to retry after a delay. * * RESUMABLE errors indicate a multi-phase operation that completed some phases * before failing (for example, a token approval landed but the execution * transaction failed). They are also retryable — re-running the operation is * safe — but callers that have a kit-level `retry()` should prefer it so that * already-completed phases are skipped. * * @param error - Unknown error to check * @returns True if error is retryable * * @example * ```typescript * import { isRetryableError } from '@core/errors' * * try { * await kit.bridge(params) * } catch (error) { * if (isRetryableError(error)) { * // Implement retry logic with exponential backoff * setTimeout(() => retryOperation(), 5000) * } * } * ``` * * @example * ```typescript * import { isRetryableError, createNetworkConnectionError, KitError } from '@core/errors' * * // KitError with RETRYABLE recoverability (priority check) * const error1 = createNetworkConnectionError('Ethereum') * isRetryableError(error1) // true * * // KitError with default retryable code (fallback check) * const error2 = new KitError({ * code: 3002, // NETWORK_TIMEOUT - in DEFAULT_RETRYABLE_ERROR_CODES * name: 'NETWORK_TIMEOUT', * type: 'NETWORK', * recoverability: 'FATAL', // Not RETRYABLE * message: 'Timeout', * }) * isRetryableError(error2) // true (code 3002 is in default list) * * // KitError with non-retryable code and FATAL recoverability * const error3 = new KitError({ * code: 1001, * name: 'INPUT_NETWORK_MISMATCH', * type: 'INPUT', * recoverability: 'FATAL', * message: 'Invalid input', * }) * isRetryableError(error3) // false * * // KitError with RESUMABLE recoverability (partially-completed operation) * const error4 = new KitError({ * code: 8101, * name: 'EARN_EXECUTION_FAILED', * type: 'SERVICE', * recoverability: 'RESUMABLE', * message: 'Execution failed after approval', * }) * isRetryableError(error4) // true * * // Non-KitError * const error5 = new Error('Standard error') * isRetryableError(error5) // false * ``` */ declare function isRetryableError(error: unknown): boolean; /** * Type guard to check if error is KitError with INPUT type. * * INPUT errors represent validation failures, invalid parameters, * or user input problems. These errors are always FATAL and require * the user to correct their input before retrying. * * @param error - Unknown error to check * @returns True if error is KitError with INPUT type * * @example * ```typescript * import { isInputError } from '@core/errors' * * try { * await kit.bridge(params) * } catch (error) { * if (isInputError(error)) { * console.log('Validation error:', error.message) * showValidationUI() * } * } * ``` */ declare function isInputError(error: unknown): error is KitError; /** * Safely extracts error message from any error type. * * This utility handles different error types gracefully, extracting * meaningful messages from Error instances, string errors, or providing * a fallback for unknown error types. Never throws. * * @param error - Unknown error to extract message from * @returns Error message string, or fallback message * * @example * ```typescript * import { getErrorMessage } from '@core/errors' * * try { * await riskyOperation() * } catch (error) { * const message = getErrorMessage(error) * console.log('Error occurred:', message) * // Works with Error, KitError, string, or any other type * } * ``` */ declare function getErrorMessage(error: unknown): string; /** * Gets the error code from a KitError, or null if not applicable. * * This utility safely extracts the numeric error code from KitError * instances, returning null for non-KitError types. Useful for * programmatic error handling based on specific error codes. * * @param error - Unknown error to extract code from * @returns Error code number, or null if not a KitError * * @example * ```typescript * import { getErrorCode, InputError } from '@core/errors' * * try { * await kit.bridge(params) * } catch (error) { * const code = getErrorCode(error) * if (code === InputError.NETWORK_MISMATCH.code) { * // Handle network mismatch specifically * showNetworkMismatchHelp() * } * } * ``` */ declare function getErrorCode(error: unknown): number | null; /** * Context object representing a wallet and signing authority on a specific blockchain network. * * Combines a wallet or contract address, the blockchain it resides on, and the adapter (signer) * responsible for authorizing transactions. Used to specify the source or destination in cross-chain * transfer operations. * * @remarks * The `adapter` (signer) and `address` do not always have to belong to the same entity. For example, * in minting or withdrawal scenarios, the signing adapter may authorize a transaction that credits * funds to a different recipient address. This context is essential for cross-chain operations, * ensuring that both the address and the associated adapter are correctly paired with the intended * blockchain, but not necessarily with each other. * * @example * ```typescript * import type { WalletContext } from '@core/provider' * import { adapter, blockchain } from './setup' * * const wallet: WalletContext = { * adapter, * address: '0x1234...abcd', * chain: blockchain, * } * ``` */ interface WalletContext { /** * The adapter (signer) for the wallet on the specified chain. * * Responsible for authorizing transactions and signing messages on behalf of the wallet or * for a different recipient, depending on the use case. */ adapter: Adapter; /** * The wallet or contract address. * * Must be a valid address format for the specified blockchain. May differ from the adapter's * own address in scenarios such as relayed transactions or third-party minting. */ address: string; /** * The blockchain network where the wallet or contract address resides. * * Determines the context and format for the address and adapter. */ chain: TChainDefinition; } /** * Represents the context of an adapter used for cross-chain operations. * * An AdapterContext must always specify both the adapter and the chain explicitly. * The address field behavior is determined by the adapter's address control model: * * - **Developer-controlled adapters**: The `address` field is required because * each operation must explicitly specify which address to use. * - **User-controlled adapters**: The `address` field is forbidden because * the address is automatically resolved from the connected wallet or signer. * - **Legacy adapters**: The `address` field remains optional for backward compatibility. * * This ensures clear, debuggable code where the intended chain is always visible at the call site, * and address requirements are enforced at compile time based on adapter capabilities. * * @typeParam TAdapterCapabilities - The adapter capabilities type to derive address requirements from * @typeParam TChainIdentifier - The chain identifier type constraint (defaults to ChainIdentifier) * * @example * ```typescript * // Developer-controlled adapter (address required) * const devContext: AdapterContext<{ addressContext: 'developer-controlled', supportedChains: [] }> = { * adapter: myDevAdapter, * chain: 'Ethereum', * address: '0x123...' // Required * } * * // User-controlled adapter (address forbidden) * const userContext: AdapterContext<{ addressContext: 'user-controlled', supportedChains: [] }> = { * adapter: myUserAdapter, * chain: 'Ethereum' * // address: '0x123...' // TypeScript error: not allowed * } * ``` */ type AdapterContext = { /** The adapter instance for blockchain operations */ adapter: Adapter; /** The chain reference, which can be a ChainDefinition, Blockchain enum, or string literal */ chain: TChainIdentifier; } & AddressField>; /** * Represents a token amount with its symbol and value. * * Used throughout the swap system to consistently represent token quantities * with their associated token identifier. The amount is always in human-readable * decimal format (e.g., '99.5' for 99.5 tokens). * * @example * ```typescript * const tokenAmount: TokenAmount = { * token: 'USDT', * amount: '99.5' * } * ``` * * @example Native token * ```typescript * const nativeAmount: TokenAmount = { * token: 'ETH', * amount: '0.000285714285714285' * } * ``` */ interface TokenAmount { /** * The token symbol or identifier. * * Can be a stablecoin symbol (e.g., 'USDC', 'USDT'), native token symbol * (e.g., 'ETH', 'SOL'), or the 'NATIVE' placeholder. */ readonly token: string; /** * The amount in human-readable decimal format. * * Provided as a string to preserve precision for large or very small numbers. * * @example '99.5' for 99.5 tokens * @example '0.000001716776195841' for very small amounts */ readonly amount: string; } /** * Allowance strategy for token approvals during swap operations. * * Defines how token allowances should be granted to the swap contract: * - `permit`: Use EIP-2612 permit signature (gas-efficient, no approval transaction) * - `approve`: Traditional approval transaction * * The default strategy is `permit` with fallback to `approve` if permit is not supported. */ type AllowanceStrategy$1 = 'permit' | 'approve'; /** * Configuration options for swap operations. * * Controls swap behavior including allowance strategy, slippage tolerance, * minimum output amounts, custom fees, and kit identification. * * @example * ```typescript * import type { ServiceSwapConfig } from '@circle-fin/provider-stablecoin-service-swap' * * // Percentage-based fee * const config: ServiceSwapConfig = { * allowanceStrategy: 'permit', * slippageBps: 300, // 3% * stopLimit: '950000', // Minimum 0.95 USDC output * customFee: { * percentageBps: 1000, // 10% fee * recipientAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' * } * } * ``` * * @example * ```typescript * // Absolute amount fee (from callback) * const config: ServiceSwapConfig = { * allowanceStrategy: 'permit', * customFee: { * amount: '10000', // 0.01 USDC fee (absolute) * recipientAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' * } * } * ``` */ interface ServiceSwapConfig { /** * Strategy for granting token allowances to the swap contract. * * Defaults to 'permit' with fallback to 'approve'. */ allowanceStrategy?: AllowanceStrategy$1; /** * Maximum acceptable slippage in basis points (BPS). * * 1 BPS = 0.01%, so 300 BPS = 3% slippage. * Defaults to 300 BPS (3%). */ slippageBps?: number; /** * Minimum acceptable output amount in smallest units (stop-limit). * * If the estimated output falls below this value, the swap will fail. * Expressed as a string to avoid precision issues. */ stopLimit?: string; /** * Custom fee configuration for this swap. * * Supports two mutually exclusive approaches: * 1. Percentage-based: Use `percentageBps` field (simple) * 2. Absolute amount: Use `amount` field (from callback) * * If both are set, validation will fail. Transaction-level percentage * takes precedence over kit-level callback policy. */ customFee?: { /** * Fee percentage in basis points (NEW). * * 100 bps = 1%, 1000 bps = 10%, 10000 bps = 100% * * Service calculates fee using `estimatedAmount` for same-chain output fees * and the input amount for cross-chain swaps. * Must be greater than 0 and less than or equal to 10000 (maximum 100%). * Mutually exclusive with `amount`. * * @example 1000 // 10% fee */ percentageBps?: number; /** * Fee amount in smallest units (for callback results). * * Absolute fee amount calculated by callback function. * Mutually exclusive with `percentageBps`. * * @example '10000' // 0.01 USDC (6 decimals) */ amount?: string; /** * Address that will receive the developer's 90% fee share. * * Required whenever a custom fee is submitted to the provider. Optional at * the type level so SDK callback flows can represent partial fee state * before final validation. * * Must be valid on the fee payout chain: source chain for input-side fees * and cross-chain swaps, destination chain for same-chain output-side fees. * * @example '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' */ recipientAddress?: string; }; /** * Stablecoin Service Kit Key used to authenticate service-backed swap * requests. * * Treat this value as a credential. Do not log it, embed it in client-side * source, or expose it in telemetry. */ kitKey?: string; /** * DEX aggregator identifier used to source the swap route. * * @example 'lifi', 'paraswap' */ provider?: string; } /** * Parameters for initiating a swap operation through the Stablecoin Service. * * This type is used as the primary input to provider swap operations, allowing users to specify * the source context, input/output tokens, swap amount, destination address, and optional configuration. * * @typeParam TFromAdapterCapabilities - The adapter capabilities type for the source adapter. * * @example * ```typescript * import type { ServiceSwapParams } from '@circle-fin/provider-stablecoin-service-swap' * import { createAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * import { Ethereum } from '@core/chains' * * const adapter = createAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY, * }) * * const params: ServiceSwapParams = { * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', // Alias resolves to chain-specific address * tokenOut: 'USDT', // Alias resolves to chain-specific address * amountIn: '100500000', // 100.50 USDC in base units * to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', * config: { * slippageBps: 300, // 3% slippage * allowanceStrategy: 'permit' * } * } * ``` */ interface ServiceSwapParams { /** * The source adapter context (wallet and chain) for the swap. */ from: WalletContext; /** * The input token address or alias to swap from. * * **Supported formats:** * - Token alias: `"USDC"`, `"USDT"`, `"NATIVE"` (case-insensitive) * - EVM address: `'0x'` prefixed hex (e.g., `'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'`) * - Solana address: Base58-encoded (e.g., `'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'`) * * Token aliases are automatically resolved to the chain-specific contract address. * * @example 'USDC' // Recommended: use alias * @example '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' // Or full address */ tokenIn: string; /** * The output token address or alias to swap to. * * **Supported formats:** * - Token alias: `"USDC"`, `"USDT"`, `"NATIVE"` (case-insensitive) * - EVM address: `'0x'` prefixed hex (e.g., `'0xdAC17F958D2ee523a2206206994597C13D831ec7'`) * - Solana address: Base58-encoded (e.g., `'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'`) * * Token aliases are automatically resolved to the chain-specific contract address. * * @example 'USDT' // Recommended: use alias * @example '0xdAC17F958D2ee523a2206206994597C13D831ec7' // Or full address */ tokenOut: string; /** * The amount of input token to swap in base units. * * SwapKit converts human-readable amounts to base units before passing to the provider. * * @example '100500000' for 100.50 USDC (6 decimals) */ amountIn: string; /** * The destination address where the swapped tokens will be sent. * * @example '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' */ to: string; /** * Optional destination chain for cross-chain swaps. * * Defaults to `from.chain` for same-chain swaps. */ toChain?: TChainDefinition; /** * Optional configuration for swap behavior. * * If omitted, defaults will be used: * - allowanceStrategy: 'permit' (fallback to 'approve') * - slippageBps: 300 (3%) */ config?: ServiceSwapConfig; } /** * Individual fee entry in the swap operation. * * @example * ```typescript * const fee: ServiceSwapFee = { * token: 'USDC', * amount: '0.1', * type: 'provider' * } * ``` * * @example Developer fee with recipientAddress * ```typescript * const developerFee: ServiceSwapFee = { * token: 'USDC', * amount: '0.01', * type: 'developer', * recipientAddress: '0xFbC171f350319a2E06A80AE640CDd76dF084D1eF' * } * ``` */ interface ServiceSwapFee { /** * The token in which the fee is charged. * * @example 'USDC', 'USDT', 'ETH' */ readonly token: string; /** * The fee amount in decimal units (e.g., '0.1' for 0.1 USDC). * * Null if the fee could not be estimated. */ readonly amount: string | null; /** * The type of fee. * * - `'provider'`: Fees collected by the swap provider (e.g., LiFi). * - `'swap'`: Additional swap fees from the routing provider. * - `'gas'`: Estimated gas/transaction fee in native currency. * - `'developer'`: The developer's share of the custom fee. */ readonly type: 'provider' | 'swap' | 'gas' | 'developer'; /** * The wallet address that receives this fee portion. * * Present for `developer` fee types (the developer's configured recipientAddress). * Not present for `provider`, `swap`, or `gas` fee types since those recipient * addresses are not available from the proxy response. */ readonly recipientAddress?: string; } /** * Result from estimating a swap operation through the Stablecoin Service. * * @remarks * This interface represents the response payload returned by Circle's Stablecoin Service * after requesting a swap quote/estimate. It contains estimated outputs and fees. * The request parameters are not echoed back since the provider already has them. * * This type is specifically for the estimate operation. For actual swap execution results, * see {@link SwapResult}. * * This design aligns with the CCTP provider pattern where responses contain only * execution artifacts, not request echoes. * * @example * ```typescript * const result: EstimateResult = { * stopLimit: { token: 'USDT', amount: '99.5' }, * estimatedOutput: { token: 'USDT', amount: '99.5' }, * fees: [ * { token: 'USDC', amount: '0.002', type: 'provider' }, * { token: 'USDC', amount: '0.001', type: 'swap' } * ] * } * ``` */ interface EstimateResult { /** * Estimated minimum token out amount with token information. * * This represents the minimum amount of tokens the user should receive after * accounting for slippage. Amount is in human-readable decimal format. * * @example * ```typescript * stopLimit: { token: 'ETH', amount: '0.000001716776195841' } * ``` */ readonly stopLimit: TokenAmount; /** * Estimated output amount with token information. * * @remarks * Provided in human-readable decimal format (e.g., '0.995' for 0.995 tokens). * This represents the expected output amount from the swap based on current * market conditions. * * @example * ```typescript * estimatedOutput: { token: 'USDT', amount: '99.5' } * ``` */ readonly estimatedOutput: TokenAmount; /** * Detailed fee breakdown for the swap operation. * * @remarks * Includes both provider fees (charged by the DEX aggregator/protocol) * and kit fees (if configured). Amounts are in decimal format for display. */ readonly fees?: readonly ServiceSwapFee[]; } /** * Individual executed transaction in a swap operation. * * @remarks * Represents a single transaction executed as part of the swap flow. * For EIP-2612 supported tokens, only the swap transaction is included. * For non-EIP-2612 tokens, both approval and swap transactions are included. */ interface ExecutedTransaction { /** * The type of transaction executed. * * - 'approval': Token approval transaction (ERC20.approve) * - 'swap': The actual swap transaction */ readonly type: 'approval' | 'swap'; /** * The transaction hash for this executed transaction. */ readonly txHash: string; } /** * Terminal status values reported by the Stablecoin Service swap status API. */ type SwapTerminalStatus$1 = 'DONE' | 'FAILED' | 'NOT_FOUND'; /** * All status values reported by the Stablecoin Service swap status API. * * `'PENDING'` indicates the swap is still in-flight. */ type SwapStatus$1 = SwapTerminalStatus$1 | 'PENDING'; /** * Lifecycle snapshot for a swap. */ interface SwapProgress$1 { readonly status: SwapStatus$1; readonly substatus?: string; readonly substatusMessage?: string; } /** * Result from executing a swap operation through the Stablecoin Service. * * @remarks * This interface represents the result of executing a swap operation through * Circle's Stablecoin Service. It contains the execution artifacts and metadata * about the swap that was performed. * * This design aligns with the BridgeResult pattern from the provider level, * containing the original request parameters along with execution results. * * @example * ```typescript * const result: SwapResult = { * tokenIn: 'USDC', // Alias used in the swap * tokenOut: 'USDT', // Alias used in the swap * chainIn: Ethereum, * chainOut: Ethereum, * chain: Ethereum, // Deprecated alias for chainIn * amountIn: '100000000', // 100 USDC in base units * fromAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', * toAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', * config: { slippageBps: 300 }, * txHash: '0xabc123...', * executedTransactions: [ * { type: 'approval', txHash: '0xdef456...' }, // Only for non-EIP-2612 tokens * { type: 'swap', txHash: '0xabc123...' } * ], * fees: [ * { token: 'USDC', amount: '0.002' } * ], * progress: { status: 'DONE' }, * amountOut: '99500000' * } * ``` */ interface SwapResult$1 { /** * The input token identifier (alias like 'USDC' or address) that was swapped from. */ readonly tokenIn: string; /** * The output token identifier (alias like 'USDC' or address) that was swapped to. */ readonly tokenOut: string; /** * The source chain of the swap. * * The provider reports only the source chain. Destination-chain identity * (`chainIn`/`chainOut` on the kit's result) is composed by the kit from * the swap request, so it is not part of this contract. */ readonly chain: ChainDefinition; /** * The input amount that was swapped in base units (stringified bigint). */ readonly amountIn: string; /** * The address that initiated the swap. */ readonly fromAddress: string; /** * The address that received the swapped tokens. */ readonly toAddress: string; /** * The swap configuration that was used for this operation. */ readonly config?: ServiceSwapConfig; /** * The transaction hash for the executed swap. */ readonly txHash: string; /** * Array of all transactions executed during the swap operation. * * @remarks * For EIP-2612 supported tokens (USDC), this will contain only the swap transaction. * For non-EIP-2612 tokens, this will contain both approval and swap transactions. */ readonly executedTransactions: readonly ExecutedTransaction[]; /** * Detailed fee breakdown for the swap operation. Includes both provider * fees (charged by the DEX aggregator/protocol) and kit fees. */ readonly fees?: readonly ServiceSwapFee[]; /** * Lifecycle snapshot: `status`, `substatus`, and `substatusMessage`. * * Optional for backward compatibility: a provider that omits it is treated * as a synchronous same-chain completion (`{ status: 'DONE' }`). Any * provider performing asynchronous or cross-chain settlement MUST report it * (e.g. `{ status: 'PENDING', substatus: 'WAIT_DESTINATION_TRANSACTION' }`) * so the kit does not mistake an in-flight swap for a completed one. */ readonly progress?: SwapProgress$1; /** * Output amount in base units when the provider can confirm the result * inline. * * Populated for same-chain swaps when the best-effort status peek reaches * `DONE`. Omitted for cross-chain swaps and any result still in-flight. */ readonly amountOut?: string; } /** * Abstract interface for swapping providers that implement token swap protocols. * * @remarks * This interface defines the standard contract that all swapping providers must implement * to support cross-chain and same-chain token swaps. It provides a standardized way * to check route support, estimate costs, and execute swap operations across different * protocols and DEX aggregators. * * Swapping providers are responsible for: * - Validating swap parameters and route support * - Estimating gas costs and swap fees * - Executing the actual swap operations * - Handling protocol-specific logic and error conditions * * @example * ```typescript * import type { SwappingProvider } from '@circle-fin/provider-stablecoin-service-swap' * import { Ethereum } from '@core/chains' * * class CustomSwappingProvider implements SwappingProvider { * readonly name = 'CustomSwappingProvider' * * supportsRoute(tokenInAddress: string, tokenOutAddress: string, chain: ChainDefinition): boolean { * // Implementation specific logic * return true * } * * async estimate(params: ServiceSwapParams): Promise { * // Cost estimation logic * return { ... } * } * * async swap(params: ServiceSwapParams): Promise { * // Swap execution logic * return { ... } * } * } * ``` */ interface SwappingProvider { /** * The name of the provider. */ readonly name: string; /** * Array of chains supported by this swap provider. * * @remarks * Each chain definition represents a blockchain network where this provider * can execute swap operations. This property is used by SwapKit to dynamically * aggregate and expose available chains without requiring manual configuration * or hardcoded chain lists. * * The list should include all chains where the provider can facilitate token * swaps, typically filtered by the provider's protocol capabilities and the * presence of required token contracts (e.g., USDC, USDT). */ readonly supportedChains: readonly ChainDefinition[]; /** * Whether this provider intentionally supports cross-chain swap routes. * * Providers that do not set this flag are treated as same-chain-only by * SwapKit route selection so older implementations cannot accidentally * accept routes while ignoring `toChain`. */ readonly supportsCrossChain?: boolean; /** * Determines if this provider supports swaps between the specified tokens on the given chain. * * This method should check if the provider can handle swaps between the given * token pair on the specified chain, typically by verifying that both tokens * are supported by the provider's protocol or DEX aggregator. * * @param tokenIn - The input token address or alias * @param tokenOut - The output token address or alias * @param chain - The source chain where the swap will start * @param toChain - Optional destination chain. Providers that support * cross-chain swaps should inspect this value; same-chain-only providers * should leave {@link SwappingProvider.supportsCrossChain} unset or `false`. * @param config - Optional route configuration used for route filtering * @returns `true` if the provider supports this route, `false` otherwise * * @remarks * Migration note: cross-chain route checks pass `toChain` and `config`. * * @example * ```typescript * import { Ethereum } from '@core/chains' * * const provider = new StablecoinServiceSwapProvider() * const usdcAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' * const usdtAddress = '0xdAC17F958D2ee523a2206206994597C13D831ec7' * const canSwap = provider.supportsRoute(usdcAddress, usdtAddress, Ethereum) * if (canSwap) { * console.log('USDC to USDT swap is supported on Ethereum') * } * ``` */ supportsRoute(tokenIn: string, tokenOut: string, chain: ChainDefinition, toChain?: ChainDefinition, config?: ServiceSwapConfig): boolean; /** * Estimates the cost and fees for a token swap operation without executing it. * * This method calculates the expected gas costs, swap fees, and output amounts * for a swap operation, allowing users to understand the total cost and expected * result before committing to the transaction. The estimation should be as accurate * as possible but may vary slightly from actual execution due to network conditions * and price movements. * * @typeParam TFromAdapterCapabilities - The adapter capabilities type for the source adapter * @param params - The swap parameters for cost estimation * @returns Promise resolving to the estimate result including estimated outputs and fees * * @example * ```typescript * import { createAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * import { Ethereum } from '@core/chains' * * const adapter = createAdapterFromPrivateKey({ privateKey: process.env.PRIVATE_KEY }) * const result = await provider.estimate({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', // Alias resolves to chain-specific address * tokenOut: 'USDT', // Alias resolves to chain-specific address * amountIn: '100.00', * to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', * config: { slippageBps: 300 } * }) * console.log('Stop limit (base units):', result.stopLimit) * console.log('Estimated output (decimal):', result.estimatedOutput) * ``` */ estimate(params: ServiceSwapParams): Promise; /** * Executes a token swap operation on the specified chain. * * This method performs the actual swap operation by coordinating with the underlying * protocol contracts or DEX aggregators and handling the multi-step process required * for token swaps. The implementation details vary by protocol but typically involve * approving tokens, executing swap transactions, and transferring the output tokens * to the destination address. * * @typeParam TFromAdapterCapabilities - The adapter capabilities type for the source adapter * @param params - The swap parameters containing source, tokens, amount, destination, and configuration * @returns Promise resolving to the swap result with transaction hash and swap details * * @example * ```typescript * import { createAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * import { Ethereum } from '@core/chains' * * const adapter = createAdapterFromPrivateKey({ privateKey: process.env.PRIVATE_KEY }) * const result = await provider.swap({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', // Alias resolves to chain-specific address * tokenOut: 'USDT', // Alias resolves to chain-specific address * amountIn: '100.00', * to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', * config: { slippageBps: 300 } * }) * console.log('Transaction hash:', result.txHash) * console.log('Amount in (base units):', result.amountIn) * console.log('Amount out (base units):', result.amountOut) * ``` */ swap(params: ServiceSwapParams): Promise; } /** * Swap provider backed by Circle's Stablecoin Service. * * @remarks * This provider enables token swaps through Circle's Stablecoin Service, * which aggregates liquidity from multiple DEX protocols to find optimal * swap routes. The provider handles: * * - Service API integration and authentication * - Parameter validation and transformation * - Transaction preparation and execution * - Confirmation monitoring and error handling * * **Features**: * - Token swaps on EVM-compatible blockchains * - Support for USDC, USDT, and native currencies (ETH, MATIC, etc.) * - Custom fee collection for developers * - Configurable slippage protection * - Integration with Circle's Stablecoin Service for optimal routing * * @example * **Basic Provider Instantiation** * * ```typescript * import { StablecoinServiceSwapProvider } from '@circle-fin/provider-stablecoin-service-swap' * * // Create provider with default configuration * const provider = new StablecoinServiceSwapProvider() * console.log('Provider name:', provider.name) * console.log('Supported chains:', provider.supportedChains.length) * ``` * * @example * **Provider with Custom Fee** * * ```typescript * import { StablecoinServiceSwapProvider } from '@circle-fin/provider-stablecoin-service-swap' * * // Create provider with developer fee (1 USDC) * const provider = new StablecoinServiceSwapProvider({ * kitKey: process.env.STABLECOIN_KIT_API_KEY, * customFee: { * amount: '1000000', // 1 USDC in base units (6 decimals) * recipientAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0' * } * }) * ``` * * @example * **Complete Swap Flow with Route Validation** * * ```typescript * import { StablecoinServiceSwapProvider } from '@circle-fin/provider-stablecoin-service-swap' * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * import { Ethereum } from '@core/chains' * * const provider = new StablecoinServiceSwapProvider() * * // Check if route is supported * const usdcAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' * const usdtAddress = '0xdAC17F958D2ee523a2206206994597C13D831ec7' * const isSupported = provider.supportsRoute(usdcAddress, usdtAddress, Ethereum) * * if (isSupported) { * const adapter = createViemAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY, * }) * * // Get swap estimate first * const estimate = await provider.estimate({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', // Token alias * tokenOut: 'USDT', // Token alias * amountIn: '100.00', * to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', * config: { slippageBps: 300 } * }) * console.log('Estimated output:', estimate.estimatedOutput) * * // Execute the swap * const result = await provider.swap({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', // Token alias * tokenOut: 'USDT', // Token alias * amountIn: '100.00', * to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', * config: { * slippageBps: 300, * allowanceStrategy: 'permit' * } * }) * console.log('Transaction hash:', result.txHash) * } * ``` * * @example * **Error Handling Best Practices** * * ```typescript * import { StablecoinServiceSwapProvider } from '@circle-fin/provider-stablecoin-service-swap' * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * import { Ethereum } from '@core/chains' * import { isKitError, isFatalError, isRetryableError } from '@core/errors' * * const provider = new StablecoinServiceSwapProvider() * const adapter = createViemAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY, * }) * * const params = { * from: { adapter, chain: Ethereum }, * tokenIn: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', * tokenOut: '0xdAC17F958D2ee523a2206206994597C13D831ec7', * amountIn: '100.00', * to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', * } * * try { * // Validate route before attempting swap * if (!provider.supportsRoute(params.tokenIn, params.tokenOut, Ethereum)) { * console.error('Route not supported') * return * } * * const result = await provider.swap(params) * console.log('Swap completed:', result.txHash) * } catch (error) { * if (isKitError(error)) { * console.error('Error code:', error.code) * console.error('Message:', error.message) * * if (isFatalError(error)) { * // Unrecoverable error - fix configuration * console.error('Fatal error, check your parameters') * } else if (isRetryableError(error)) { * // Transient error - retry is possible * console.log('Retrying swap...') * } * } else { * console.error('Unexpected error:', error) * } * } * ``` */ declare class StablecoinServiceSwapProvider implements SwappingProvider { /** The name of the provider */ readonly name = "StablecoinServiceSwapProvider"; /** * Array of chains supported by this swap provider. * * This list is used by SwapKit to determine which chains are available * for swapping tokens through this provider. */ readonly supportedChains: readonly ChainDefinition[]; /** This provider supports same-chain and cross-chain swap routes. */ readonly supportsCrossChain = true; /** * Normalize a token identifier for consistent comparison. * * @remarks * - 'NATIVE' literal and native currency symbols (e.g., 'ETH', 'SOL') are normalized to 'native'. * - 'USDC' literal is normalized to the chain's USDC address (lowercased). * - 'USDT' literal is normalized to the chain's USDT address (lowercased). * - EVM addresses (starting with `0x`) are lowercased for case-insensitive matching. * - Solana base58 addresses are preserved as-is to maintain case sensitivity. * * @param tokenAddress - The token address or symbol to normalize, or `null`. * @param chain - The chain definition containing the native currency symbol. * @returns The normalized token identifier, or an empty string if `null`. */ private normalizeToken; /** * Determine whether the provider supports the supplied swap route. * * @remarks * This method validates that: * - The chain is swap-supported (mainnet or whitelisted testnet with CCTPv2) * - Both tokens are supported (USDC, USDT, native currency, or token literals) * - The tokens are different (no same-token swaps) * * Token addresses are normalized for comparison: * - EVM addresses are lowercased for case-insensitive matching * - Solana addresses preserve case sensitivity * - Token literals ('NATIVE', 'USDC', 'USDT') are recognized and normalized (case-insensitive) * * @param tokenIn - The input token address or alias. * @param tokenOut - The output token address or alias. * @param chain - The source chain where the swap will start. * @param toChain - Optional destination chain for cross-chain swaps. * @returns `true` if the provider supports this route, `false` otherwise. * * @example * ```typescript * import { StablecoinServiceSwapProvider } from '@circle-fin/provider-stablecoin-service-swap' * import { Ethereum, Base, Solana } from '@core/chains' * * const provider = new StablecoinServiceSwapProvider() * * // Check swap using token literals * const usdcToUsdt = provider.supportsRoute('USDC', 'USDT', Ethereum) * console.log('USDC→USDT on Ethereum:', usdcToUsdt) // true * * // Check swap using literal and address * const usdcToNative = provider.supportsRoute('USDC', 'NATIVE', Base) * console.log('USDC→NATIVE on Base:', usdcToNative) // true * * // Check EVM token swap with addresses (case-insensitive) * const addressSwap = provider.supportsRoute( * '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC address * '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT address * Ethereum * ) * console.log('Address-based swap:', addressSwap) // true * * // Check native token swap using NATIVE * const usdcToNativeBase = provider.supportsRoute( * '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC address on Base * 'NATIVE', // Native gas token * Base * ) * console.log('USDC→NATIVE on Base:', usdcToNativeBase) // true * * // Check Solana swap (addresses are case-sensitive) * const solanaSwap = provider.supportsRoute( * 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC * 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', // USDT * Solana * ) * console.log('USDC→USDT on Solana:', solanaSwap) // true * ``` */ supportsRoute(tokenInAddress: string, tokenOutAddress: string, chain: ChainDefinition, toChain?: ChainDefinition): boolean; /** * Estimate swap costs and output amount by delegating to the Stablecoin Service quote API. * * @remarks * This method fetches a quote from Circle's Stablecoin Service API without * executing the actual swap. Unlike on-chain gas estimation, this method * does NOT require the wallet to hold the input token balance—gas limits * are provided by the service response. * * **Important**: The gas fee returned in `fees` is the gas limit (max units), * not the actual gas cost in wei. For precise fee calculations, multiply * by the current gas price. * * Use this to: * - Display expected output amounts to users * - Show fee breakdown before committing to a swap * - Validate that the swap parameters are acceptable * * The estimate includes: * - `stopLimit`: Minimum guaranteed output in base units * - `estimatedOutput`: Expected output in human-readable format * - `fees`: Detailed breakdown of all applicable fees * - `transaction`: Pre-built transaction data for execution * * @typeParam TFromAdapterCapabilities - The adapter capabilities type for the source adapter. * @param params - The swap parameters containing adapter context, tokens, amount, and config. * @returns A promise resolving to the estimate result with transaction data and fees. * @throws {@link KitError} If validation fails or the service is unavailable. * * @example * ```typescript * import { StablecoinServiceSwapProvider } from '@circle-fin/provider-stablecoin-service-swap' * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * import { Ethereum } from '@core/chains' * * const provider = new StablecoinServiceSwapProvider() * const adapter = createViemAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY * }) * * // Get swap estimate for USDC → USDT * const estimate = await provider.estimate({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', // Token alias * tokenOut: 'USDT', // Token alias * amountIn: '100.00', * to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', * config: { slippageBps: 300 } // 3% slippage tolerance * }) * * console.log('Stop limit (base units):', estimate.stopLimit) * console.log('Estimated output:', estimate.estimatedOutput) * console.log('Fees:', estimate.fees) * ``` * * @example * ```typescript * // Estimate with custom stop limit * const estimateWithStopLimit = await provider.estimate({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', // Token alias * tokenOut: 'USDT', // Token alias * amountIn: '100.00', * to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', * config: { * slippageBps: 300, * stopLimit: '99000000' // Minimum 99 USDT output * } * }) * ``` */ estimate(params: ServiceSwapParams): Promise; /** * Prepares a token approval transaction for swap operations. * * @remarks * This method creates a prepared transaction that approves the Adapter Contract * to spend a specified amount of tokens. The approval is required before * initiating a swap operation when the token doesn't support EIP-2612 permits. * * This follows the same pattern as CCTPV2BridgingProvider.approve(), ensuring * consistency across the codebase. * * **Approval Strategy:** * * The method intelligently selects the appropriate approval function based on * the token being approved: * * - **USDC**: Uses `usdc.increaseAllowance` (safer, avoids race conditions) * - OpenZeppelin's ERC20 extension with relative increase * - Prevents front-running attacks on allowance changes * - Recommended for USDC by Circle * * - **Other ERC-20 tokens** (USDT, DAI, etc.): Uses `token.approve` * - Standard ERC-20 approve function * - Industry standard used by Uniswap, 1inch, and all major DEXs * - Theoretical race condition is rare in practice * - Atomic swap execution provides additional safety * * **When is this used?** * - Token doesn't support EIP-2612 (e.g., USDT, some DAI versions) * - Fallback when permit signature generation fails * - User explicitly requests approval strategy via config * * **Flow:** * 1. Detect token type (USDC vs other ERC-20) * 2. Prepare appropriate action (`usdc.increaseAllowance` or `token.approve`) * 3. Return prepared request (caller executes and waits for confirmation) * * @typeParam TFromAdapterCapabilities - The adapter capabilities type for the source adapter * @param adapter - The adapter for transaction preparation and execution * @param amount - The amount of tokens to approve (as string in base units) * @param tokenAddress - The token contract address to approve * @param spenderAddress - The Adapter Contract address that will spend the tokens * @param resolvedContext - The resolved operation context containing chain and address information * @returns Promise resolving to a prepared chain request ready for execution * * @throws Error when adapter is not provided * @throws Error when amount is invalid (empty, negative, or malformed) * * @example USDC approval (uses increaseAllowance) * ```typescript * // Internal use in swap() method for USDC * const approvalRequest = await this.approve( * adapter, * '1000000', // 1 USDC (6 decimals) * '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC address * '0xAdapterContract', // Adapter Contract address * resolvedContext * ) * // Uses usdc.increaseAllowance internally * * // Execute and wait for confirmation * const txHash = await approvalRequest.execute() * await adapter.waitForTransaction(txHash, undefined, chain) * ``` * * @example USDT approval (uses approve) * ```typescript * // Internal use in swap() method for USDT * const approvalRequest = await this.approve( * adapter, * '1000000', // 1 USDT (6 decimals) * '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT address * '0xAdapterContract', // Adapter Contract address * resolvedContext * ) * // Uses token.approve internally * * // Execute and wait for confirmation * const txHash = await approvalRequest.execute() * await adapter.waitForTransaction(txHash, undefined, chain) * ``` * * @internal */ private approve; /** * Handle EVM token approval for swap operations. * * @remarks * Determine whether the swap requires a separate on-chain approval * transaction and, if so, execute it before the swap itself. * * **Decision matrix (in order of precedence):** * * | Condition | Result | * |-----------|--------| * | Native token (ETH, MATIC, …) | Skip — no approval needed | * | `allowanceStrategy === 'approve'` | Force on-chain approval, even when permit is available | * | Token supports EIP-2612 **and** adapter supports EIP-2612 | Skip — permit will be bundled into the swap tx | * | Otherwise | On-chain approval required | * * When on-chain approval **is** required the method delegates to * {@link handleUsdtApproval} (USDT needs a reset-to-zero flow) or * {@link handleStandardApproval} (all other ERC-20 tokens, including * USDC via `increaseAllowance`). * * @typeParam TFromAdapterCapabilities - The adapter capabilities type * @param adapter - The adapter for transaction execution * @param chain - The chain definition containing contract addresses * @param serviceParams - The swap parameters from the service * @param resolvedContext - The resolved operation context * @param executedTransactions - Array to track executed transactions * @param allowanceStrategy - Optional override for the allowance strategy. * When set to `'approve'`, the permit flow is bypassed and a standard * on-chain approval transaction is always sent. When `'permit'` or * `undefined` the method follows the default EIP-2612 detection logic. * @returns Promise that resolves when approval is complete (or skipped) * @throws KitError when adapter contract address is missing or approval fails */ private handleEvmTokenApproval; /** * Handle USDT approval with reset flow * * USDT requires allowance to be reset to 0 before setting a new non-zero value. * This method checks the current allowance and: * - If sufficient, proceeds without changes * - If non-zero but insufficient, resets to 0 then sets new amount * - If zero, sets new amount directly * * @param adapter - The chain adapter instance * @param chain - The chain definition * @param serviceParams - The service parameters containing amount and addresses * @param adapterContractAddress - The adapter contract address to approve * @param resolvedContext - The resolved operation context * @param executedTransactions - Array to track executed approval transactions * @returns A Promise that resolves when approval transactions are complete * * @throws Error if the adapter fails to prepare or execute approval transactions * @throws Error if transaction confirmation fails or times out */ private handleUsdtApproval; /** * Handle standard ERC-20 token approval * * Execute a standard ERC-20 approve transaction for non-USDT tokens. * This is a straightforward approval without special reset requirements. * * @param adapter - The chain adapter instance * @param chain - The chain definition * @param serviceParams - The service parameters containing amount and addresses * @param adapterContractAddress - The adapter contract address to approve * @param resolvedContext - The resolved operation context * @param executedTransactions - Array to track executed approval transactions * @returns A Promise that resolves when the approval transaction is complete * * @throws Error if the adapter fails to prepare or execute the approval transaction * @throws Error if transaction confirmation fails or times out */ private handleStandardApproval; /** * Execute a token swap operation through the Stablecoin Service. * * @remarks * This method coordinates with the stablecoin-service to prepare and execute swap transactions. * It handles parameter validation, service communication, automatic token approval, transaction * preparation, execution, and confirmation monitoring. * * **Automatic Approval Flow** (similar to BridgeKit): * 1. **Check for EIP-2612 support**: Detects if token supports gasless approvals * 2. **If token supports EIP-2612** (USDC, EURC): * - Generate permit signature (off-chain, gasless) * - Execute swap in **single transaction** * 3. **If token doesn't support EIP-2612**: * - Execute approval transaction (on-chain) * - Wait for approval confirmation * - Execute swap transaction * - Total: **two transactions** * * **EIP-2612 Benefits**: * - Single transaction (lower gas costs) * - Better UX (one wallet interaction) * - No pre-approval required * * **Supported EIP-2612 Tokens**: * - USDC on Ethereum, Base, Arbitrum, Optimism, Polygon, Avalanche, World Chain, Sepolia * * @typeParam TFromAdapterCapabilities - The adapter capabilities type for the source adapter * @param params - The swap parameters containing adapter context, tokens, amount, destination, and configuration * @returns Promise resolving to SwapResult with transaction hash and details * @throws KitError when validation fails or transaction reverts * @throws Error when service communication or transaction execution fails * * @example Swap with EIP-2612 (single transaction) * ```typescript * import { StablecoinServiceSwapProvider } from '@circle-fin/provider-stablecoin-service-swap' * import { createAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * import { Ethereum } from '@core/chains' * * const provider = new StablecoinServiceSwapProvider({ * kitKey: process.env.STABLECOIN_KIT_API_KEY * }) * * const adapter = createAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY * }) * * // Swap USDC → USDT (supports EIP-2612) * const result = await provider.swap({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', // Token alias * tokenOut: 'USDT', // Token alias * amountIn: '100.00', * to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' * }) * // User signs permit (off-chain) → Swap executes (1 tx total) * * console.log('Swap completed:', result.txHash) * ``` * * @example Swap without EIP-2612 (two transactions) * ```typescript * // Swap token that doesn't support EIP-2612 * const result = await provider.swap({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', // Token alias * tokenOut: 'NATIVE', // Token alias (ETH on Ethereum) * amountIn: '100.00', * to: '0xRecipient' * }) * // Approval tx executes and confirms → Then swap executes (2 txs total) * ``` * * @example Swap with custom configuration * ```typescript * const result = await provider.swap({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', // Alias supported * tokenOut: 'USDT', // Alias supported * amountIn: '100.00', * to: '0xRecipient', * config: { * slippageBps: 500, // 5% slippage tolerance * customFee: { * value: '1000000', // 1 USDC fee * recipientAddress: '0xFeeRecipient' * } * } * }) * * console.log('Transaction hash:', result.txHash) * console.log('Input amount:', result.amountIn) * console.log('Fees:', result.fees) * ``` * * @example * ```typescript * // Swap with error handling * import { isKitError, isFatalError, isRetryableError } from '@core/errors' * * try { * const result = await provider.swap({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', // Token alias * tokenOut: 'USDT', // Token alias * amountIn: '100.00', * to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' * }) * console.log('Swap completed:', result.txHash) * } catch (error) { * if (isKitError(error)) { * if (isFatalError(error)) { * console.error('Fatal error - check parameters:', error.message) * } else if (isRetryableError(error)) { * console.log('Transient error - retrying...') * } * } * } * ``` * * @example Error handling * ```typescript * try { * const result = await provider.swap(params) * console.log('Success:', result.txHash) * } catch (error) { * if (error instanceof KitError) { * console.error('Swap failed:', error.message) * console.error('Recoverability:', error.recoverability) * console.error('Trace:', error.cause?.trace) * } * } * ``` */ swap(params: ServiceSwapParams): Promise; /** * Executes a swap transaction with the appropriate gas limit for the chain type. * * For EVM chains, performs a local eth_estimateGas call, applies a 1.3x safety * multiplier for the full adapter execution path (DEX swap, fee split, token sweep), * and uses the greater of (local estimate × 1.3) or the proxy's gasLimit. If local * estimation fails, falls back to the proxy's gasLimit. * * @param preparedAction - The prepared chain request to execute. * @param evmGasLimit - Optional gas limit from the proxy service for EVM transactions. * @returns The transaction hash. */ private executeSwapTransaction; /** * Build formatted fee arrays from the service response. * * @remarks * Consolidate provider, swap, and developer fee formatting into a * single call so both {@link estimate} and {@link swap} share the * same logic. * * @param fees - The fee breakdown from the service response. * @param chain - The chain definition for token resolution and formatting. * @param adapter - The adapter for on-chain decimals lookup of unregistered tokens. * @param recipientAddress - Optional developer fee recipient address. * @returns Promise resolving to the combined formatted fee array. */ private buildFormattedFees; /** * Format service fee items into the SDK's ServiceSwapFee structure. * * @remarks * Transform raw fee data from the service into formatted fees with: * - Resolved token symbols (USDC, USDT, NATIVE) when possible * - Human-readable decimal amounts via {@link formatTokenValue} * - On-chain decimals fallback for unregistered tokens via adapter * - Raw passthrough only when both registry and adapter fail * * @param feeItems - Array of fee items from the service response. * @param chain - The chain definition for token resolution and formatting. * @param type - The fee type to assign ('provider' or 'swap'). * @param adapter - The adapter for on-chain decimals lookup of unregistered tokens. * @returns Promise resolving to formatted ServiceSwapFee array. */ private formatServiceFees; /** * Format developer fee items into the SDK's ServiceSwapFee structure. * * @param feeItems - Array of developer fee items from the service response. * @param chain - The chain definition for token resolution and formatting. * @param recipientAddress - The developer's fee recipient address from config. * @param adapter - The adapter for on-chain decimals lookup of unregistered tokens. * @returns Promise resolving to formatted ServiceSwapFee array with developer entries. */ private formatDeveloperFees; } /** * Latest cached USD rate for a single (chain, token) pair, as written by the * Stablecoin Service's `token-rate-update` cron. * * @remarks * The cron refreshes entries once per minute and they expire after 15 minutes * of staleness. Inspect {@link TokenRate.fetchedAt} when freshness matters — * a value older than a few minutes likely means the upstream provider was * unreachable on the most recent run. * * @example * ```typescript * const rate: TokenRate = { priceUSD: '1.0001', fetchedAt: 1716240000000 } * const ageMs = Date.now() - rate.fetchedAt * ``` */ interface TokenRate { /** * USD price as reported by the upstream provider. * * Returned as a string to preserve precision — parse with `Number` or a * decimal library depending on how much precision callers need. */ readonly priceUSD: string; /** * Unix timestamp in milliseconds at which the price was fetched from the * upstream provider. */ readonly fetchedAt: number; /** * On-chain decimal precision of the token, when the upstream provider * reports it. Absent for tokens the provider has not catalogued. * * Use it to convert a raw base-unit balance into a human amount before * multiplying by `priceUSD`: `balance / 10 ** decimals * Number(priceUSD)`. */ readonly decimals?: number; } /** * Response payload from `GET /v1/stablecoinKits/rates`. * * Nested map keyed first by chain name (matching the `Blockchain` enum), then * by token address. Address keys preserve the service's canonical casing: * EVM hex addresses are lowercased, Solana base58 mints are case-preserved. * * @remarks * - A successful call always returns a `rates` object, but it may be empty * when no cached entries are found. * - When `addresses` was supplied, missing addresses are simply absent — * there is no 404 for partial lookups. * * @example * ```typescript * const response: GetTokenRatesResponse = { * rates: { * Ethereum: { * '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48': { * priceUSD: '1.0001', * fetchedAt: 1716240000000, * }, * }, * }, * } * ``` */ interface GetTokenRatesResponse { /** * Nested map of token rates keyed by `[chain][address]`. * * Always present; may be an empty object when no rates are found. */ readonly rates: Readonly>>>; } /** * A type alias that enables flexible provider type handling. * * This type alias is used for the providers array to avoid strict type checking * issues while maintaining type safety at usage sites through the properly typed * provider interfaces and method signatures. * * This follows the same pattern as BridgeKit's FlexibleBridgingProvider. */ type FlexibleSwappingProvider = SwappingProvider; /** * Fee context when fee is taken from INPUT token. * * Used for swaps where the fee is collected from the input token, including * all cross-chain swaps. * Extends the resolved swap parameters with a discriminator to indicate input fee scenario. * * @typeParam TFromAdapterCapabilities - The adapter capabilities type for the source adapter * * @example * ```typescript * // USDC → RandomToken swap (fee from input) * const context: SwapInputFeeContext = { * type: 'input', * from: { * adapter: viemAdapter, * chain: Ethereum, * address: '0x...' * }, * tokenIn: 'USDC', * tokenOut: 'RandomToken', * amountIn: '100000000', // 100 USDC in base units * to: '0x...' * } * ``` */ interface SwapInputFeeContext extends ResolvedSwapParams { /** * Fee source discriminator - input token. */ type: 'input'; } /** * Fee context when fee is taken from OUTPUT token. * * Used for swaps where the output token is supported for fee collection. * Extends the resolved swap parameters with output amounts and discriminator. * * @typeParam TFromAdapterCapabilities - The adapter capabilities type for the source adapter * * @remarks * If using `estimatedAmount` in callback and quote cache expires, * calculated fee may not match fresh quote. Use `minAmount` for * predictability at the cost of potentially lower fees. * * @example * ```typescript * // RandomToken → USDC swap (fee from output) * const context: SwapOutputFeeContext = { * type: 'output', * from: { * adapter: viemAdapter, * chain: Ethereum, * address: '0x...' * }, * tokenIn: 'RandomToken', * tokenOut: 'USDC', * amountIn: '100000000', * to: '0x...', * minAmount: '50000000', // 50 USDC guaranteed minimum * estimatedAmount: '55000000' // 55 USDC expected output * } * ``` */ interface SwapOutputFeeContext extends ResolvedSwapParams { /** * Fee source discriminator - output token. */ type: 'output'; /** * Guaranteed minimum output amount in base units. * * More stable but lower than estimatedAmount. Use this for * predictable fee calculations. */ minAmount: string; /** * Estimated output amount in base units. * * Expected output based on current market conditions. May be * higher than minAmount. Subject to change if quote expires. */ estimatedAmount: string; } /** * Discriminated union for swap fee contexts. * * Provides different context based on whether fee is from input or output token. * Discriminated by the `type` field: 'input' for fees from input token, 'output' for output token. * * Includes full swap parameters (adapter, chain, tokens, amounts) for maximum flexibility * in fee calculation logic. * * @typeParam TFromAdapterCapabilities - The adapter capabilities type for the source adapter */ type SwapFeeContext = SwapInputFeeContext | SwapOutputFeeContext; /** * Custom fee policy for SwapKit (callback-based approach). * * Provides hooks to calculate an absolute fee amount and resolve the fee * recipient address. The callback receives a discriminated context with * different fields based on operation type (bridge vs swap) and fee source * (input vs output). * * @remarks * This is mutually exclusive with transaction-level percentage fees. * If both are set, the transaction-level percentage takes precedence. * * The callback approach makes two API calls for same-chain output fees: * 1. GET /quote - retrieve fee context and quote * 2. POST /swap - execute with calculated fee * * Cross-chain swaps always use input fees on the source chain, so callbacks * receive `type: 'input'` for those routes. * * @example * ```typescript * import type { CustomFeePolicy } from '@circle-fin/swap-kit' * * const policy: CustomFeePolicy = { * computeFee: async (ctx) => { * // Discriminate by fee source (input vs output) * if (ctx.type === 'input') { * // Simple percentage for input fees * return (parseFloat(ctx.amountIn) * 0.1).toString() * } else { * // Complex logic for output fees (VIP tiers, etc.) * const user = await database.getUser(...) * if (user.isVIP) { * return (parseFloat(ctx.minAmount) * 0.05).toString() * } * return (parseFloat(ctx.estimatedAmount) * 0.1).toString() * } * }, * resolveFeeRecipientAddress: (chain) => { * return chain.type === 'solana' * ? 'SolanaAddress...' * : '0xEVMAddress...' * }, * } * ``` */ interface CustomFeePolicy { /** * Calculate custom fee amount based on swap context. * * Receives full swap parameters including adapter, chain, tokens, and amounts. * Context is discriminated by `type` field: * - 'input': Fee from input token (OK → Any swaps and all cross-chain swaps) * - 'output': Fee from output token (same-chain Any → OK swaps), includes minAmount and estimatedAmount * * The wrapper automatically converts amounts to/from base units, so your * callback works with human-readable numbers (e.g., '0.1' for 0.1 USDC). * * @typeParam TFromAdapterCapabilities - The adapter capabilities type for the source adapter * @param context - Discriminated swap fee context with full swap parameters * @returns Absolute fee amount as string in human-readable format * * @example * ```typescript * computeFee: async (ctx) => { * if (ctx.type === 'output') { * // Output fee scenario * // Use estimatedAmount or minAmount for calculation * return (parseFloat(ctx.estimatedAmount) * 0.01).toString() * } * // Input fee scenario * return (parseFloat(ctx.amountIn) * 0.01).toString() * } * ``` * * @example * ```typescript * computeFee: async (ctx) => { * if (ctx.type === 'output') { * // Use minAmount for predictable fees * return (parseFloat(ctx.minAmount) * 0.01).toString() * } * return (parseFloat(ctx.amountIn) * 0.01).toString() * } * ``` */ computeFee: (context: SwapFeeContext) => Promise | string; /** * Resolve fee recipient address for the chain where fee is collected. * * Called with the chain where fee will be paid. For cross-chain swaps this * is always the source chain. Must return a valid address format for that * chain type (EVM or Solana). * * @param feePayoutChain - Chain definition where fee is collected * @param context - The swap fee context with full parameters * @returns Fee recipient address for the chain * * @example * ```typescript * resolveFeeRecipientAddress: (chain, ctx) => { * // Chain-based routing * if (chain.type === 'solana') { * return 'SolanaAddress...' * } * return '0xEVMAddress...' * } * ``` */ resolveFeeRecipientAddress: (feePayoutChain: ChainDefinition, context: SwapFeeContext) => Promise | string; } /** * Adapter context constrained to swap-supported chains. */ type SwapAdapterContext = Omit, 'chain'> & { chain: SwapChainIdentifier; }; /** * Destination details for same-chain or cross-chain swaps. */ interface SwapDestination { /** * Optional destination chain. * * Defaults to `from.chain` for same-chain swaps. */ chain?: SwapChainIdentifier; /** * Optional recipient address. * * Defaults to the source wallet address for same-chain swaps. Cross-chain * swaps require this field because the destination wallet may be on a * different chain/address format. */ recipientAddress?: string; } /** * Allowance strategy for token approvals during swap operations. * * Defines how token allowances should be granted to the swap contract: * - `permit`: Use EIP-2612 permit signature (gas-efficient, no approval transaction) * - `approve`: Traditional approval transaction * * The default strategy is `permit` with fallback to `approve` if permit is not supported. */ type AllowanceStrategy = 'permit' | 'approve'; /** * Configuration options for swap operations. * * Controls swap behavior including allowance strategy, slippage tolerance, * minimum output amounts, custom fees, and kit identification. * * @example * ```typescript * // With percentage-based custom fee * const config: SwapConfig = { * allowanceStrategy: 'permit', * slippageBps: 300, // 3% * stopLimit: '0.95', // Minimum 0.95 USDC output * customFee: { * percentageBps: 1000, // 10% fee (100 bps = 1%) * recipientAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' * } * } * ``` * * @example * ```typescript * // Without custom fee * const config: SwapConfig = { * slippageBps: 300, * allowanceStrategy: 'approve' * } * ``` */ interface SwapConfig { /** * Strategy for granting token allowances to the swap contract. * * Defaults to 'permit' with fallback to 'approve'. */ allowanceStrategy?: AllowanceStrategy; /** * Maximum acceptable slippage in basis points (BPS). * * 1 BPS = 0.01%, so 300 BPS = 3% slippage. * Defaults to 300 BPS (3%). */ slippageBps?: number; /** * Minimum acceptable output amount in human-readable format (stop-limit). * * If the estimated output falls below this value, the swap will fail. * Expressed as a decimal string (e.g., '0.4' for 0.4 USDT). * The value is automatically converted to base units using the tokenOut decimals. * * @example '99.5' for minimum 99.5 USDT output */ stopLimit?: string; /** * Custom fee configuration for this swap (percentage-based approach). * * Allows specifying a percentage fee and recipient address at the * transaction level. This is mutually exclusive with kit-level callback * fee policy. If both are set, transaction-level takes precedence. * * For complex fee logic (VIP tiers, database lookups), use kit-level * callback approach via `setCustomFeePolicy()` instead. * * @remarks * - Service calculates fee using `estimatedAmount` for same-chain output fees * - Cross-chain fees are always collected from the source-chain input token * - Fee is sent to your configured `recipientAddress` * - Single API call (efficient) * - Must be greater than 0 and less than or equal to 10000 bps (100%) * * @example * ```typescript * customFee: { * percentageBps: 1000, // 10% fee * recipientAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' * } * ``` */ customFee?: { /** * Fee percentage in basis points. * * 100 bps = 1%, 1000 bps = 10%, 10000 bps = 100% * * Fee is calculated from `estimatedAmount` for same-chain output fees and * from the input amount for cross-chain swaps. * Must be greater than 0 and less than or equal to 10000 (maximum 100%). * * For complex fee logic, use kit-level `setCustomFeePolicy()` instead. * * @example 1000 // 10% fee */ percentageBps: number; /** * Address that will receive the developer's 90% fee share. * * Must be valid on the fee payout chain: source chain for input-side fees * and cross-chain swaps, destination chain for same-chain output-side fees. * * @example '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' */ recipientAddress: string; }; /** * Stablecoin Service Kit Key used to authenticate service-backed swap * requests. * * Treat this value as a credential. Do not log it, embed it in client-side * source, or expose it in telemetry. */ kitKey?: string; } /** * Estimation result for a swap operation. * * Contains the provider's swap quote including minimum output (stop limit), * estimated output amount, fee breakdown, and input context fields * * @remarks * To resolve the chain back to a full ChainDefinition object, use: * ```typescript * import { getChainByEnum } from '@circle-fin/swap-kit' * const chainDef = getChainByEnum(estimate.chain) // Blockchain.Ethereum → full ChainDefinition * ``` * * @example * ```typescript * const estimate: SwapEstimate = { * // Input context * tokenIn: 'NATIVE', * tokenOut: 'USDC', * amountIn: '0.00001', * chain: Blockchain.Ethereum, * fromAddress: '0x2971...5EE9f', * toAddress: '0x2971...5EE9f', * // Estimate details * stopLimit: { token: 'USDT', amount: '99.45' }, * estimatedOutput: { token: 'USDT', amount: '99.45' }, * fees: [ * { token: 'USDC', amount: '0.05', type: 'provider' }, * { token: 'USDC', amount: '0.01', type: 'swap' } * ] * } * ``` */ interface SwapEstimate { /** * The input token that will be swapped from. */ readonly tokenIn: SupportedSwapToken; /** * The output token that will be swapped to. */ readonly tokenOut: SupportedSwapToken; /** * The input amount that will be swapped. * * Expressed as a human-readable decimal string in token units * (e.g., `'0.05'` for 0.05 USDC or 0.05 ETH). */ readonly amountIn: string; /** * The source chain of the swap. * * Returns the chain name (e.g. `Blockchain.Ethereum`). Use * `getChainByEnum(chainIn)` to resolve back to a full `ChainDefinition`. */ readonly chainIn: Blockchain; /** * Destination chain of the swap. * * Equal to `chainIn` for same-chain estimates; different for cross-chain * estimates. Always populated. */ readonly chainOut: Blockchain; /** * @deprecated Use {@link SwapEstimate.chainIn} instead. Still populated * with the source chain for a deprecation window; will be removed in a * future major release. */ readonly chain: Blockchain; /** * The address that will initiate the swap. */ readonly fromAddress: string; /** * The address that will receive the swapped tokens. */ readonly toAddress: string; /** * Estimated minimum token out amount with token information. * * This represents the minimum amount of tokens the user should receive after * accounting for slippage. Amount is in human-readable decimal format. * * @example * ```typescript * stopLimit: { token: 'ETH', amount: '0.000001716776195841' } * ``` */ readonly stopLimit: TokenAmount; /** * Estimated output amount with token information. * * @remarks * Provided in human-readable decimal format (e.g., '99.5' for 99.5 tokens). * This represents the expected output amount based on current market conditions. * This field is always provided by the service. * * @example * ```typescript * estimatedOutput: { token: 'USDT', amount: '99.5' } * ``` */ readonly estimatedOutput: TokenAmount; /** * Detailed fee breakdown for the swap operation. * * @remarks * Includes both provider fees (charged by the DEX aggregator/protocol) * and kit fees (if configured). Amounts are in decimal format for display. */ readonly fees?: readonly ServiceSwapFee[]; } /** * Configuration for swap results. * * @example * ```typescript * const config: SwapResultConfig = { * slippageBps: 300, * allowanceStrategy: 'permit' * } * ``` */ /** * Internal swap configuration used after callback execution. * Extends SwapConfig with internal `amount` field set by SDK. * * @internal */ interface ResolvedSwapConfig extends Omit { customFee?: { /** * Fee percentage in basis points (user-provided). */ percentageBps?: number; /** * Fee amount in base units (SDK-computed from callback). * This field is set internally by the SDK after callback execution. * Users should NOT provide this directly. * * @internal */ amount?: string; /** * Fee recipient address when one has been resolved. */ recipientAddress?: string; }; } type SwapResultConfig = Omit; /** * Result of an executed swap operation. * * Captures the source-chain execution outcome for a swap transaction. * * @remarks * `progress.status` tells callers whether the swap has reached a terminal * state: * * - Same-chain swaps: a short best-effort status peek runs inline, so * `progress.status` is typically `'DONE'` and `amountOut` is populated by * the time `.swap()` returns. If the peek fails, `progress.status` falls * back to `'PENDING'`. * - Cross-chain swaps: the inline peek is skipped (destination mint takes * minutes via CCTPv2 attestation). `progress.status` is always `'PENDING'` * and `amountOut` is undefined; the caller should poll * {@link SwapKit.getSwapStatus} for destination-leg details until the * status becomes terminal. * * The `chain` field returns a {@link Blockchain} enum value; use * `getChainByEnum(chain)` to resolve back to a full {@link ChainDefinition} * if needed. * * @example * ```typescript * const result = await kit.swap(params) * * if (result.progress.status === 'DONE') { * console.log(`Received ${result.amountOut} on ${result.chainOut}`) * } else { * // Cross-chain: poll kit.getSwapStatus() for destination details. * } * ``` */ interface SwapResult { /** * The input token that was swapped from. */ readonly tokenIn: SupportedSwapToken; /** * The output token that was swapped to. */ readonly tokenOut: SupportedSwapToken; /** * The source chain of the swap. * * Returns the chain name (e.g. `Blockchain.Ethereum`). Use * `getChainByEnum(chainIn)` to resolve back to a full `ChainDefinition`. */ readonly chainIn: Blockchain; /** * Destination chain of the swap. * * Equal to `chainIn` for same-chain swaps; different for cross-chain * swaps. Always populated so consumers don't have to fall back to * `chainIn` when the destination is implicit. */ readonly chainOut: Blockchain; /** * @deprecated Use {@link SwapResult.chainIn} instead. Still populated * with the source chain for a deprecation window; will be removed in a * future major release. */ readonly chain: Blockchain; /** * The input amount that was swapped, as a human-readable decimal string * in token units (e.g. `'0.05'` for 0.05 USDC or 0.05 ETH). */ readonly amountIn: string; /** * The address that initiated the swap. */ readonly fromAddress: string; /** * The address that received the swapped tokens. */ readonly toAddress: string; /** * The swap configuration that was used for this operation. */ readonly config?: SwapResultConfig; /** * The source-chain transaction hash for the executed swap. */ readonly txHash: string; /** * The formatted explorer URL for the source-chain transaction. Only * present when `txHash` is non-empty. */ readonly explorerUrl?: string; /** * Detailed fee breakdown for the swap operation. Includes both provider * fees (charged by the DEX aggregator/protocol) and kit fees. */ fees?: readonly ServiceSwapFee[]; /** * Lifecycle snapshot: `status`, `substatus`, and `substatusMessage`. * * `status` is `'DONE'` when the swap completed end-to-end (same-chain * only), `'PENDING'` while the destination leg is still in-flight, or a * terminal failure value (`'FAILED'` / `'NOT_FOUND'`). */ readonly progress: SwapProgress; /** * Output amount when `.swap()` can confirm the result inline. * * Expressed as a human-readable decimal string in output-token units (run * through the output-token transform), never base units. * * Populated for same-chain swaps when the best-effort status peek reaches * `DONE`. Undefined for cross-chain swaps; call * {@link SwapKit.getSwapStatus} to retrieve destination-leg details. */ readonly amountOut?: string; } /** * Resolved parameters for swap operations after validation and normalization. * * Internal type used by SwapKit operations after `resolveSwapParams()` has: * - Validated the input parameters * - Resolved chain identifiers to full ChainDefinition objects * - Extracted and validated wallet addresses * * Note: Raw user input is validated first, then `tokenIn` and `tokenOut` may * be canonicalized for downstream routing (for example, Arc Testnet * `NATIVE` → `USDC`). Address resolution is handled by the provider layer. * * This type is consumed by swap providers and internal operations but is not * exposed to end users. It extends ServiceSwapParams which is the format expected * by the StablecoinServiceSwapProvider. * * @typeParam TFromAdapterCapabilities - The adapter capabilities type for the source adapter. * * @example * ```typescript * // After resolution, SwapParams becomes ResolvedSwapParams: * const resolved: ResolvedSwapParams = { * from: { * adapter: viemAdapter, * chain: Ethereum, // Full chain definition * address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' * }, * tokenIn: 'USDC', // Canonicalized for provider routing * tokenOut: 'USDT', // Canonicalized for provider routing * amountIn: '100500000', // Converted to base units (100.5 USDC with 6 decimals) * to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', * config: { * slippageBps: 300, * allowanceStrategy: 'permit' * } * } * ``` */ type ResolvedSwapParams = ServiceSwapParams; /** * Parameters for initiating a same-chain or cross-chain stablecoin swap. * * This type is used as the primary input to swap operations, allowing users to specify * the source context, input/output tokens, swap amount, and optional configuration. * * SwapKit supports swaps between stablecoins (USDC, USDT, EURC, USDe, DAI, PYUSD), * wrapped tokens (WBTC, WETH, WSOL, WAVAX, WPOL), and native tokens * (e.g., ETH on Ethereum, SOL on Solana) via the `NATIVE` alias. * * - The `from` field specifies the source adapter context (wallet and chain). * The chain must be a {@link SwapChain} member (or its corresponding string * literal) so IDE autocomplete only surfaces swap-supported networks. * - The `tokenIn` field specifies the input token (see {@link SupportedToken}). * - The `tokenOut` field specifies the output token (see {@link SupportedToken}). * - The `amountIn` field is a human-readable decimal string (e.g., '10.5'). * - The `config` field allows customization of swap behavior. * * @typeParam TFromAdapterCapabilities - The adapter capabilities type for the source adapter. * * @example * ```typescript * import { SwapParams } from '@circle-fin/swap-kit' * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * * const adapter = createViemAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY, * }) * * // Swap 100.50 USDC for USDT * const params: SwapParams = { * from: { adapter, chain: 'Ethereum' }, * tokenIn: 'USDC', * tokenOut: 'USDT', * amountIn: '100.50', // 100.50 USDC * config: { * slippageBps: 300, // 3% slippage * allowanceStrategy: 'permit' * } * } * ``` * * @example * ```typescript * // Swap 0.05 ETH for USDC with explicit address and string literal chain * const paramsWithNative: SwapParams = { * from: { * adapter, * chain: 'Ethereum', * address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' * }, * tokenIn: 'NATIVE', * tokenOut: 'USDC', * amountIn: '0.05' // 0.05 ETH * } * ``` */ /** * Terminal status values for a swap tracked by the Stablecoin Service. * * `'DONE'` indicates the swap completed (including any cross-chain delivery). * `'FAILED'` indicates the swap failed after on-chain submission. * `'NOT_FOUND'` indicates the service has no record of the transaction. */ type SwapTerminalStatus = 'DONE' | 'FAILED' | 'NOT_FOUND'; /** * All possible status values for a swap tracked by the Stablecoin Service. * * `'PENDING'` means the swap is still in-flight. Callers should keep calling * {@link SwapKit.getSwapStatus} on `'PENDING'` until the status becomes * terminal (see {@link SwapTerminalStatus}). */ type SwapStatus = SwapTerminalStatus | 'PENDING'; /** * Lifecycle snapshot for a swap. * * Grouped so progress signals live in one place on both {@link SwapResult} * and {@link SwapStatusResult}. */ interface SwapProgress { /** * Current swap status. Only `'DONE'`, `'FAILED'`, and `'NOT_FOUND'` are * terminal. `'PENDING'` means the caller should re-invoke * {@link SwapKit.getSwapStatus} until a terminal status is observed. */ readonly status: SwapStatus; /** * Provider-specific status detail (e.g. `'WAIT_DESTINATION_TRANSACTION'`, * `'COMPLETED'`). Surfaced as-is from the Stablecoin Service. */ readonly substatus?: string; /** * Human-readable provider status explanation. */ readonly substatusMessage?: string; } /** * Source-leg transaction and token information reported by the service. * * Populated on {@link SwapStatusResult} from the service's `sendingTxHash`. * `token` is shape-reserved for future service support (the status endpoint * does not report source-token metadata today) so consumers can rely on a * symmetric `source` / `destination` layout. */ interface SwapSourceLeg { /** * Source-chain transaction hash reported by the service. */ readonly txHash?: string; /** * Source-token metadata. Shape-reserved — not populated by the current * Stablecoin Service status endpoint, but exposed here so this field * mirrors {@link SwapDestinationLeg.token} if/when the service starts * returning it. */ readonly token?: { /** * Source token symbol (e.g. `'USDC'`, `'USDT'`). */ readonly symbol?: string; /** * Source token contract address (EVM hex or Solana base58). */ readonly address?: string; }; } /** * Destination-leg transaction and token information reported by the service. * * Present on {@link SwapStatusResult} once the service reports destination * data. Omitted while the destination leg is still in-flight. */ interface SwapDestinationLeg { /** * Destination-chain transaction hash reported by the service. */ readonly txHash?: string; /** * Destination-token metadata reported by the service. */ readonly token?: { /** * Destination token symbol (e.g. `'USDC'`, `'EURC'`). */ readonly symbol?: string; /** * Destination token contract address (EVM hex or Solana base58). */ readonly address?: string; }; /** * Amount received on the destination leg, as a human-readable decimal * string (e.g. `'0.082927'`). * * Present once the destination leg has landed and the service has * reported both `amountOut` and `receivingTokenDecimals`. Omitted * otherwise — never a raw base-unit value, so consumers can render * `destination.amount` directly in a UI without worrying about unit * conversion. */ readonly amount?: string; } /** * Parameters for {@link SwapKit.getSwapStatus}. * * @remarks * Only `txHash`, `chainIn`, and `kitKey` are required. Supply `chainOut` * when the original swap was cross-chain (source chain ≠ destination * chain). * * The field names mirror {@link SwapResult.chainIn} / {@link * SwapResult.chainOut} so consumers can pipe a `SwapResult` straight into * this call without renaming. * * This call performs a single HTTP request to the Stablecoin Service and * returns the current status. Cross-chain swaps can take several minutes to * reach `'DONE'` — the caller is responsible for polling (e.g., in a `while` * loop with a `setTimeout` delay) until a terminal status is observed. * * @example * ```typescript * const result = await kit.swap(params) * * let status = await kit.getSwapStatus({ * txHash: result.txHash, * chainIn: result.chainIn, * chainOut: result.chainOut, * kitKey: process.env.KIT_KEY ?? '', * }) * while (status.progress.status === 'PENDING') { * await new Promise((r) => setTimeout(r, 3_000)) * status = await kit.getSwapStatus({ * txHash: result.txHash, * chainIn: result.chainIn, * chainOut: result.chainOut, * kitKey: process.env.KIT_KEY ?? '', * }) * } * ``` */ interface GetSwapStatusParams { /** * The source-chain transaction hash returned by {@link SwapKit.swap}. */ txHash: string; /** * Chain the swap was initiated on. * * Accepts a {@link Blockchain} enum value, a string literal of the enum, * or a full {@link ChainDefinition} (for convenience when piping through * resolved parameters). */ chainIn: SwapChainIdentifier | Blockchain; /** * Destination chain for cross-chain swaps. * * Must be supplied when the source and destination chains differ. Omit * (or match `chainIn`) for same-chain swaps. */ chainOut?: SwapChainIdentifier | Blockchain; /** * Stablecoin Service Kit Key used as a bearer credential for the status * request. Treat this value as a secret and do not log it. */ kitKey: string; } /** * Result of a swap status lookup — a single snapshot of the swap's state at * the time of the call. * * @remarks * While `progress.status === 'PENDING'` the swap is still in-flight; the * caller should re-invoke {@link SwapKit.getSwapStatus} (with an appropriate * delay) until the status becomes terminal. * * `destination.amount` is a human-readable decimal string (formatted using the * destination-token decimals reported by the service). `destination` is * populated once the destination leg lands. * * @example * ```typescript * const status = await kit.getSwapStatus(params) * if (status.progress.status === 'DONE') { * console.log(`Received ${status.destination?.amount} ${status.destination?.token?.symbol}`) * } * ``` */ interface SwapStatusResult { /** * Lifecycle snapshot: `status`, `substatus`, and `substatusMessage`. */ readonly progress: SwapProgress; /** * Source-leg transaction and token metadata. `source.txHash` is * populated from the service's `sendingTxHash`; `source.token` is * shape-reserved pending service support. */ readonly source?: SwapSourceLeg; /** * Destination-leg transaction, token, and amount metadata. Omitted until * the service reports destination-chain data (typically once the swap * reaches `'DONE'`). The received amount lives under `destination.amount`. */ readonly destination?: SwapDestinationLeg; } /** * Common knobs shared by both {@link WaitForSwapParams} variants. */ interface WaitForSwapCommonParams { /** * Stablecoin Service Kit Key used as a bearer credential. Treat as a * secret and do not log it. */ readonly kitKey: string; /** * Overall wait budget, in milliseconds. The promise rejects with a * RETRYABLE {@link KitError} when this elapses without a terminal * status. Cross-chain swaps typically settle within 30s–3min; pad the * budget if production traffic shows occasional outliers. * * Default: `300_000` (5 minutes). */ readonly timeoutMs?: number; /** * Fired on every poll that produces a status snapshot. Useful for * surfacing `substatus` transitions to a UI (e.g. spinner copy, log * lines). Not called on retried 429/5xx responses. */ readonly onProgress?: (status: SwapStatusResult) => void; } /** * `waitForSwap` parameter shape that pipes a {@link SwapResult} straight * through — the most ergonomic form when you've just called * {@link SwapKit.swap} or {@link SwapKit.executeSwap}. * * @example * ```typescript * const result = await kit.swap(params) * const final = await kit.waitForSwap({ result, kitKey }) * ``` */ interface WaitForSwapResultParams extends WaitForSwapCommonParams { /** * The {@link SwapResult} returned by {@link SwapKit.swap} or * {@link SwapKit.executeSwap}. `txHash`, `chainIn`, and `chainOut` are * read directly from this object. */ readonly result: SwapResult; } /** * `waitForSwap` parameter shape for callers that don't have a * {@link SwapResult} on hand — e.g. picking up an in-flight swap from a * persisted record or a copy-pasted tx hash. * * @example * ```typescript * const final = await kit.waitForSwap({ * txHash: '0x…', * chainIn: 'Ethereum', * chainOut: 'Base', * kitKey, * }) * ``` */ interface WaitForSwapDiscreteParams extends WaitForSwapCommonParams { /** * The source-chain transaction hash returned by {@link SwapKit.swap} or * {@link SwapKit.executeSwap}. */ readonly txHash: string; /** * Chain the swap was initiated on. Accepts a {@link Blockchain} enum * value, a string literal of the enum, or a full {@link ChainDefinition}. */ readonly chainIn: SwapChainIdentifier | Blockchain; /** * Destination chain for cross-chain swaps. Omit (or match `chainIn`) * for same-chain swaps. */ readonly chainOut?: SwapChainIdentifier | Blockchain; } /** * Parameters for {@link SwapKit.waitForSwap}. * * @remarks * `waitForSwap` polls {@link SwapKit.getSwapStatus} until `progress.status` * becomes terminal (`'DONE'`, `'FAILED'`, or `'NOT_FOUND'`) or `timeoutMs` * elapses. Same-chain swaps are already terminal at `swap`/`executeSwap` * time, so the first poll returns immediately. * * Two shapes are accepted (discriminated by the presence of `result`): * * - {@link WaitForSwapResultParams}: pass the {@link SwapResult} returned * by {@link SwapKit.swap} directly. Cleanest at the call site. * - {@link WaitForSwapDiscreteParams}: pass `txHash`, `chainIn`, and * optional `chainOut` separately. Useful when resuming an in-flight * swap from persisted state or an external source. * * @example * Passthrough — the common case after `kit.swap()`: * ```typescript * const result = await kit.swap(params) * * const final = await kit.waitForSwap({ * result, * kitKey: process.env.KIT_KEY ?? '', * onProgress: (snapshot) => console.log(snapshot.progress.status), * }) * * if (final.progress.status === 'DONE') { * console.log(`Received ${final.destination?.amount}`) * } * ``` * * @example * Discrete — resuming from a persisted tx hash: * ```typescript * const final = await kit.waitForSwap({ * txHash: persisted.txHash, * chainIn: persisted.chainIn, * chainOut: persisted.chainOut, * kitKey: process.env.KIT_KEY ?? '', * }) * ``` */ type WaitForSwapParams = WaitForSwapResultParams | WaitForSwapDiscreteParams; /** * Parameters for {@link SwapKit.getTokenRates}. * * @remarks * `chain` accepts any {@link Blockchain} value or {@link ChainDefinition} — * the rates pipeline is broader than swap routes, so the kit does not narrow * to {@link SwapChainIdentifier}. Chains the service does not track return * an empty `rates` map. * * @example * ```typescript * const params: GetTokenRatesParams = { * chain: 'Ethereum', * tokens: ['USDC', 'EURC'], * kitKey: process.env.KIT_KEY ?? '', * } * ``` */ interface GetTokenRatesParams { /** * Chain to look up rates for. Accepts a {@link Blockchain} enum value, a * string literal of the enum, or a full {@link ChainDefinition}. */ chain: ChainIdentifier$1; /** * Optional list of tokens (max 100) to look up on `chain`. Each entry may be: * * - A registered {@link TokenSymbol} (e.g. `'USDC'`, `'EURC'`). The kit * resolves it to the chain's address via the built-in `TokenRegistry`. * Symbol matching is case-insensitive. * - The literal `'NATIVE'`, or the chain's native gas symbol (e.g. `'ETH'` * on Ethereum, `'POL'` on Polygon). Both translate to the chain's native * sentinel address — `0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee` for * EVM, `11111111111111111111111111111111` for Solana — before querying * the service. * - A raw on-chain address (EVM `0x` hex) or mint (Solana base58), passed * through verbatim (EVM lowercased to match service response casing). * * Omit the field to get every cached rate for the chain. * * Unknown strings that are neither a registered symbol nor a well-formed * address are rejected with a `KitError` to surface typos at the call site * instead of returning a silently empty rate map. */ tokens?: readonly TokenSymbol[]; /** * Stablecoin Service Kit Key used as a bearer credential for the rates * request. Treat this value as a secret and do not log it. */ kitKey: string; } /** * Result of a token rates lookup. * * Nested map keyed by chain name first, then by token address. Address keys * preserve the service's canonical casing: EVM hex addresses are lowercased, * Solana base58 mints are case-preserved. Lowercase EVM addresses before * indexing into `rates[chain]`. * * @example * ```typescript * const { rates } = await kit.getTokenRates({ * chain: 'Ethereum', * tokens: ['USDC'], * kitKey: process.env.KIT_KEY ?? '', * }) * * const usdc = rates['Ethereum']?.['0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'] * if (usdc) { * console.log(`USDC: $${usdc.priceUSD} (as of ${new Date(usdc.fetchedAt).toISOString()})`) * } * ``` */ type GetTokenRatesResult = GetTokenRatesResponse; interface SwapParams { /** * The source adapter context (wallet and chain) for the swap. */ from: SwapAdapterContext; /** * The input token to swap from. * * Supports stablecoins (USDC, USDT, EURC, USDe, DAI, PYUSD), * wrapped tokens (WBTC, WETH, WSOL, WAVAX, WPOL), * and native tokens (NATIVE or chain-specific symbols). */ tokenIn: SupportedSwapToken; /** * The output token to swap to. * * Supports stablecoins (USDC, USDT, EURC, USDe, DAI, PYUSD), * wrapped tokens (WBTC, WETH, WSOL, WAVAX, WPOL), * and native tokens (NATIVE or chain-specific symbols). */ tokenOut: SupportedSwapToken; /** * The amount of the input token to swap. * * Expressed as a human-readable decimal string in token units * (e.g., `'0.05'` for 0.05 USDC or 0.05 ETH). */ amountIn: string; /** * Optional destination chain/address. * * For same-chain swaps this may be omitted and the source wallet address is * used as the recipient. Cross-chain swaps require `recipientAddress`. */ to?: SwapDestination; /** * Optional configuration for swap behavior. * * If omitted, defaults will be used: * - allowanceStrategy: 'permit' (fallback to 'approve') * - slippageBps: 300 (3%) */ config?: SwapConfig; } /** * Mutable context for SwapKit operations. * * Contains the array of swap providers, an optional custom fee policy, * and a TokenRegistry for resolving token decimals that will be used * for all swap operations initiated through this context. * * The context is designed to be mutable, allowing providers and fee policies * to be updated after initialization. The generic type parameter preserves the * exact types of all providers (default + custom) for type safety. * * @typeParam TExtraProviders - Array type of additional swap providers * * @example * ```typescript * import { SwapKitContext } from '@circle-fin/swap-kit' * import { createTokenRegistry } from '@core/tokens' * * const context: SwapKitContext = { * providers: [], * customFeePolicy: { * computeFee: async () => '0.1', * resolveFeeRecipientAddress: async () => '0x1234567890123456789012345678901234567890' * }, * tokens: createTokenRegistry() * } * ``` */ interface SwapKitContext { /** * Array of swap providers that will be used to execute swap operations. * * Providers are checked in order to find one that supports the requested swap route. * This array combines default providers with any custom providers specified in the * configuration, preserving their exact types for type safety. * * The type includes both default providers and any extra providers passed in the * configuration, enabling proper type extraction and inference. */ providers: TProviders; /** * A custom fee policy for all swaps executed through this context. * * When set, this policy will be used to calculate fees and resolve fee recipient * addresses for swap operations. */ customFeePolicy: CustomFeePolicy | undefined; /** * Token registry for resolving token decimals and metadata. * * This registry is created internally and passed to providers during swap * operations. It provides the source of truth for token decimals, with * on-chain fallback for unknown tokens. */ tokens: TokenRegistry; } /** * Configuration options for creating a SwapKit context. * * Used by the factory function to initialize the SwapKitContext with * optional providers and custom fee policy. The generic type parameter * preserves the exact types of custom providers for type safety. * * @typeParam TExtraProviders - Array type of additional swap providers * * @example * ```typescript * import { SwapKitConfig } from '@circle-fin/swap-kit' * * const config: SwapKitConfig = { * providers: [], * customFeePolicy: { * computeFee: async () => '0.1', * resolveFeeRecipientAddress: async () => '0x1234567890123456789012345678901234567890' * } * } * ``` */ interface SwapKitConfig { /** * Optional array of swap providers. * * If not provided, default providers will be initialized. * Each provider must implement the SwappingProvider interface and declare * which chains and tokens it supports. */ providers?: TExtraProviders; /** * Optional custom fee policy for swap operations. * * If provided, will be used to calculate fees and resolve recipient addresses * for all swaps executed through the created context. */ customFeePolicy?: CustomFeePolicy; /** * Disable error telemetry. * * When `true`, the SDK will not POST error details to the telemetry * endpoint when public methods throw. Defaults to `false` (enabled). * * @defaultValue false */ disableErrorReporting?: boolean; } /** * A high-level class-based interface for same-chain and cross-chain token swap operations. * * SwapKit provides a familiar class-based API for developers who prefer traditional * object-oriented patterns. The class maintains an internal context and provides * methods for estimating and executing swap operations, querying supported chains, * and managing custom fee policies. * * Key features: * - Strongly typed swap parameters with comprehensive validation * - Same-chain and cross-chain token swaps between USDC, USDT, and native tokens * - Customizable fee policies with automatic unit conversion * - Support for multiple swap providers with automatic routing * - Full TypeScript support with IntelliSense * * @example * ```typescript * import { SwapKit } from '@circle-fin/swap-kit' * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * import { Ethereum } from '@core/chains' * * // Create kit with default configuration * const kit = new SwapKit() * * // Create adapter for swap operations * const adapter = createViemAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY * }) * * // Execute a swap * const result = await kit.swap({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', * tokenOut: 'USDT', * amountIn: '100.50' * }) * * console.log(`Swap completed: ${result.txHash}`) * ``` * * @example * ```typescript * import { SwapKit } from '@circle-fin/swap-kit' * * // Create kit with custom fee policy * const kit = new SwapKit({ * customFeePolicy: { * computeFee: (params) => { * // Return fee in human-readable format (e.g., '0.1' for 0.1 USDC) * // Kit automatically converts to smallest units * return '0.1' * }, * resolveFeeRecipientAddress: (chain) => { * return chain.type === 'solana' * ? 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' * : '0x1234567890123456789012345678901234567890' * } * } * }) * ``` * * @remarks * For functional usage, import and use the operations directly: * ```typescript * import { createSwapKitContext, swap } from '@circle-fin/swap-kit' * const context = createSwapKitContext() * await swap(context, params) * ``` */ declare class SwapKit { private context; /** Whether error telemetry is disabled. */ private readonly disableErrorReporting; /** Per-kit telemetry identity for shared helpers. */ private readonly telemetryConfig; /** * Create a new SwapKit instance. * * The constructor accepts an optional configuration object to customize the * kit's behavior. If no configuration is provided, the kit will be initialized * with default settings (currently using stubbed providers that will be * implemented in future phases). * * The configuration allows you to: * - Specify custom swap providers * - Set a default fee policy for all swaps * * @param config - Optional configuration for the SwapKit instance * * @example * ```typescript * import { SwapKit } from '@circle-fin/swap-kit' * * // Create with default configuration * const kit = new SwapKit() * ``` * * @example * ```typescript * import { SwapKit } from '@circle-fin/swap-kit' * import { Blockchain } from '@core/chains' * * // Create with custom fee policy * const kit = new SwapKit({ * customFeePolicy: { * computeFee: (params) => { * // Charge different fees based on chain * return params.from.chain.chain === Blockchain.Ethereum * ? '0.1' // 0.1 USDC on Ethereum * : '0.05' // 0.05 USDC on other chains * }, * resolveFeeRecipientAddress: (chain, params) => { * // Return appropriate recipient for the chain * return chain.type === 'solana' * ? 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' * : '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' * } * } * }) * ``` */ constructor(config?: SwapKitConfig); /** * Estimate the output amount and fees for a swap operation. * * Query the configured swap providers to get a quote for swapping tokens * on the same chain or across chains. Returns the minimum output amount (stop limit), estimated * output amount, and detailed fee breakdown without executing the swap. * * This method is useful for showing users the expected output and costs before * they commit to the swap transaction. The estimate includes both provider fees * (charged by the DEX/aggregator) and kit fees (if a custom fee policy is set). * * @param params - The swap parameters including tokens, amount, and config * @returns A promise resolving to the swap estimate with output and fees * @throws {@link KitError} If validation fails or no provider supports the route * * @example * ```typescript * import { SwapKit } from '@circle-fin/swap-kit' * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * import { Ethereum } from '@core/chains' * * const kit = new SwapKit() * const adapter = createViemAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY * }) * * const quote = await kit.estimate({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', * tokenOut: 'USDT', * amountIn: '100.50', * config: { slippageBps: 300 } * }) * * console.log(`Stop limit: ${quote.stopLimit.amount} ${quote.stopLimit.token}`) * console.log(`Estimated output: ${quote.estimatedOutput?.amount} ${quote.estimatedOutput?.token}`) // Decimal format * console.log(`Fees:`, quote.fees) * ``` */ estimate(params: SwapParams): Promise; /** * Execute a token swap operation on the same chain or across chains. * * Submit a swap transaction to exchange one token for another on the same * blockchain. The method handles the complete swap flow including validation, * allowance management (via permit or approve), and transaction execution. * * Returns the swap result with transaction hash, amounts, execution state, * and fee information. The result can be used to track the swap's progress * and display transaction details to users. * * @param params - The swap parameters including tokens, amount, and config * @returns A promise resolving to the swap result with transaction details * @throws {@link KitError} If validation fails or no provider supports the route * * @example * ```typescript * import { SwapKit } from '@circle-fin/swap-kit' * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * import { Ethereum } from '@core/chains' * * const kit = new SwapKit() * const adapter = createViemAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY * }) * * const result = await kit.swap({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', * tokenOut: 'USDT', * amountIn: '100.50', * config: { * slippageBps: 300, * allowanceStrategy: 'permit' * } * }) * * console.log(`Transaction: ${result.txHash}`) * console.log(`Status: ${result.progress.status}`) * console.log(`Amount in: ${result.amountIn}`) * ``` * * @example * ```typescript * // Swap native token (ETH) for USDC * const result = await kit.swap({ * from: { adapter, chain: Ethereum }, * tokenIn: 'NATIVE', // ETH on Ethereum * tokenOut: 'USDC', * amountIn: '1.5' * }) * ``` */ swap(params: SwapParams): Promise; /** * Fetch the current status of a swap from the Stablecoin Service. * * Performs a **single** HTTP request and returns the service's snapshot of * the swap's state. For cross-chain swaps (e.g. CCTPv2-backed routes) the * status can remain `'PENDING'` for several minutes while attestation and * destination-chain mint complete; callers are responsible for polling — * re-calling this method with a delay — until the status becomes terminal * (`'DONE'`, `'FAILED'`, or `'NOT_FOUND'`). * * Keeping the polling loop in caller code gives full control over cadence, * backoff, logging of intermediate `substatus` transitions, and * cancellation via any mechanism the host application already uses. * * When the service returns `receivingTokenDecimals`, `destination.amount` * is formatted as a human-readable decimal string; when decimals are * missing the field is omitted entirely (never raw base units). * * @param params - `txHash`, `chainIn`, optional `chainOut`, and `kitKey`. * @returns A snapshot of the swap's status at the time of the call. * @throws \{KitError\} If `chainIn` or `chainOut` is malformed. * * @example * ```typescript * const result = await kit.swap(swapParams) * * // Single snapshot: * const status = await kit.getSwapStatus({ * txHash: result.txHash, * chainIn: result.chainIn, * chainOut: result.chainOut, * kitKey: process.env.KIT_KEY ?? '', * }) * console.log(status.progress.status, status.progress.substatus) * ``` * * @example * ```typescript * // Poll until terminal: * let status = await kit.getSwapStatus({ * txHash: result.txHash, * chainIn: result.chainIn, * chainOut: result.chainOut, * kitKey: process.env.KIT_KEY ?? '', * }) * while (status.progress.status === 'PENDING') { * await new Promise((r) => setTimeout(r, 3_000)) * status = await kit.getSwapStatus({ * txHash: result.txHash, * chainIn: result.chainIn, * chainOut: result.chainOut, * kitKey: process.env.KIT_KEY ?? '', * }) * } * * if (status.progress.status === 'DONE') { * console.log( * `Received ${status.destination?.amount} ${status.destination?.token?.symbol}`, * ) * } * ``` */ getSwapStatus(params: GetSwapStatusParams): Promise; /** * Poll {@link SwapKit.getSwapStatus} until the swap reaches a terminal * status (`'DONE'`, `'FAILED'`, `'NOT_FOUND'`) or `timeoutMs` elapses. * * Removes the boilerplate `while (status === 'PENDING') { sleep; refetch }` * loop that consumers otherwise write themselves. Uses an escalating * backoff (3s → 6s → 12s → 24s → 24s) so short cross-chain swaps return * quickly while long-tail ones don't hammer the service. * * Same-chain swaps return on the first poll because `executeSwap` / * `swap` already left them in a terminal state. Cross-chain swaps * typically settle within 30s–3min; raise `timeoutMs` if your * production traffic shows tail outliers. * * Transport errors from the underlying status call (network failures, * 5xx, etc.) surface unchanged so the caller can decide whether to * retry the whole wait. The wait itself only emits a timeout error. * * @param params - Wait configuration including identifiers, kit key, * optional `timeoutMs`, and an optional `onProgress` * callback fired on every successful poll. * @returns The first terminal {@link SwapStatusResult} observed. * @throws {@link KitError} (`NETWORK.TIMEOUT`, RETRYABLE) when the * wait budget elapses without a terminal status. * @throws {@link KitError} for invalid inputs (empty `txHash`, malformed * chains, non-positive `timeoutMs`). * * @example * Pipe a `SwapResult` straight in — the common case: * ```typescript * import { SwapKit } from '@circle-fin/swap-kit' * * const kit = new SwapKit() * const result = await kit.swap(swapParams) * * const final = await kit.waitForSwap({ * result, * kitKey: process.env.KIT_KEY ?? '', * onProgress: (snap) => console.log(snap.progress.substatus ?? snap.progress.status), * }) * * if (final.progress.status === 'DONE') { * console.log(`Received ${final.destination?.amount} ${final.destination?.token?.symbol}`) * } else { * console.error(`Swap ${final.progress.status}: ${final.progress.substatusMessage}`) * } * ``` * * @example * Resume from a persisted tx hash (no `SwapResult` on hand): * ```typescript * const final = await kit.waitForSwap({ * txHash: persisted.txHash, * chainIn: persisted.chainIn, * chainOut: persisted.chainOut, * kitKey: process.env.KIT_KEY ?? '', * }) * ``` */ waitForSwap(params: WaitForSwapParams): Promise; /** * Fetch cached USD rates for one or more tokens from the Stablecoin Service. * * Two lookup modes are supported: * * - **Per-chain dump**: omit `tokens` to retrieve every rate cached for * `chain`. * - **Targeted lookup**: supply `tokens` (up to 100) to retrieve a specific * set of rates on `chain`. Each entry may be a registered token symbol * from the kit's `TokenRegistry`, the literal `'NATIVE'`, the chain's * native gas symbol (e.g. `'ETH'` on Ethereum), or a raw EVM address / * Solana mint. Unknown strings are rejected with a `KitError`. * * The rates pipeline is broader than the swap pipeline — `chain` accepts * any {@link Blockchain} value or {@link ChainDefinition}, not just the * swap-supported subset. Chains the cron does not track return an empty * `rates` map. * * Response keys preserve the service's canonical casing: EVM hex addresses * are lowercased, Solana base58 mints are case-preserved. Lowercase EVM * addresses before indexing into `result.rates[chain]`. * * Native gas rates: `'NATIVE'` (or a chain's native currency symbol) * translates to the chain's native sentinel address — `0xEee…` for EVM, * `1111…` for Solana — before querying the service. If the service does * not cache a rate for the native asset on that chain, the response simply * omits the address. * * @param params - `chain`, optional `tokens`, and `kitKey`. * @returns A nested map of `[chain][address] → { priceUSD, fetchedAt }`. * @throws \{KitError\} If `chain` is malformed, `tokens` exceeds 100 * entries, or any entry is neither a registered symbol nor a * well-formed address for `chain`. * * @example * Symbol lookup: * ```typescript * const { rates } = await kit.getTokenRates({ * chain: 'Ethereum', * tokens: ['USDC', 'EURC', 'NATIVE'], * kitKey: process.env.KIT_KEY ?? '', * }) * * const usdc = rates['Ethereum']?.['0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'] * console.log(`USDC: $${usdc?.priceUSD ?? 'unknown'}`) * ``` * * @example * Mixed symbols and raw addresses: * ```typescript * const { rates } = await kit.getTokenRates({ * chain: 'Ethereum', * // 'USDC' (registry symbol) + raw USDT address * tokens: ['USDC', '0xdAC17F958D2ee523a2206206994597C13D831ec7'], * kitKey: process.env.KIT_KEY ?? '', * }) * ``` * * @example * Per-chain dump: * ```typescript * const { rates } = await kit.getTokenRates({ * chain: 'Base', * kitKey: process.env.KIT_KEY ?? '', * }) * ``` */ getTokenRates(params: GetTokenRatesParams): Promise; /** * Get all chains supported by the configured swap providers. * * Aggregate and deduplicate the supported chains from all registered providers. * This provides a comprehensive list of chains that can be used for swap operations * through this kit instance. * * The method automatically deduplicates chains based on their chain identifier, * ensuring each chain appears only once in the result regardless of how many * providers support it. This is a synchronous operation that queries the static * chain definitions from providers without making network calls. * * @returns Array of unique chain definitions supported by the providers * * @example * ```typescript * import { SwapKit } from '@circle-fin/swap-kit' * import { Blockchain } from '@core/chains' * * const kit = new SwapKit() * const chains = kit.getSupportedChains() * * console.log('Supported chains:') * chains.forEach(chain => { * console.log(`- ${chain.name} (${chain.type})`) * }) * * // Check if a specific chain is supported * const isEthereumSupported = chains.some( * c => c.chain === Blockchain.Ethereum * ) * ``` */ getSupportedChains(): ChainDefinition[]; /** * Set a custom fee policy for all swap operations. * * Configure a custom fee policy that will be applied to all subsequent swap * operations performed with this kit instance. The fee policy determines how * fees are calculated and where they are sent for each swap. * * The method validates the fee policy and wraps the `computeFee` function * to automatically convert human-readable fee amounts to the smallest token units: * - For USDC/USDT: converts decimal strings to 6-decimal precision (e.g., "0.1" → "100000") * - For NATIVE: uses the chain's native currency decimals from chain definition * * This allows you to specify fees in a natural decimal format while ensuring * they are properly formatted for on-chain transactions. * * @param policy - The custom fee policy containing fee calculation and recipient resolution logic * @throws {@link ValidationError} If the custom fee policy is invalid or missing required functions * * @example * ```typescript * import { SwapKit } from '@circle-fin/swap-kit' * import { Blockchain } from '@core/chains' * * const kit = new SwapKit() * * kit.setCustomFeePolicy({ * computeFee: (params) => { * // Return decimal string - automatically converted to smallest units * // '0.1' becomes '100000' (0.1 USDC with 6 decimals) * return params.from.chain.chain === Blockchain.Ethereum * ? '0.1' // 0.1 USDC * : '0.2' // 0.2 USDC * }, * resolveFeeRecipientAddress: (feePayoutChain, params) => { * // Return a valid address for the fee payout chain * return feePayoutChain.chain === Blockchain.Ethereum * ? '0x23f9a5BEA7B92a0638520607407BC7f0310aEeD4' * : '0x1E1A18B7bD95bcFcFb4d6E245D289C1e95547b35' * } * }) * ``` * * @example * ```typescript * // Set a fixed fee policy (simplest approach) * kit.setCustomFeePolicy({ * computeFee: () => { * return '0.1' // 0.1 USDC/USDT fee * }, * resolveFeeRecipientAddress: (feePayoutChain) => { * // Return appropriate address for the chain type * return feePayoutChain.type === 'solana' * ? 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' * : '0x1234567890123456789012345678901234567890' * } * }) * ``` */ setCustomFeePolicy(policy: CustomFeePolicy): void; /** * Remove the custom fee policy from the kit. * * Clear any previously configured fee policy, returning the kit to the default * state where no custom fees are applied to swap operations. After calling this * method, subsequent swaps will not include any kit-level fees unless a new * policy is set via `setCustomFeePolicy()`. * * This is useful when you want to temporarily disable fee collection or when * switching between different fee configurations. * * @example * ```typescript * import { SwapKit } from '@circle-fin/swap-kit' * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * import { Ethereum } from '@core/chains' * * const kit = new SwapKit() * const adapter = createViemAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY * }) * * // Set a custom fee policy * kit.setCustomFeePolicy({ * computeFee: () => '1.0', // 1 USDC * resolveFeeRecipientAddress: () => '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' * }) * * // This swap will include the custom fee * await kit.swap({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', * tokenOut: 'USDT', * amountIn: '100.00' * }) * * // Remove the fee policy * kit.removeCustomFeePolicy() * * // This swap will NOT include any custom fee * await kit.swap({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', * tokenOut: 'USDT', * amountIn: '100.00' * }) * ``` */ removeCustomFeePolicy(): void; } /** * The default providers that will be used in addition to the providers provided * to the createSwapKitContext factory function. * * @returns An array containing the default StablecoinServiceSwapProvider * @internal */ declare const getDefaultProviders: () => readonly [StablecoinServiceSwapProvider]; /** * Type alias for the default providers returned by getDefaultProviders. * * Exported to allow proper type extraction from SwapKitContext. */ type DefaultProviders = ReturnType; /** * Create a SwapKit context with validated configuration. * * This factory function initializes a SwapKitContext with default providers * and optional custom configuration. It validates any provided custom fee * policy and merges default and custom providers, preserving their exact * types for type safety. * * The function is pure and side-effect-free, returning a plain context object * that can be used with swap operations. Default providers are currently * stubbed and will be implemented in future phases. * * @typeParam TExtraProviders - Array type of additional swap providers * @param config - Optional configuration for the SwapKit context * @returns A fully initialized SwapKitContext ready for swap operations * @throws \{ValidationError\} If the custom fee policy is invalid * * @example * ```typescript * import { createSwapKitContext } from '@circle-fin/swap-kit' * * // Create context with defaults * const context = createSwapKitContext() * ``` * * @example * ```typescript * import { createSwapKitContext } from '@circle-fin/swap-kit' * * // Create context with custom fee policy * const context = createSwapKitContext({ * customFeePolicy: { * computeFee: async (params) => { * // Calculate based on swap amount * return '0.1' // 0.1 USDC * }, * resolveFeeRecipientAddress: async (chain, params) => { * // Return recipient based on chain * if (chain.type === 'solana') { * return 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' * } * return '0x1234567890123456789012345678901234567890' * } * } * }) * ``` * * @example * ```typescript * import { createSwapKitContext } from '@circle-fin/swap-kit' * * // Create context with custom providers (future use) * const context = createSwapKitContext({ * providers: [] // Custom swap providers will be supported in future phases * }) * ``` */ declare function createSwapKitContext(config?: SwapKitConfig): SwapKitContext<[...DefaultProviders, ...TExtraProviders]>; /** * Estimates the output amount and fees for a swap operation. * * Queries the configured swap providers to get a quote for swapping tokens * on the same chain or across chains. Returns minimum output amount (stop limit), estimated * output amount, fee breakdown, and input context fields * * @typeParam TFromAdapterCapabilities - The adapter capabilities type for the source adapter * @param context - The SwapKit context containing providers and fee policy * @param params - The swap parameters including tokens, amount, and config * @returns A promise resolving to the swap estimate with input context * * @throws \{ValidationError\} If validation fails * @throws \{UnsupportedSwapRouteError\} If no provider supports the route * * @example * ```typescript * import { createSwapKitContext, estimate } from '@circle-fin/swap-kit' * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * import { Ethereum } from '@core/chains' * * const context = createSwapKitContext() * const adapter = createViemAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY * }) * * const quote = await estimate(context, { * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', * tokenOut: 'USDT', * amountIn: '100.50', * config: { slippageBps: 300 } * }) * * // Input context is included in the result * console.log(`Chain: ${quote.chain}`) // Blockchain.Ethereum * console.log(`From: ${quote.fromAddress}`) * console.log(`Amount: ${quote.amountIn}`) * console.log(`Stop limit: ${quote.stopLimit.amount} ${quote.stopLimit.token}`) * console.log(`Estimated output: ${quote.estimatedOutput?.amount} ${quote.estimatedOutput?.token}`) * ``` */ declare function estimate(context: SwapKitContext, params: SwapParams): Promise; /** * Executes a token swap operation on the same chain or across chains. * * This function performs a complete swap workflow including parameter * validation, resolution, provider selection, and transaction execution. * The swap happens on a single blockchain network between two supported * tokens (USDC, USDT, or native token). * * The function handles: * - Parameter validation using Zod schemas * - Chain and token resolution * - Custom fee policy merging (context-level and inline config) * - Provider selection based on route support * - Transaction execution via the selected provider * - Result transformation to kit-level format * * @typeParam TFromAdapterCapabilities - The adapter capabilities type * @param context - The SwapKit context containing providers and fee policy * @param params - The swap parameters including tokens, amount, and config * @param onBroadcast - Internal callback invoked once with the transaction * hash immediately after the provider's broadcast * succeeds. Used by `SwapKit.swap()` to capture the * hash for error telemetry when post-broadcast logic * throws. Not part of the supported public surface for * functional-style consumers — leave unset. * * A callback that throws is swallowed and surfaced as * a `console.warn` prefixed with * `[stablecoin-kits swap-kit] callback threw and was * swallowed:` (the raw `Error` is passed as the * second `console.warn` argument so the stack * survives). * @returns A promise resolving to the swap result with transaction details * * @throws \{KitError\} If the parameters fail schema validation or swap execution fails * @throws \{UnsupportedRouteError\} If no provider supports the swap route * * @example * ```typescript * import { createSwapKitContext, swap } from '@circle-fin/swap-kit' * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * * const context = createSwapKitContext() * const adapter = createViemAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY * }) * * const result = await swap(context, { * from: { adapter, chain: 'Ethereum' }, * tokenIn: 'USDC', * tokenOut: 'USDT', * amountIn: '100.50', * config: { * slippageBps: 300, * allowanceStrategy: 'permit' * } * }) * * console.log(`Transaction: ${result.txHash}`) * ``` * * @example * ```typescript * // Swap with custom fee policy * const context = createSwapKitContext({ * customFeePolicy: { * computeFee: async () => '0.01', // 0.01 USDC * resolveFeeRecipientAddress: async () => '0x1234...' * } * }) * * const result = await swap(context, { * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', * tokenOut: 'NATIVE', // ETH on Ethereum * amountIn: '50.00' * }) * ``` */ declare function swap(context: SwapKitContext, params: SwapParams, /** @internal */ onBroadcast?: (txHash: string) => void): Promise; /** * Fetch the current status of a swap from the Stablecoin Service. * * This usually performs a single HTTP request and returns the service's * current snapshot; on a suspected indexing race it sleeps briefly and * retries once. For cross-chain swaps the status may be `'PENDING'` for * several minutes; callers are responsible for polling — i.e. re-calling * this function with a delay — until `progress.status` is terminal * (`'DONE'`, `'FAILED'`, or `'NOT_FOUND'`). * * `destination.amount` is formatted as a human-readable decimal string when * the service returns `receivingTokenDecimals`. When decimals are missing * the field is omitted entirely (never raw base units). * * @param params - `txHash`, `chainIn`, optional `chainOut`, and `kitKey`. * @returns A single snapshot of the swap's status. * @throws \{KitError\} If `chainIn` or `chainOut` is malformed. * * @example * ```typescript * import { getSwapStatus, swap, createSwapKitContext } from '@circle-fin/swap-kit' * * const context = createSwapKitContext() * const result = await swap(context, params) * * // Poll until terminal * let status = await getSwapStatus({ * txHash: result.txHash, * chainIn: result.chainIn, * chainOut: result.chainOut, * kitKey: process.env.KIT_KEY ?? '', * }) * while (status.progress.status === 'PENDING') { * await new Promise((r) => setTimeout(r, 3_000)) * status = await getSwapStatus({ * txHash: result.txHash, * chainIn: result.chainIn, * chainOut: result.chainOut, * kitKey: process.env.KIT_KEY ?? '', * }) * } * * if (status.progress.status === 'DONE') { * console.log(`Received ${status.destination?.amount} ${status.destination?.token?.symbol}`) * } * ``` */ declare function getSwapStatus(params: GetSwapStatusParams): Promise; /** * Poll {@link getSwapStatus} until the swap reaches a terminal status or * the wait budget expires. * * Terminal statuses are `'DONE'`, `'FAILED'`, and `'NOT_FOUND'`. The * returned snapshot carries the same shape as a one-shot * `getSwapStatus` call; branch on `result.progress.status` to handle * success vs failure. * * Polling cadence is an escalating backoff (3s → 6s → 12s → 24s → 24s, * repeating). Same-chain swaps short-circuit on the first poll since the * service marks them terminal at submission. Transport errors from the * underlying status call surface unchanged so the caller can decide * whether to retry the whole wait. * * Two parameter shapes are accepted: * * - Pass the {@link SwapResult} directly: `{ result, kitKey, ... }`. * Cleanest when chaining off `kit.swap()`. * - Pass discrete fields: `{ txHash, chainIn, chainOut?, kitKey, ... }`. * Useful for resuming an in-flight swap from a persisted record. * * @param params - Wait configuration. See {@link WaitForSwapParams}. * @returns The first terminal {@link SwapStatusResult} observed. * @throws {@link KitError} (`NETWORK.TIMEOUT`, RETRYABLE) when * `timeoutMs` elapses before a terminal status is reached. * @throws {@link KitError} when `txHash` is empty or `timeoutMs` is * non-positive. * * @example * Pipe a `SwapResult` straight in — the common case: * ```typescript * import { waitForSwap, swap, createSwapKitContext } from '@circle-fin/swap-kit' * * const context = createSwapKitContext() * const result = await swap(context, params) * * const final = await waitForSwap({ * result, * kitKey: process.env.KIT_KEY ?? '', * onProgress: (snapshot) => { * console.log(snapshot.progress.substatus ?? snapshot.progress.status) * }, * }) * * if (final.progress.status === 'DONE') { * console.log(`Received ${final.destination?.amount}`) * } * ``` * * @example * Resume from a persisted tx hash: * ```typescript * const final = await waitForSwap({ * txHash: '0x…', * chainIn: 'Ethereum', * chainOut: 'Base', * kitKey: process.env.KIT_KEY ?? '', * }) * ``` */ declare function waitForSwap(params: WaitForSwapParams): Promise; /** * Fetch cached USD rates for one or more tokens from the Stablecoin Service. * * Two lookup modes are supported: * * - **Per-chain dump**: omit `tokens` to retrieve every rate cached for * `chain`. * - **Targeted lookup**: supply `tokens` (up to 100) to retrieve a specific * set of rates on `chain`. Each entry may be: * - A registered token symbol (e.g. `'USDC'`, `'EURC'`) — resolved to the * chain's address via the kit's `TokenRegistry`. Case-insensitive. * - The literal `'NATIVE'`, or the chain's native gas symbol (e.g. `'ETH'` * on Ethereum, `'POL'` on Polygon) — translates to the chain's native * sentinel address (`0xEee…` for EVM, `1111…` for Solana) before * querying the service. * - A raw EVM address or Solana mint — passed through verbatim. * * Unknown strings that are neither a registered symbol nor a well-formed * address are rejected with a structured `KitError` so typos surface at the * call site instead of returning a silently empty rate map. * * The rates pipeline is broader than the swap pipeline — `chain` accepts any * {@link Blockchain} value (or `ChainDefinition`), not just the swap-supported * subset. Chains the rate cron does not track return an empty `rates` map. * * @remarks * Response keys preserve the service's canonical casing: EVM hex addresses * are lowercased, Solana base58 mints are case-preserved. Lowercase EVM * addresses before indexing into `result.rates[chain]`. * * Rates are refreshed once per minute and expire after 15 minutes. Use * `result.rates[chain][address].fetchedAt` when freshness matters. * * Native gas rates: passing `'NATIVE'` (or a chain's native currency symbol) * translates to the native sentinel before querying the service. If the * service does not cache a rate for the native asset on that chain, the * response simply omits the address — same behavior as any uncached token. * * @param context - SwapKit context (provides the `TokenRegistry` used for * symbol resolution). * @param params - `chain`, optional `tokens`, and `kitKey`. * @returns A nested map of `[chain][address] → { priceUSD, fetchedAt }`. * @throws \{KitError\} If `chain` cannot be resolved, `tokens` exceeds 100 * entries, or any entry is neither a registered symbol nor a * well-formed address for `chain`. * * @example * ```typescript * import { createSwapKitContext, getTokenRates } from '@circle-fin/swap-kit' * import { Blockchain } from '@core/chains' * * const context = createSwapKitContext() * const { rates } = await getTokenRates(context, { * chain: Blockchain.Ethereum, * tokens: ['USDC', 'EURC', 'NATIVE'], * kitKey: process.env.KIT_KEY ?? '', * }) * * const usdc = rates['Ethereum']?.['0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'] * console.log(`USDC: $${usdc?.priceUSD ?? 'unknown'}`) * ``` */ declare function getTokenRates(context: SwapKitContext, params: GetTokenRatesParams): Promise; /** * Get the list of chains supported by the configured swap providers. * * @remarks * Query all configured providers to determine which chains support token swap * operations. Chains are automatically deduplicated by chain identifier, with * later providers' definitions taking precedence over earlier ones in case of * duplicates. * * This operation is efficient and synchronous - it will aggregate the static chain * definitions from each provider without making network calls. The result * reflects the current provider configuration and automatically updates when * providers are added or removed from the context. * * @param context - The SwapKit context containing the configured providers * @returns An array of unique chain definitions supported across all providers * * @example * ```typescript * import { createSwapKitContext, getSupportedChains, Blockchain } from '@circle-fin/swap-kit' * * const context = createSwapKitContext() * const supportedChains = getSupportedChains(context) * * console.log(`Supported chains: ${supportedChains.map(c => c.name).join(', ')}`) * * // Check if a specific chain is supported * const isEthereumSupported = supportedChains.some( * c => c.chain === Blockchain.Ethereum * ) * ``` */ declare function getSupportedChains(context: SwapKitContext): ChainDefinition[]; /** * Set a custom fee policy on a SwapKit context. * * This function validates and configures a custom fee policy for all swap operations * performed with the given context. The fee policy determines how fees are calculated * and where they are sent for each swap. * * The function wraps the provided `computeFee` function to: * 1. Convert input amount from smallest units to human-readable format before calling computeFee * 2. Convert the returned fee from human-readable format back to smallest units * * Supported tokens and their decimals: * - USDC/USDT: 6 decimals (e.g., "0.1" → "100000") * - NATIVE: Uses chain's native currency decimals from chain definition * * This function follows an immutable pattern, returning a new context with the * wrapped policy attached. The original context remains unchanged. * * @typeParam TProviders - The tuple of swap providers in the context, preserved through the returned context * @param context - The SwapKitContext to configure with the custom fee policy * @param customFeePolicy - The custom fee policy containing fee calculation and recipient resolution logic * @returns A new SwapKitContext\ with the custom fee policy configured, preserving the original provider types * @throws \{ValidationError\} If the custom fee policy is invalid or missing required functions * @throws \{KitError\} If the token is not supported (not USDC, USDT, or NATIVE) * * @example * ```typescript * import { createSwapKitContext, setCustomFeePolicy } from '@circle-fin/swap-kit' * import { Blockchain } from '@core/chains' * * let context = createSwapKitContext() * * // Capture the returned context (immutable pattern) * context = setCustomFeePolicy(context, { * computeFee: (ctx) => { * // All amounts are human-readable (e.g., '100' for 100 USDC) * if (ctx.type === 'output') { * // Same-chain output fee: fee taken from output token * // Use estimatedAmount or minAmount for calculation * return (parseFloat(ctx.estimatedAmount) * 0.01).toString() * } * // Input fee: fee taken from input token. * // Cross-chain swaps always use this branch. * return (parseFloat(ctx.amountIn) * 0.01).toString() * }, * resolveFeeRecipientAddress: (feePayoutChain, params) => { * // Return a valid address for the fee payout chain * return feePayoutChain.chain === Blockchain.Ethereum * ? '0x23f9a5BEA7B92a0638520607407BC7f0310aEeD4' * : '0x1E1A18B7bD95bcFcFb4d6E245D289C1e95547b35' * }, * }) * ``` */ declare function setCustomFeePolicy(context: SwapKitContext, customFeePolicy: CustomFeePolicy): SwapKitContext; /** * Remove the custom fee policy from the SwapKit context. * * Return a new context with no custom fee policy configured, restoring it * to the default state where no custom fees are applied to swap operations. * Subsequent swaps using the returned context will not include any kit-level * fees unless a new policy is set. * * This function follows an immutable pattern—the original context remains * unchanged. This is useful when you want to temporarily disable fee collection * or when switching between different fee configurations. * * @param context - The SwapKit context to clear the fee policy from * @returns A new SwapKitContext with the custom fee policy removed * * @example * ```typescript * import { * createSwapKitContext, * setCustomFeePolicy, * removeCustomFeePolicy, * swap * } from '@circle-fin/swap-kit' * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * import { Ethereum } from '@core/chains' * * let context = createSwapKitContext() * const adapter = createViemAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY * }) * * // Set a custom fee policy (capture returned context) * context = setCustomFeePolicy(context, { * computeFee: () => '0.1', // 0.1 USDC fee * resolveFeeRecipientAddress: () => '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' * }) * * // This swap will include the custom fee * await swap(context, { * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', * tokenOut: 'USDT', * amountIn: '100.00' * }) * * // Remove the fee policy (capture returned context) * context = removeCustomFeePolicy(context) * * // This swap will NOT include any custom fee * await swap(context, { * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', * tokenOut: 'USDT', * amountIn: '100.00' * }) * ``` */ declare function removeCustomFeePolicy(context: SwapKitContext): SwapKitContext; /** * Resolves swap parameters from user input to provider-consumable format. * * Transforms SwapParams with chain identifiers and token aliases into * ResolvedSwapParams with full chain definitions, canonicalized token aliases, * and validated wallet addresses. Uses the TokenRegistry from context for * decimal resolution. * * Note: Raw user input is validated first, then `NATIVE` may be canonicalized * to a stablecoin symbol on chains whose native gas token is itself a * supported stablecoin (for example, Arc Testnet `NATIVE` → `USDC`). * Address resolution is handled by the provider layer. * * @typeParam TFromAdapterCapabilities - The adapter capabilities type for the source adapter * @param params - The input swap parameters to resolve * @param tokens - TokenRegistry for resolving token decimals * @returns The resolved swap parameters ready for provider consumption * * @throws \{KitError\} If resolution fails (invalid chain, unsupported token, address validation, etc.) * * @example * ```typescript * import { resolveSwapParams } from '@circle-fin/swap-kit' * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2' * import { Ethereum } from '@core/chains' * import { createTokenRegistry } from '@core/tokens' * * const adapter = createViemAdapterFromPrivateKey({ * privateKey: process.env.PRIVATE_KEY * }) * const tokens = createTokenRegistry() * * const resolved = await resolveSwapParams({ * from: { adapter, chain: Ethereum }, * tokenIn: 'USDC', * tokenOut: 'USDT', * amountIn: '100.5' * }, tokens) * * // resolved.tokenIn === 'USDC' // Canonicalized for provider routing * // resolved.tokenOut === 'USDT' // Canonicalized for provider routing * // resolved.to === wallet address from adapter * ``` */ declare function resolveSwapParams(params: SwapParams, tokens: TokenRegistry): Promise>; /** * Schema for validating swap parameters with chain identifiers. * * This schema validates the complete swap operation input, ensuring: * - Valid adapter context (adapter + chain + optional address) * - Valid tokenIn and tokenOut (symbols or addresses) * - Valid amountIn as a positive decimal string * - Optional valid configuration * * The schema validates amounts with up to 18 decimal places to support * various token standards. * * @example * ```typescript * import { swapParamsSchema } from '@circle-fin/swap-kit' * * // Using token symbols (recommended for supported tokens) * const paramsWithSymbols = { * from: { * adapter: sourceAdapter, * chain: 'Ethereum' * }, * tokenIn: 'USDC', * tokenOut: 'USDT', * amountIn: '100.50', // 100.50 USDC * config: { * slippageBps: 300, * allowanceStrategy: 'permit' * } * } * * // Using token addresses (works for any token) * const paramsWithAddresses = { * from: { * adapter: sourceAdapter, * chain: 'Base' * }, * tokenIn: '0x4200000000000000000000000000000000000006', // WETH address * tokenOut: '0x532f27101965dd16442E59d40670FaF5eBB142E4', // Any token address * amountIn: '0.1' // 0.1 WETH * } * * // Using native token alias * const paramsWithNative = { * from: { * adapter: sourceAdapter, * chain: 'Ethereum' * }, * tokenIn: 'NATIVE', // ETH on Ethereum, MATIC on Polygon, etc. * tokenOut: 'USDC', * amountIn: '1.5' // 1.5 ETH * } * * const result = swapParamsSchema.safeParse(paramsWithSymbols) * if (result.success) { * console.log('Parameters are valid') * } else { * console.error('Validation failed:', result.error) * } * ``` */ declare const swapParamsSchema: z.ZodType; /** * Schema for validating SwapKit custom fee policy. * * Validates the shape of CustomFeePolicy, which lets SDK consumers * provide custom fee calculation and fee-recipient resolution logic. * * - computeFee: required function that returns a fee as a string (or Promise). * - resolveFeeRecipientAddress: required function that returns a recipient address as a * string (or Promise). * * This schema only ensures the presence and return types of the functions; it * does not validate their argument types. * * @example * ```typescript * import { customFeePolicySchema } from '@circle-fin/swap-kit' * * const config = { * computeFee: async () => '0.1', * resolveFeeRecipientAddress: () => '0x1234567890123456789012345678901234567890', * } * * const result = customFeePolicySchema.safeParse(config) * // result.success === true * ``` */ declare const customFeePolicySchema: z.ZodObject<{ computeFee: z.ZodFunction, z.ZodUnion<[z.ZodString, z.ZodPromise]>>; resolveFeeRecipientAddress: z.ZodFunction, z.ZodUnion<[z.ZodString, z.ZodPromise]>>; }, "strict", z.ZodTypeAny, { computeFee: (...args: unknown[]) => string | Promise; resolveFeeRecipientAddress: (...args: unknown[]) => string | Promise; }, { computeFee: (...args: unknown[]) => string | Promise; resolveFeeRecipientAddress: (...args: unknown[]) => string | Promise; }>; /** * Assert that the provided value conforms to {@link CustomFeePolicy}. * * This function validates the custom fee policy configuration using the * customFeePolicySchema. It ensures that both required functions * (computeFee and resolveFeeRecipientAddress) are present and have * the correct return types. * * Throws a structured error with detailed validation messages if the * configuration is malformed. Uses state tracking to avoid duplicate * validations. * * @param config - The custom fee policy to validate * @throws \{KitError\} If the policy fails validation * * @example * ```typescript * import { assertCustomFeePolicy } from '@circle-fin/swap-kit' * * const config = { * computeFee: () => '0.1', * resolveFeeRecipientAddress: () => '0x1234567890123456789012345678901234567890', * } * * try { * assertCustomFeePolicy(config) * // If no error is thrown, `config` is a valid CustomFeePolicy * } catch (error) { * console.error('Invalid fee policy:', error.message) * } * ``` */ declare function assertCustomFeePolicy(config: unknown): asserts config is CustomFeePolicy; /** * Assert that the provided value conforms to the SwapParams schema. * * This function validates swap parameters using the provided Zod schema * and tracks validation state to avoid duplicate checks. It performs * comprehensive validation including: * - Adapter context structure * - Token specifications * - Amount format and range * - Optional configuration values * * Throws a structured error with detailed validation messages if * any parameter is invalid. * * @typeParam T - The expected type after validation * @param params - The swap parameters to validate * @param schema - The Zod schema to validate against * @throws \{KitError\} If the parameters fail validation * * @example * ```typescript * import { assertSwapParams, swapParamsSchema } from '@circle-fin/swap-kit' * * const params = { * from: { adapter: sourceAdapter, chain: 'Ethereum' }, * tokenIn: 'USDC', * tokenOut: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', * amountIn: '100.50' * } * * try { * assertSwapParams(params, swapParamsSchema) * // Parameters are valid, proceed with swap * } catch (error) { * console.error('Invalid parameters:', error.message) * } * ``` */ declare function assertSwapParams(params: unknown, schema: z.ZodSchema): asserts params is T; export { Blockchain, KitError, SwapChain, SwapKit, assertCustomFeePolicy, assertSwapParams, createSwapKitContext, customFeePolicySchema, estimate, getChainByEnum, getErrorCode, getErrorMessage, getSupportedChains, getSwapStatus, getTokenRates, isFatalError, isInputError, isKitError, isRetryableError, removeCustomFeePolicy, resolveSwapParams, setCustomFeePolicy, setExternalPrefix, swap, swapParamsSchema, waitForSwap }; export type { AdapterContext, AllowanceStrategy, ChainDefinition, ChainIdentifier$1 as ChainIdentifier, CustomFeePolicy, ErrorDetails, GetSwapStatusParams, GetTokenRatesParams, GetTokenRatesResult, Recoverability, SwapAdapterContext, SwapChainDefinition, SwapChainIdentifier, SwapConfig, SwapDestinationLeg, SwapEstimate, SwapFeeContext, SwapInputFeeContext, SwapKitConfig, SwapKitContext, SwapOutputFeeContext, SwapParams, SwapProgress, SwapResult, SwapSourceLeg, SwapStatus, SwapStatusResult, SwapTerminalStatus, TokenRate, WaitForSwapDiscreteParams, WaitForSwapParams, WaitForSwapResultParams };