/** * 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 { Chain, PublicClient, WalletClient, Abi as Abi$1, TransactionReceipt, EIP1193Provider } from 'viem'; import { Abi } from 'abitype'; import { TransactionInstruction, Signer, AddressLookupTableAccount } from '@solana/web3.js'; import { z } from '/home/runner/_work/stablecoin-kits-private/stablecoin-kits-private/node_modules/zod/dist/types/index.d.ts'; import { PrivateKeyAccount } from '/home/runner/_work/stablecoin-kits-private/stablecoin-kits-private/node_modules/viem/_types/accounts/index.d.ts'; /** * @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 CCTPx configuration. * * @description When provided, the chain supports CCTPx (Cross-Chain Token Service). * CCTPx is a service-level protocol layered on top of CCTP v2's message-passing layer * that enables cross-chain transfers of registered tokens (Circle-issued or otherwise). * * The CCTS contract is deployed via CREATE3 so its address is deterministic and may * be committed to chain config ahead of the on-chain deployment. * * Use the {@link isCCTPXSupported} type guard to check if a chain has CCTPx support * before accessing this property. * * @example * ```typescript * if (isCCTPXSupported(chain)) { * console.log('CCTS address:', chain.cctpx.serviceAddress) * } * ``` * * @see {@link CCTPXChainConfig} for the structure of CCTPx configuration. * @see {@link isCCTPXSupported} for checking CCTPx support. */ cctpx?: CCTPXChainConfig; /** * 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; /** * Address of the `TokenMessengerWithFees` wrapper, when deployed on this chain. * * Optional. Present only on chains that support the prepaid FORWARD path * (source-chain fee collection via `depositForBurnWithHookAndFees`). Resolve * it with `resolveCCTPV2ContractAddress(chain, 'tokenMessengerWithFees')`. */ tokenMessengerWithFees?: 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; /** * Address of the `TokenMessengerWithFees` wrapper, when deployed on this chain. * * Optional. Present only on chains that support the prepaid FORWARD path * (source-chain fee collection via `depositForBurnWithHookAndFees`). Resolve * it with `resolveCCTPV2ContractAddress(chain, 'tokenMessengerWithFees')`. */ tokenMessengerWithFees?: 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; }; } /** * Configuration for Circle's Cross-Chain Token Service (CCTS) — the CCTPx protocol. * * @category Types * * @description Contains the CCTS proxy contract address on a given chain. The CCTS * contract is the service-level entry point for CCTPx cross-chain transfers of * registered tokens (Circle-issued or otherwise). Addresses are deterministic via CREATE3 * and may be committed to chain config ahead of the on-chain deploy. * * @example * ```typescript * const cctpxConfig: CCTPXChainConfig = { * serviceAddress: '0x1234567890abcdef1234567890abcdef12345678' * } * ``` */ interface CCTPXChainConfig { /** * The CrossChainTokenService (CCTS) proxy contract address on this chain. * * @description Deterministic CREATE3 address. Used by the SDK as the `to` field * when calling `crossChainTransfer` and `resolveTokenManager`. * * @example "0x1234567890abcdef1234567890abcdef12345678" */ serviceAddress: string; } /** * 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; /** * The address of the `DepositForHandler` contract. * * @description Optional. The handler the GenericExecutor calls on this chain * to run a fast cross-chain deposit into the {@link GatewayV1Contracts.wallet}. * Present only on chains that are fast-deposit destinations; other Gateway * chains omit it. * * 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 "0xD05E7D2E7d30b92c5F17d7d0fC575fce231F1A48" */ depositForHandler?: string; /** * The address of the `GenericExecutor` contract. * * @description Optional. The contract that acts as `mintRecipient` and * `destinationCaller` for the CCTP v2 prepaid FORWARD path. It receives the * CCTP mint and calls {@link GatewayV1Contracts.depositForHandler} to * complete the fast deposit into the {@link GatewayV1Contracts.wallet}. * Present only on chains that are fast-deposit destinations; other Gateway * chains omit it. * * @example "0xFa7be2f04F3Ad4ca969260729c6d45B5625984A7" */ genericExecutor?: 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", Arbitrum = "Arbitrum", Arbitrum_Sepolia = "Arbitrum_Sepolia", Arc = "Arc", Arc_Testnet = "Arc_Testnet", 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", Cronos = "Cronos", Cronos_Testnet = "Cronos_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", Plasma = "Plasma", Plasma_Testnet = "Plasma_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", X_Layer = "X_Layer", X_Layer_Testnet = "X_Layer_Testnet", ZKSync_Era = "ZKSync_Era", ZKSync_Sepolia = "ZKSync_Sepolia" } /** * 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>; /** * Token selector accepted by adapters and the static * {@link TokenRegistry.resolve} method. * * @remarks * Keep this alias at adapter and registry boundaries so their accepted token * forms remain explicit even when other product surfaces define narrower * token inputs. */ type RegistryTokenSelector = 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; } /** * Options for creating a token registry. * * @remarks * The registry always includes built-in tokens (USDC, etc.) by default. * Use `tokens` to add custom token definitions that will be merged with * the built-ins. Custom tokens with the same symbol as a built-in will * override it. * * @example * ```typescript * // Add custom tokens (USDC is still available) * const registry = createTokenRegistry({ * tokens: [myCustomToken], * }) * * // Override USDC with custom definition * const registry = createTokenRegistry({ * tokens: [customUsdcDefinition], * }) * ``` */ interface TokenRegistryOptions { /** * Additional token definitions to register. * These are merged with built-in tokens (DEFAULT_TOKENS). * Tokens with the same symbol as a built-in will override it. */ readonly tokens?: readonly TokenDefinition[]; /** * Require decimals when using raw locator selectors. * When true, raw selectors without decimals will throw. * @defaultValue false */ readonly requireDecimals?: boolean; } /** * 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. Accepts a 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: RegistryTokenSelector, 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; }; } /** * Simplified error information structure for logging and events. * * @remarks * This lightweight type is used for error reporting in events, logs, and * observability systems. It provides essential error context without the * full ErrorDetails structure. Used across retry mechanisms, adapters, * and other subsystems that need to record error information. * * @example * ```typescript * import { ErrorInfo } from '@core/errors' * * const info: ErrorInfo = { * name: 'NETWORK_TIMEOUT', * message: 'Request timed out after 5000ms', * code: 3002 * } * ``` */ interface ErrorInfo { /** Error name (e.g., 'TypeError', 'KitError', 'NETWORK_TIMEOUT'). */ name: string; /** Error message describing what went wrong. */ message: string; /** Optional error code if the error has one (e.g., KitError codes). */ code?: number; /** Error category (e.g., INPUT, RPC, ONCHAIN, BALANCE, NETWORK, UNKNOWN). Only set for KitError instances. */ type?: string; } 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); } /** * Create a token registry with built-in tokens and optional extensions. * * @remarks * The registry always includes built-in tokens (DEFAULT_TOKENS) like USDC. * Custom tokens are merged on top - use this to add new tokens or override * built-in definitions. * * @param options - Configuration options for the registry. * @returns A token registry instance. * * @example * ```typescript * import { createTokenRegistry } from '@core/tokens' * * // Create registry with built-in tokens (USDC, etc.) * const registry = createTokenRegistry() * * // Resolve USDC on Ethereum * const usdc = registry.resolve('USDC', 'Ethereum') * console.log(usdc.locator) // '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' * console.log(usdc.decimals) // 6 * ``` * * @example * ```typescript * // Add custom tokens (built-ins are still included) * const myToken: TokenDefinition = { * symbol: 'MY', * decimals: 18, * locators: { Ethereum: '0x...' }, * } * * const registry = createTokenRegistry({ tokens: [myToken] }) * registry.resolve('USDC', 'Ethereum') // Still works! * registry.resolve('MY', 'Ethereum') // Also works * ``` * * @example * ```typescript * // Override a built-in token * const customUsdc: TokenDefinition = { * symbol: 'USDC', * decimals: 6, * locators: { MyChain: '0xCustomAddress' }, * } * * const registry = createTokenRegistry({ tokens: [customUsdc] }) * // Now USDC resolves to customUsdc definition * ``` * * @example * ```typescript * // Resolve arbitrary tokens by raw locator * const registry = createTokenRegistry() * const token = registry.resolve( * { locator: '0x1234...', decimals: 18 }, * 'Ethereum' * ) * ``` */ declare function createTokenRegistry(options?: TokenRegistryOptions): TokenRegistry; /** * Structured fields that can be attached to log entries. */ type LogFields = Record; /** * Logger interface providing structured logging with child scoping. * * @remarks * This interface defines a minimal, framework-agnostic logging contract. * The underlying implementation uses pino for transport handling (console, * file, remote, JSON, pretty, etc.) but consumers only interact with this * stable interface. * * @example * ```typescript * import { createLogger } from '@core/runtime' * * const logger = createLogger({ level: 'debug' }) * * // Simple message * logger.info('Server started') * * // Message with structured fields * logger.info('Request received', { method: 'POST', path: '/api/transfer' }) * * // Create child logger with context * const requestLogger = logger.child({ requestId: 'abc-123' }) * requestLogger.debug('Processing transfer') * // Output includes: requestId in all subsequent logs * ``` */ interface Logger { /** * Log a debug-level message. * @param message - The log message. * @param fields - Optional structured fields. * @returns void */ debug(message: string, fields?: LogFields): void; /** * Log an info-level message. * @param message - The log message. * @param fields - Optional structured fields. * @returns void */ info(message: string, fields?: LogFields): void; /** * Log a warning-level message. * @param message - The log message. * @param fields - Optional structured fields. * @returns void */ warn(message: string, fields?: LogFields): void; /** * Log an error-level message. * @param message - The log message. * @param fields - Optional structured fields. * @returns void */ error(message: string, fields?: LogFields): void; /** * Create a child logger with additional contextual bindings. * * @param tags - Key-value pairs to add to the child logger's context. * Undefined values are filtered out automatically. * @returns A new Logger instance with merged bindings. * * @example * ```typescript * const requestLogger = logger.child({ requestId: 'req-123' }) * const userLogger = requestLogger.child({ userId: 'user-456' }) * // All logs from userLogger include both requestId and userId * ``` */ child(tags: LogFields): Logger; } /** * Handler function for event subscriptions. */ type EventHandler = (event: Event) => void | Promise; /** * Event bus for publishing and subscribing to events. * * @remarks * Supports wildcard topic subscriptions: * - `*` matches exactly one segment * - `**` matches zero or more segments * * @example * ```typescript * const bus = createEventBus() * * // Subscribe to all events * bus.on((event) => console.log(event)) * * // Subscribe to specific topic * bus.on('tx.wait.started', (event) => console.log(event)) * * // Subscribe with wildcard * bus.on('tx.wait.*', (event) => console.log(event)) * bus.on('tx.**', (event) => console.log(event)) * * // Emit events * bus.emit({ name: 'tx.wait.started', data: { txId: '0x123' } }) * ``` */ interface EventBus { /** * Emit an event to all matching subscribers. * * @param event - The event to emit. * @remarks * Synchronous and never throws. Handler errors are isolated. */ emit(event: Event): void; /** * Create a child event bus with scoped tags. * * @param tags - Tags to merge into all emitted events. * @returns A new EventBus with merged tags. */ child(tags: Tags): EventBus; /** * Subscribe to all events. * * @param handler - Function called for every event. * @returns Unsubscribe function. */ on(handler: EventHandler): () => void; /** * Subscribe to events matching a pattern. * * @param pattern - Topic pattern (supports `*` and `**` wildcards). * @param handler - Function called for matching events. * @returns Unsubscribe function. * * @remarks * Wildcard semantics: * - `*` matches exactly one segment (no dots) * - `**` matches zero or more segments (only valid at end) * * @example * ```typescript * bus.on('*', handler) // matches 'tx', 'user' (single segment only) * bus.on('tx.wait.*', handler) // matches tx.wait.started, tx.wait.failed * bus.on('tx.wait.**', handler) // matches tx.wait, tx.wait.started, tx.wait.foo.bar * bus.on('**', handler) // matches all events * ``` */ on(pattern: string, handler: EventHandler): () => void; } /** * Lifecycle event types and constants for pipeline phase tracking. * * @packageDocumentation */ /** * Terminal state of a lifecycle phase. * * @remarks * Exported as a discriminated union so typed consumers of the event * stream (agents, dashboards, test assertions) can exhaustively switch * on phase outcomes instead of string-comparing event names. * * @remarks * Carried on every lifecycle event's `data.status` (see * {@link LifecycleEventData}) so consumers can switch on the outcome * without re-deriving it from the event name. * * @example * ```typescript * import type { LifecycleEventData } from '@core/runtime' * * function handle(data: LifecycleEventData) { * switch (data.status) { * case 'started': return // ... * case 'succeeded': return // ... * case 'failed': return // ... * } * } * ``` */ type OperationPhaseStatus = 'started' | 'succeeded' | 'failed'; /** * Data payload for lifecycle events. * * @remarks * These events are emitted by pipeline middleware to track phase execution. * Field availability by event: * - `op.phase.started`: `status`, `meta`, `inputSummary` * - `op.phase.succeeded`: `status`, `meta`, `inputSummary`, `durationMs` * - `op.phase.failed`: `status`, `meta`, `inputSummary`, `durationMs`, `error` */ interface LifecycleEventData { /** * Terminal status of the phase, mirroring the {@link OperationPhaseStatus} * discriminant carried by the event name. Present on every lifecycle * event so typed consumers can `switch (data.status)` without parsing * the event name. */ status: OperationPhaseStatus; /** Metadata from the pipeline context. */ meta?: Record | undefined; /** Shallow summary of input (type + keys) to avoid PII exposure. */ inputSummary?: { type: string; keys?: string[]; } | undefined; /** Duration of the phase in milliseconds (only for succeeded/failed). */ durationMs?: number; /** Error information (only for failed events). */ error?: ErrorInfo | undefined; } /** * Augmentable event type registry for the App Kits ecosystem. * * @remarks * This module provides a type-safe event system using TypeScript's * module augmentation pattern. Packages can extend `KitEventMap` * to register their own event types. * * **How to Augment:** * * Other packages can add their own event types by augmenting the * `KitEventMap` interface: * * ```typescript * // In your package's types file (e.g., my-package/src/types.ts) * declare module '@core/runtime' { * interface KitEventMap { * 'tx.wait.started': { txId: string } * 'tx.wait.completed': { txId: string; confirmations: number } * 'tx.wait.failed': { txId: string; error: string } * } * } * ``` * * After augmentation, the event system will be fully typed: * * ```typescript * // Type-safe event emission * emit('tx.wait.started', { txId: '0x123' }) // ✓ OK * emit('tx.wait.started', { wrong: 'field' }) // ✗ Type error * emit('unknown.event', {}) // ✗ Type error (if strict) * ``` * * @example * ```typescript * // bridge-kit/src/events.ts * declare module '@core/runtime' { * interface KitEventMap { * 'bridge.transfer.initiated': { * sourceChain: string * destChain: string * amount: string * } * 'bridge.transfer.completed': { * txHash: string * duration: number * } * } * } * ``` */ /** * The core event map interface for module augmentation. * * @remarks * This interface pre-registers the lifecycle events that are always * emitted by the pipeline middleware. Packages can extend it via * TypeScript's declaration merging to register additional event types. * * **Pre-registered events:** * - `op.phase.started` - Emitted when a pipeline phase begins * - `op.phase.succeeded` - Emitted when a pipeline phase completes successfully * - `op.phase.failed` - Emitted when a pipeline phase throws an error * * @example * ```typescript * // Add your own events via module augmentation * declare module '@core/runtime' { * interface KitEventMap { * 'my.custom.event': { payload: string } * } * } * ``` */ interface KitEventMap { 'op.phase.started': LifecycleEventData; 'op.phase.succeeded': LifecycleEventData; 'op.phase.failed': LifecycleEventData; } /** * Generic event map type for event-related constraints. * * @remarks * Use this when you need to accept any event map without requiring * the specific `KitEventMap` augmentations. * * Uses `any` to accept both interface and type declarations. * `unknown` would reject interfaces without index signatures. */ type EventMap = Record; /** * Type-level event pattern matching utilities. * * @remarks * These types provide compile-time pattern matching for event keys, * mirroring the runtime matcher semantics: * - Delimiter: `.` * - `*` matches exactly one segment * - `**` matches zero or more segments (only valid as last segment) * - Exact matching otherwise * - Invalid patterns (empty segments, `**` not last) yield `never` * * See `matching.test-d.ts` for type-level tests. */ /** * Extract string keys from an event map. * * @typeParam M - The event map type. * @returns Union of string keys from the map. */ type Keys = Extract; /** * Filter keys that match a pattern. * * @typeParam K - Union of string keys to filter. * @typeParam P - Pattern to match against. * @returns Union of keys that match the pattern, or `never` if pattern is invalid. * * @example * ```typescript * type Events = { * 'tx.wait.started': { txId: string } * 'tx.wait.failed': { error: string } * 'tx.send.completed': { hash: string } * } * * // Match single segment wildcard * type WaitEvents = MatchKeys * // = 'tx.wait.started' | 'tx.wait.failed' * * // Match multi-segment wildcard * type AllTxEvents = MatchKeys * // = 'tx.wait.started' | 'tx.wait.failed' | 'tx.send.completed' * * // Match all events * type All = MatchKeys * // = keyof Events * ``` */ type MatchKeys = ParsePattern

extends never ? never : ParsePattern

extends infer PSegs extends string[] ? K extends unknown ? MatchKey : never : never; /** * Split a string by '.' delimiter into a tuple of segments. * Returns `never` if any segment is empty (handles `.a`, `a.`, `a..b`). */ type SplitDot = S extends '' ? [] : S extends `${infer Head}.${infer Tail}` ? Head extends '' ? never : Tail extends '' ? never : SplitDot extends infer Rest ? Rest extends never ? never : Rest extends string[] ? [Head, ...Rest] : never : never : [S]; /** * Validate that `**` only appears as the last segment (or is the entire pattern). * Returns `true` if valid, `false` if invalid. */ type ValidateGlob = Segs extends [] ? true : Segs extends [infer Head extends string, ...infer Tail extends string[]] ? Head extends '**' ? Tail extends [] ? true : false : ValidateGlob : true; /** * Parse and validate a pattern string. * Returns the segments tuple if valid, `never` if invalid. */ type ParsePattern

= SplitDot

extends infer Segs ? Segs extends never ? never : Segs extends string[] ? ValidateGlob extends true ? Segs : never : never : never; /** * Match a single key against parsed pattern segments. * Returns the key if it matches, `never` otherwise. */ type MatchKey = SplitDot extends infer KSegs ? KSegs extends never ? never : KSegs extends string[] ? MatchSegments extends true ? K : never : never : never; /** * Recursively match key segments against pattern segments. * * Rules: * - If pattern exhausted: key must also be exhausted * - If pattern head is `**`: match (since it's validated to be last) * - If pattern head is `*`: consume one key segment * - Otherwise: exact segment match required */ type MatchSegments = PSegs extends [] ? KSegs extends [] ? true : false : PSegs extends [infer PHead extends string, ...infer PTail extends string[]] ? PHead extends '**' ? true : KSegs extends [ infer KHead extends string, ...infer KTail extends string[] ] ? PHead extends '*' ? MatchSegments : PHead extends KHead ? MatchSegments : false : false : false; /** * Type-safe wrapper for the untyped EventBus. * * @remarks * This module provides compile-time type safety for event emission * and subscription while maintaining zero runtime overhead. */ /** * A strongly-typed event with known name and data types. * * @typeParam Name - The event name literal type. * @typeParam Data - The event payload type. */ type TypedEvent = Omit & { name: Name; data: Data; }; /** * Create a discriminated union of typed events from an event map. * * @typeParam M - The event map. * @typeParam K - The keys to include (defaults to all keys). * * @remarks * This creates a proper discriminated union where TypeScript can narrow * the `data` type based on checking the `name` field. */ type EventUnion = Keys> = { [N in K]: TypedEvent; }[K]; /** * Handler for typed events. */ type TypedEventHandler = (event: E) => void | Promise; /** * Metrics type definitions for the Runtime module. * * @remarks * Define a minimal, pluggable metrics interface that can be backed by any * metrics library (hot-shots, dd-trace, prom-client, etc.). * * The interface supports: * - Counters for monotonically increasing values * - Histograms for distributions (latencies, sizes) * - Timers as a convenience wrapper for timing operations * - Label scoping via `child()` for dimensional metrics * * @example * ```typescript * // Basic usage * metrics.counter('requests.total').inc({ method: 'POST' }) * metrics.histogram('request.duration').observe({ status: 200 }, 42.5) * * // Timer convenience * const stop = metrics.timer('db.query').start({ table: 'users' }) * await query() * stop() // Records duration automatically * * // Scoping adds base labels to all metrics * const scoped = metrics.child({ service: 'bridge', env: 'prod' }) * scoped.counter('transfers').inc() // Includes service + env labels * ``` */ /** * Labels for dimensional metrics. * * @remarks * Labels (also called tags in some systems) are key-value pairs that * provide dimensions for metric aggregation and filtering. * Values must be primitives for serialization compatibility. * * @example * ```typescript * const labels: MetricLabels = { * chain: 'Ethereum', * status: 'success', * retries: 3, * cached: true, * } * ``` */ type MetricLabels = Record; /** * A counter metric for monotonically increasing values. * * @remarks * Use counters for values that only go up: request counts, error counts, * bytes processed, etc. The value resets only on process restart. * * @see {@link Metrics.counter} to obtain a Counter instance. * * @example * ```typescript * const counter = metrics.counter('http.requests') * * // Increment by 1 * counter.inc() * counter.inc({ method: 'GET' }) * * // Increment by specific value * counter.inc(5) * counter.inc({ method: 'POST' }, 3) * ``` */ interface Counter { /** * Increment the counter value. * * @param labelsOrValue - The labels object or increment value. * When a number, increments by that amount with no labels. * When an object, uses as labels with optional value in second param. * @param value - The increment value when first arg is labels. Default: 1. * @returns void * * @example * ```typescript * counter.inc() // +1, no labels * counter.inc(5) // +5, no labels * counter.inc({ method: 'GET' }) // +1, with labels * counter.inc({ method: 'POST' }, 3) // +3, with labels * ``` */ inc(labelsOrValue?: MetricLabels | number, value?: number): void; } /** * A histogram metric for recording value distributions. * * @remarks * Use histograms for values that vary and need percentile analysis: * request durations, response sizes, queue depths, etc. * * @see {@link Metrics.histogram} to obtain a Histogram instance. * * @example * ```typescript * const histogram = metrics.histogram('http.duration') * * // Record a value * histogram.observe(42.5) * histogram.observe({ status: 200 }, 42.5) * ``` */ interface Histogram { /** * Record an observation value. * * @param labelsOrValue - The labels object or observed value. * When a number, records that value with no labels. * When an object, uses as labels with value in second param. * @param value - The observed value when first arg is labels. Default: 0. * @returns void * * @example * ```typescript * histogram.observe(42.5) // Value only * histogram.observe({ status: 200 }, 42.5) // With labels * ``` */ observe(labelsOrValue?: MetricLabels | number, value?: number): void; } /** * A timer metric for measuring operation durations. * * @remarks * Timers provide a convenience wrapper that automatically records durations * to an underlying histogram. Call `start()` to begin timing and the * returned function to stop and record the elapsed time in milliseconds. * * @see {@link Metrics.timer} to obtain a Timer instance. * * @example * ```typescript * const timer = metrics.timer('db.query') * * // Start timing * const stop = timer.start({ table: 'users' }) * await performQuery() * stop() // Records duration in milliseconds * ``` */ interface Timer { /** * Start timing an operation. * * @param labels - The optional labels for the timing observation. * @returns A stop function that records the duration when called. * * @example * ```typescript * const stop = timer.start({ operation: 'fetch' }) * await fetchData() * stop() // Records elapsed time * ``` */ start(labels?: MetricLabels): () => void; } /** * Main metrics interface for instrumentation. * * @remarks * This interface is designed to be thin and pluggable. Implementations * can delegate to any metrics library: * * - **hot-shots**: StatsD/DogStatsD client * - **dd-trace**: Datadog APM * - **prom-client**: Prometheus * - **opentelemetry-js**: OpenTelemetry * * The `child()` method creates a scoped metrics instance that automatically * includes base labels on all metric operations. * * @see {@link createMockMetrics} for testing. * @see {@link noopMetrics} for a no-op implementation. * * @example * ```typescript * // Create a scoped metrics instance * const appMetrics = metrics.child({ * service: 'app-kit', * version: '1.0.0', * }) * * // All metrics include service + version labels * appMetrics.counter('transfers.initiated').inc({ chain: 'ethereum' }) * ``` */ interface Metrics { /** * Get or create a counter metric by name. * * @param name - The metric name (e.g., 'http.requests.total'). * @returns A Counter instance for the given name. * * @example * ```typescript * const counter = metrics.counter('requests.total') * counter.inc({ method: 'GET' }) * ``` */ counter(name: string): Counter; /** * Get or create a histogram metric by name. * * @param name - The metric name (e.g., 'http.request.duration'). * @returns A Histogram instance for the given name. * * @example * ```typescript * const histogram = metrics.histogram('request.duration') * histogram.observe({ status: 200 }, 42.5) * ``` */ histogram(name: string): Histogram; /** * Get or create a timer metric by name. * * @param name - The metric name (e.g., 'db.query.duration'). * @returns A Timer instance for the given name. * * @example * ```typescript * const stop = metrics.timer('db.query').start() * await query() * stop() * ``` */ timer(name: string): Timer; /** * Create a child metrics instance with scoped labels. * * @param labels - The base labels to include on all metric operations. * @returns A new Metrics instance with merged labels. * * @remarks * Labels from the child are merged with any labels passed to individual * metric operations. Call-site labels take precedence for the same key. * * @example * ```typescript * const scoped = metrics.child({ chain: 'Ethereum' }) * scoped.counter('transfers').inc({ status: 'success' }) * // Labels: { chain: 'Ethereum', status: 'success' } * ``` */ child(labels: MetricLabels): Metrics; } /** * Backoff strategy for retry delays. * * @remarks * - `constant` - Same delay between each retry * - `linear` - Delay increases linearly (baseDelay * attempt) * - `exponential` - Delay doubles each attempt (baseDelay * 2^attempt) */ type RetryBackoff = 'constant' | 'linear' | 'exponential'; /** * Options for the retry middleware. * * @example * ```typescript * const retry = createRetryMiddleware({ * maxAttempts: 3, * baseDelayMs: 200, * backoff: 'exponential', * }) * ``` */ interface RetryOptions { /** * Maximum number of retry attempts (not including the initial attempt). * * @remarks * Total attempts = 1 (initial) + maxAttempts (retries) * * @defaultValue 3 */ maxAttempts?: number; /** * Base delay between retries in milliseconds. * @defaultValue 200 */ baseDelayMs?: number; /** * Maximum delay between retries in milliseconds. * @defaultValue 10000 (10 seconds) */ maxDelayMs?: number; /** * Backoff strategy for calculating retry delays. * @defaultValue 'exponential' */ backoff?: RetryBackoff; /** * Whether to add jitter (±25% randomness) to the delay. * @defaultValue true */ jitter?: boolean; /** * Custom function to determine if an error is retryable. * * @remarks * If not provided, uses the default from `@core/errors` which * considers network errors and rate limits as retryable. */ isRetryable?: (error: unknown) => boolean; } /** * Core type definitions for the runtime package. * * @remarks * This module defines the foundational types used throughout the SDK: * * - {@link Runtime} - Complete runtime with all services (clock, logger, metrics, events) * - {@link ExecutionContext} - Context for middleware with observability surface * - {@link Clock}, {@link Tags}, {@link Event} - Supporting types * * **Naming Convention** * * | Input Type | Resolved Type | Description | * |------------|---------------|-------------| * | `Partial` | `Runtime` | Runtime services | * | `OperationMeta` | `OperationContext` | WHAT - operation target | * | `InvocationMeta` | `InvocationContext` | WHO/HOW - call chain | * * @packageDocumentation */ /** * Transaction-related behavioral settings. * * @remarks * Controls how transactions are priced and confirmed. * All fields are optional — each adapter falls back to its own * defaults when a value is not provided. * * | Setting | Used by | Default | * |---------|---------|---------| * | `feePriceBufferBps` | execute (EVM), calculateFee | 2 000 (20%) | * | `minPriorityFeeWei` | execute (EVM) | 1.5 gwei | * | `confirmationTimeoutMs` | waitForTransaction | client default | * * **EVM adapters** — `feePriceBufferBps` is injected into viem's * `chain.fees.baseFeeMultiplier` (e.g. 2 000 bps → 1.2×). This * preserves viem's full gas handling pipeline while applying the * buffer natively. A `minPriorityFeeWei` floor is enforced as a * safety net for chains/RPCs that report near-zero priority fees. * * @example * ```typescript * import type { TransactionConfig } from '@core/runtime' * * const txConfig: TransactionConfig = { * feePriceBufferBps: 2000n, // 20% buffer on gas pricing * confirmationTimeoutMs: 120_000, // 2 min confirmation timeout * } * ``` */ interface TransactionConfig { /** * Buffer applied to gas/fee pricing in basis points. * * @remarks * Protects against price fluctuations between estimation and block * inclusion. * * **EVM (viem)** — maps to `chain.fees.baseFeeMultiplier`, which * buffers the base fee (EIP-1559) or legacy gas price natively * through viem's pipeline. * * **Fee estimation** — used as the default buffer for * `calculateFee` when `bufferBasisPoints` is not passed per-call. * * - 500 = 5% * - 2000 = 20% * - 5000 = 50% * * @defaultValue `2000n` (20%) — matches the MetaMask default and * provides reliable block inclusion with free/public RPCs. */ readonly feePriceBufferBps?: bigint | undefined; /** * Minimum priority fee floor in wei (EVM only). * * @remarks * Free/public RPCs frequently report a `maxPriorityFeePerGas` near * zero. On chains with near-zero base fees (e.g. Avalanche where * `baseFeePerGas` can be 1 wei), the entire gas price depends on the * priority fee alone. Without a floor, transactions are silently * dropped by validators. * * This floor is applied as the minimum value for * `maxPriorityFeePerGas`. If the RPC reports a higher value, the * RPC value is used instead. * * On EVM, unused gas is always refunded — only `gasUsed * * effectiveGasPrice` is charged. A generous floor increases * willingness-to-pay but not actual cost when the network price is * lower. * * @defaultValue `1_500_000_000n` (1.5 gwei) */ readonly minPriorityFeeWei?: bigint | undefined; /** * Default timeout for waiting for transaction confirmation in milliseconds. * * @remarks * Applied by `waitForTransaction` when no per-call `timeout` is * specified. When omitted, the underlying client's default is used. * * @defaultValue `undefined` (client default) */ readonly confirmationTimeoutMs?: number | undefined; } /** * Operational configuration for behavioral settings. * * @remarks * Groups operational behavior settings that control how operations execute. * This type is used at multiple levels of the stack: * * - **Adapter level**: Default config for all operations via `AdapterContext.config` * - **Invocation level**: Per-call overrides via `InvocationMeta.config` * * **Design Rationale** * * A single `OperationalConfig` type ensures consistency across the stack. * Settings cascade from adapter defaults → invocation overrides. * * This design is extensible - future settings (timeout, circuit breaker, etc.) * can be added without polluting higher-level interfaces. * * @example * ```typescript * import type { OperationalConfig } from '@core/runtime' * * // Adapter-level defaults * const adapterConfig: OperationalConfig = { * retry: { maxAttempts: 3, baseDelayMs: 1000 }, * transaction: { feePriceBufferBps: 1000n }, * } * * // Per-call override * const callConfig: OperationalConfig = { * retry: { maxAttempts: 5 }, * } * ``` */ interface OperationalConfig { /** * Retry configuration for transient failure handling. * * @remarks * Retry is **enabled by default** with exponential backoff for retryable * errors (network failures, rate limits, etc.). Pass a `RetryOptions` * object to customise behaviour, or `false` to disable retries entirely. */ readonly retry?: RetryOptions | false | undefined; /** * Transaction pricing, caching, and confirmation settings. * * @remarks * Controls gas/fee price buffering, cache TTL, and confirmation * timeouts. Each primitive falls back to sensible defaults when * individual fields are omitted. */ readonly transaction?: TransactionConfig | undefined; } /** * Clock interface for time operations. * * @remarks * Abstracting time allows tests to control timing without real delays. * Production code uses {@link defaultClock}, tests use mock implementations. * * @example * ```typescript * import { defaultClock, type Clock } from '@core/runtime' * * const start = defaultClock.now() * // ... do work ... * const elapsed = defaultClock.since(start) * ``` */ interface Clock { /** * Return the current timestamp in milliseconds since Unix epoch. * * @returns Current time in milliseconds. */ now(): number; /** * Calculate elapsed time since a given timestamp. * * @param start - The start timestamp in milliseconds. * @returns Elapsed time in milliseconds (`now() - start`). */ since: (start: number) => number; } /** * Contextual metadata tags for logging and metrics. * * @remarks * Tags are key-value pairs attached to log entries and metrics. * Values can be primitives or undefined (undefined values are filtered out). * * Common tags include: * - `opId` - Operation identifier for correlation * - `chain` - Blockchain network name * - `phase` - Current pipeline phase * * @example * ```typescript * import type { Tags } from '@core/runtime' * * const tags: Tags = { * opId: 'op-abc123', * chain: 'Ethereum', * phase: 'validate', * optional: undefined, // Will be filtered out * } * ``` */ type Tags = Record; /** * A structured event emitted by the runtime. * * @remarks * Events provide a standardized way to capture lifecycle moments, * actions, and state changes throughout the SDK. */ interface Event { /** The event name/identifier. */ name: string; /** Log level for the event. */ level?: 'debug' | 'info' | 'warn' | 'error'; /** Timestamp (epoch ms) when the event occurred. */ at?: number; /** Contextual tags for filtering/categorization. */ tags?: Tags; /** Arbitrary payload data associated with the event. */ data?: unknown; } /** * Complete runtime with all services guaranteed present. * * @remarks * The runtime is the container for all cross-cutting concerns: logging, * timing, events, and metrics. All services are guaranteed to be available. * * **Creating a Runtime** * * Use {@link createRuntime} to create a fully-configured runtime: * ```typescript * import { createRuntime } from '@core/runtime' * * const runtime = createRuntime() * runtime.logger.info('Hello') * runtime.metrics.counter('requests').inc() * ``` * * **Partial Runtime Input** * * When accepting runtime configuration as input, use `Partial`: * ```typescript * function myFunction(options: { runtime?: Partial }) { * const runtime = createRuntime(options.runtime) * // ... * } * ``` * * @example * ```typescript * import { createRuntime, type Runtime } from '@core/runtime' * * const runtime: Runtime = createRuntime() * * // All services are guaranteed present * runtime.logger.info('Processing request') * runtime.metrics.counter('requests').inc() * runtime.events.emit({ name: 'request.received' }) * const now = runtime.clock.now() * ``` */ interface Runtime { /** * Clock for time operations. * * @remarks * Provides the current timestamp. Use {@link defaultClock} for production * or custom clocks for deterministic testing. */ clock: Clock; /** * Logger for structured logging. * * @remarks * Provides debug, info, warn, error methods with structured data support. */ logger: Logger; /** * Metrics collector for observability. * * @remarks * Provides counters, histograms, and timers for application metrics. */ metrics: Metrics; /** * Event bus for pub/sub events. * * @remarks * Enables decoupled event emission and subscription across the SDK. */ events: EventBus; } /** * A component in the call chain. * * @remarks * Each caller identifies itself with a type (app, kit, provider, adapter) * and a name/version. This enables proper attribution in logs, metrics, and traces. * * @example * ```typescript * import type { Caller } from '@core/runtime' * * const appCaller: Caller = { type: 'app', name: 'MyDApp', version: '1.0.0' } * const kitCaller: Caller = { type: 'kit', name: 'BridgeKit', version: '2.0.0' } * ``` */ interface Caller { /** * Type of component in the call hierarchy. * * @remarks * Common types: `app`, `kit`, `provider`, `adapter` */ readonly type: string; /** Name of the component (e.g., 'BridgeKit', 'cctp-v2'). */ readonly name: string; /** Version of the component (e.g., '1.0.0'). */ readonly version?: string | undefined; } /** * User input for invocation metadata. * * @remarks * Defines **WHO** called and **HOW** to observe: trace correlation, runtime override, * and caller chain. This is the user-facing input type, resolved to `InvocationContext`. * * Passed as the optional invocation argument to actions and primitives. * * @example * ```typescript * import type { InvocationMeta } from '@core/runtime' * * // Minimal - just traceId * const meta: InvocationMeta = { traceId: 'abc-123' } * * // Full - with runtime override and caller chain * const meta: InvocationMeta = { * traceId: 'abc-123', * runtime: myRuntime, * callers: [ * { type: 'app', name: 'MyDApp', version: '1.0.0' }, * ], * } * ``` */ interface InvocationMeta { /** * Trace ID for distributed tracing correlation. * * @remarks * If not provided, generated automatically. */ readonly traceId?: string | undefined; /** * Runtime override (complete replacement). * * @remarks * When provided, this runtime completely replaces the default runtime. * Must be a complete Runtime instance (e.g., from `createRuntime()`). * If not provided, the default runtime is used. */ readonly runtime?: Runtime | undefined; /** * Token registry override (complete replacement). * * @remarks * When provided, this registry completely replaces the default token registry. * Enables kits to pass their token registry to adapters. * If not provided, the default token registry is used. */ readonly tokens?: TokenRegistry | undefined; /** * Call chain - each caller appends itself. * * @remarks * Ordered from outermost (first) to innermost (last). * Example: [app, kit, provider] */ readonly callers?: readonly Caller[] | undefined; /** * Cooperative cancellation signal for the invocation. * * @remarks * When provided, the signal is threaded onto the resolved * {@link InvocationContext} so operations can forward it to * cancellable work (e.g. `fetch`, timers, adapter calls). Optional and * backwards-compatible: callers that do not support cancellation simply * omit it. Aborting the signal is the caller's responsibility; the * runtime only propagates it. */ readonly signal?: AbortSignal | undefined; } /** * Resolved invocation context with guaranteed values. * * @remarks * Defines **WHO** called and **HOW** to observe. Contains the complete runtime * and guaranteed trace ID. * * Created by `resolveInvocationContext()` from `InvocationMeta`. * * @example * ```typescript * import type { InvocationContext } from '@core/runtime' * * const ctx: InvocationContext = { * traceId: 'abc-123', * runtime: completeRuntime, * callers: [{ type: 'app', name: 'MyDApp' }], * } * ``` */ interface InvocationContext { /** * Trace ID for distributed tracing correlation. * * @remarks * Guaranteed to be present (generated if not provided in input). */ readonly traceId: string; /** * Complete runtime with all services. * * @remarks * All services (clock, logger, events, metrics) are guaranteed present. */ readonly runtime: Runtime; /** * Token registry for resolving token information. * * @remarks * Guaranteed to be present. Uses provided registry or falls back to default. */ readonly tokens: TokenRegistry; /** * Call chain (empty array if not provided). */ readonly callers: readonly Caller[]; /** * Cooperative cancellation signal for the invocation. * * @remarks * Mirrors {@link InvocationMeta.signal}. Present only when the caller * supplied one — the runtime never fabricates a signal. Operations may * forward it to cancellable work; aborting remains the caller's * responsibility. */ readonly signal?: AbortSignal | undefined; } /** * Context passed to error normalizers for richer error messages. * * @remarks * A partial view of execution context used when normalizing errors. * All fields are optional since errors can occur at various points in * the execution lifecycle where not all context is available. */ interface ErrorContext { /** Trace ID for correlation. */ readonly traceId?: string | undefined; /** Name of the operation where the error occurred. */ readonly name?: string | undefined; /** * Observability tags for the operation. * * @remarks * This is where the canonical `chain` dimension lives — it is populated * by `buildOperationTags` on the execution context. Error normalizers * should read the chain name from here (tags-first) and treat * {@link ErrorContext.meta} only as a fallback overlay. */ readonly tags?: Tags | undefined; /** Additional metadata (chain, adapter, etc.). */ readonly meta?: Record | undefined; } /** * Function signature for error normalizers. * * @remarks * Normalizers inspect an error and either: * - **Throw a KitError** if they recognize the error type * - **Return void** to allow fallback handling * * This design is simpler than returning intermediate types because: * - Adapters use `@core/errors` factories directly * - No intermediate type conversion needed * - Clear control flow (throw = handled, return = not handled) * * **Adapter implementors** should handle at least these categories: * * | Category | Example framework errors | Maps to (`@core/errors`) | * |----------|--------------------------|--------------------------| * | Revert | `ContractFunctionRevertedError` | `createTransactionRevertedError` | * | Insufficient funds | `InsufficientFundsError` | `createInsufficientFundsError` | * | User rejection | `UserRejectedRequestError` | `createUserRejectedError` | * | RPC / transport | `HttpRequestError`, `TimeoutError` | `createRpcError` | * | Nonce conflict | `NonceTooLowError` | `createNonceError` | * * @param err - The error to normalize. * @param ctx - Optional context for richer error messages. * * @example * ```typescript * import type { ErrorNormalizer } from '@core/runtime' * import { createTransactionRevertedError } from '@core/errors' * * export const viemErrorNormalizer: ErrorNormalizer = (err, ctx) => { * if (err instanceof ContractFunctionRevertedError) { * throw createTransactionRevertedError( * ctx?.chain ?? 'Unknown', * err.reason ?? 'Transaction reverted', * { rawError: err } * ) * } * // Not recognized - let fallback handle it * } * ``` */ type ErrorNormalizer = (err: unknown, ctx?: ErrorContext) => void; /** * Factory for creating Runtime instances with defaults. * * @packageDocumentation */ /** * Configuration options for {@link createRuntime}. * * @remarks * Only `logger` and `metrics` can be overridden. Internal services * (`clock`, `events`) are managed by the SDK. */ interface RuntimeOptions { /** * Custom logger implementation. * * @remarks * Integrate with your existing logging infrastructure (pino, winston, etc.). * Must implement debug, info, warn, error, and child methods. * * Default: pino logger at 'info' level. */ logger?: Logger; /** * Custom metrics implementation. * * @remarks * Plug into your observability stack (Prometheus, StatsD, DataDog, etc.). * Must implement counter, histogram, timer, and child methods. * * Default: No-op metrics (no collection). */ metrics?: Metrics; } /** * Create a complete Runtime with sensible defaults. * * @param options - Optional configuration to override logger and metrics. * @returns A frozen, immutable Runtime with all services guaranteed present. * @throws KitError (INPUT_VALIDATION_FAILED) if options contain invalid services. * * @remarks * Creates a fully-configured runtime by merging provided options with defaults. * The returned runtime is frozen to enforce immutability. * * | Service | Default | Configurable | * |---------|---------|--------------| * | `logger` | pino logger (info level) | Yes | * | `metrics` | No-op metrics | Yes | * | `events` | Internal event bus | No | * | `clock` | `Date.now()` | No | * * **Why only logger and metrics?** * * - **Logger/Metrics**: Integration points with your infrastructure * - **Events**: Internal pub/sub mechanism - subscribe via `runtime.events.on()` * - **Clock**: Testing concern - use mock factories for tests * * @example * ```typescript * import { createRuntime, createLogger } from '@core/runtime' * * // Use all defaults * const runtime = createRuntime() * * // Custom logger * const runtime = createRuntime({ * logger: createLogger({ level: 'debug' }), * }) * * // Custom metrics (e.g., Prometheus) * const runtime = createRuntime({ * metrics: myPrometheusMetrics, * }) * * // Subscribe to events (don't replace the bus) * runtime.events.on('operation.*', (event) => { * console.log('Event:', event.name) * }) * ``` */ declare function createRuntime(options?: RuntimeOptions): Runtime; /** * Signing strategy contract for pluggable transaction authorization. * * @remarks * A {@link SigningStrategy} sits between an adapter's transaction * preparation and submission: the adapter builds a ready-to-sign * {@link TransactionPayload} and hands it to the strategy, which decides * how the transaction gets authorized — sign it in-process, hand it to * the caller's own infrastructure, or ask a remote service. * * The contract is intentionally minimal and synchronous-in-process: the * strategy's `execute()` resolves within the lifetime of the calling * process. Long-running flows (multi-sig quorums, challenge approvals * that survive restarts) are a future, additive extension and are out of * scope here. * * @packageDocumentation */ /** * A single pre-encoded EVM call within a {@link EvmCallsPayload}. */ interface EvmCall { /** The target contract or recipient address. */ readonly to: `0x${string}`; /** ABI-encoded calldata. Omitted for plain native-value transfers. */ readonly data?: `0x${string}` | undefined; /** Native token value to send with the call, in wei. */ readonly value?: bigint | undefined; } /** * ERC-20 approval context attached to a {@link EvmCallsPayload}. * * @remarks * When present, the payload's calls require the given ERC-20 allowance * to succeed. It is informational for the strategy: the layer that * builds the payload remains responsible for sequencing any `approve` * transaction. A strategy may use it to display or verify what the * signer is consenting to. */ interface EvmApprovalContext { /** The ERC-20 token contract address requiring approval. */ readonly token: `0x${string}`; /** The spender being approved (e.g. a router or token messenger). */ readonly spender: `0x${string}`; /** The required allowance in base units. */ readonly amount: bigint; } /** * Pre-encoded EVM call batch — the `evm-calls` payload variant. * * @remarks * Produced by an adapter at the moment a step's transaction becomes * buildable, and consumed by a {@link SigningStrategy}. The shape is the * single envelope every strategy receives for EVM execution, whether the * signer is in-process, the caller's own infrastructure, or a remote * service. */ interface EvmCallsPayload { /** Discriminator for the {@link TransactionPayload} union. */ readonly type: 'evm-calls'; /** The target EVM chain for execution. */ readonly chain: EVMChainDefinition; /** Pre-encoded calls to execute, in order. */ readonly calls: readonly EvmCall[]; /** Optional ERC-20 approval context the calls depend on. */ readonly approval?: EvmApprovalContext | undefined; /** * The address expected to authorize and send the calls. * * @remarks * A strategy that signs must sign as this address. The adapter * validates the {@link SignedSigningResult.signerAddress} echo against * this field before submitting, preventing "valid signature, wrong * account" bugs. */ readonly fromAddress: `0x${string}`; } /** * A single field of an EIP-712 struct definition. */ interface EvmTypedDataField { /** Name of the struct field. */ readonly name: string; /** Solidity type of the struct field (e.g. `'address'`, `'uint256'`). */ readonly type: string; } /** * EIP-712 domain separator carried in a {@link EvmTypedDataPayload}. * * @remarks * Every field is optional: some verifiers bind replay protection into * the signed message body instead (e.g. Gateway burn intents use a * domain with only `name` and `version`). */ interface EvmTypedDataDomain { /** Human-readable name of the signing domain (e.g. `'USD Coin'`). */ readonly name?: string | undefined; /** Major version of the signing domain (e.g. `'2'`). */ readonly version?: string | undefined; /** EVM chain ID where the verifying contract is deployed. */ readonly chainId?: number | bigint | undefined; /** Address of the contract that will verify the signature. */ readonly verifyingContract?: `0x${string}` | undefined; /** Optional salt for domain separation. */ readonly salt?: `0x${string}` | undefined; } /** * The EIP-712 structure inside a {@link EvmTypedDataPayload}. */ interface EvmTypedData { /** EIP-712 domain separator. */ readonly domain: EvmTypedDataDomain; /** Mapping of struct names to their field definitions. */ readonly types: Record; /** The root struct type being signed (a key of `types`). */ readonly primaryType: string; /** The message payload to be signed. */ readonly message: Record; } /** * EIP-712 typed-data signing request — the `evm-typed-data` payload * variant. * * @remarks * Produced when a provider needs a detached signature rather than a * transaction: EIP-2612 permits (swap), ERC-3009 authorizations (earn), * Gateway burn intents. Nothing is broadcast — the strategy resolves * with a {@link SignatureSigningResult} and the layer that requested the * signature decides what to do with it. */ interface EvmTypedDataPayload { /** Discriminator for the {@link TransactionPayload} union. */ readonly type: 'evm-typed-data'; /** The EVM chain the signature is intended for. */ readonly chain: EVMChainDefinition; /** The EIP-712 structure to sign. */ readonly typedData: EvmTypedData; /** * The address expected to produce the signature. * * @remarks * The adapter validates the {@link SignatureSigningResult.signerAddress} * echo against this field before returning the signature. */ readonly fromAddress: `0x${string}`; } /** * The transaction envelope handed to a {@link SigningStrategy}. * * @remarks * A discriminated union over payload families. Strategies `switch` on * `type`; adding a new variant (e.g. `solana-message`) is a compile * error for strategies that do not handle it exhaustively. */ type TransactionPayload = EvmCallsPayload | EvmTypedDataPayload; /** * A payload family a {@link SigningStrategy} can declare support for. * * @remarks * The discriminator values of {@link TransactionPayload}, used by * {@link SigningStrategyManifest.supportedPayloadFamilies} to * capability-gate dispatch before any payload is built. */ type SigningPayloadFamily = TransactionPayload['type']; /** * Describes the operation a payload belongs to. * * @remarks * Lets a strategy present meaningful context to whoever authorizes the * transaction (a human approving in a UI, an audit log, a policy * engine) without parsing calldata. */ interface SigningIntent { /** The kit-level action, e.g. `'bridge'` or `'swap'`. */ readonly action: string; /** The step within the action, e.g. `'approve'`, `'burn'`, `'mint'`. */ readonly step?: string | undefined; } /** * Per-invocation context passed alongside a {@link TransactionPayload}. */ interface SigningContext { /** What this transaction is for. See {@link SigningIntent}. */ readonly intent: SigningIntent; /** * Cooperative cancellation signal. * * @remarks * Strategies must check the signal before performing irreversible * work (signing, broadcasting) and should abort in-progress waits * when it fires. Once a transaction is broadcast, aborting cannot * recall it. */ readonly signal?: AbortSignal | undefined; } /** * Result of a strategy that signed and broadcast the transaction itself. * * @remarks * The adapter must not submit anything for this payload — doing so * would double-spend. Strategies returning this shape declare * `broadcasts: true` in their {@link SigningStrategyManifest}. */ interface BroadcastedSigningResult { /** Discriminator for the {@link SigningResult} union. */ readonly type: 'broadcasted'; /** Hash of the already-broadcast transaction. */ readonly txHash: `0x${string}`; /** Optional echo of the address that authorized the transaction. */ readonly signerAddress?: `0x${string}` | undefined; } /** * Result of a strategy that signed but did not broadcast. * * @remarks * The adapter submits the signed bytes (e.g. via * `eth_sendRawTransaction`) after validating `signerAddress` against * the payload's `fromAddress`. */ interface SignedSigningResult { /** Discriminator for the {@link SigningResult} union. */ readonly type: 'signed'; /** The serialized, signed transaction ready for submission. */ readonly signedTransaction: `0x${string}`; /** * The address that produced the signature. * * @remarks * Required: the adapter rejects the result when it does not match the * payload's `fromAddress`. */ readonly signerAddress: `0x${string}`; } /** * Result of a strategy that produced a detached signature. * * @remarks * The response to a {@link EvmTypedDataPayload}: nothing was broadcast * and nothing will be — the adapter returns the signature verbatim to * whoever requested it. * * The signature is opaque bytes of any length. It is never parsed or * split into v/r/s components by the adapter: contract-wallet (ERC-1271) * signers produce signatures longer than the 65 bytes ECDSA allows, and * the deployed USDC verifiers accept them via their `bytes signature` * overloads. */ interface SignatureSigningResult { /** Discriminator for the {@link SigningResult} union. */ readonly type: 'signature'; /** The detached signature over the requested payload. */ readonly signature: `0x${string}`; /** * The address that produced the signature. * * @remarks * Required: the adapter rejects the result when it does not match the * payload's `fromAddress`. */ readonly signerAddress: `0x${string}`; } /** * What a {@link SigningStrategy} returns from `execute()`. */ type SigningResult = BroadcastedSigningResult | SignedSigningResult | SignatureSigningResult; /** * Static description of a {@link SigningStrategy}. * * @remarks * Read by the adapter before any payload is built: * `supportedPayloadFamilies` capability-gates dispatch — a request for * an unsupported family fails with a clear error instead of reaching the * strategy — and `broadcasts` tells the adapter whether to submit the * result itself, letting it operate without a wallet client at all when * the strategy owns both signing and submission. */ interface SigningStrategyManifest { /** Stable identifier for diagnostics and events, e.g. `'external'`. */ readonly name: string; /** * The payload families the strategy can handle. * * @remarks * One entry per {@link TransactionPayload} variant the strategy's * `execute()` accepts. New families extend this list rather than * adding per-surface booleans. */ readonly supportedPayloadFamilies: readonly SigningPayloadFamily[]; /** * Whether the strategy submits transactions to the network itself. * * @remarks * When `true`, the strategy resolves `evm-calls` payloads with a * {@link BroadcastedSigningResult} and the adapter must not submit. * Detached-signature payloads are unaffected — nothing is ever * broadcast for them. */ readonly broadcasts: boolean; /** * Whether the strategy executes a multi-call `evm-calls` payload as a * single atomic unit. * * @remarks * Not a payload family — it qualifies `evm-calls`: when `true`, the * adapter may hand the strategy one payload whose `calls` carry an * entire batch (e.g. approve + burn) and the strategy resolves with a * single transaction outcome for the whole batch (a smart-contract * account's `executeBatch`, an EIP-5792 wallet, a bundler). When * absent or `false`, payloads always carry exactly one call. * * @defaultValue `false` */ readonly atomicBatch?: boolean | undefined; } /** * Pluggable transaction authorization. * * @remarks * Implementations decide how a built, unsigned transaction gets * authorized and (optionally) submitted. Everything else — building the * transaction, sequencing steps, waiting for confirmations — stays with * the adapter and kits. * * @example * ```typescript * const logOnly: SigningStrategy = { * manifest: { * name: 'my-signer', * supportedPayloadFamilies: ['evm-calls'], * broadcasts: true, * }, * async execute(payload, ctx) { * const txHash = await myOwnFlow(payload, ctx.signal) * return { type: 'broadcasted', txHash } * }, * } * ``` */ interface SigningStrategy { /** Static description of the strategy. See {@link SigningStrategyManifest}. */ readonly manifest: SigningStrategyManifest; /** * Authorize (and, per the manifest, optionally submit) a transaction. * * @remarks * When the signer deliberately declines (a denied PIN challenge, a * dismissed approval prompt), reject with the error from * `createSigningRejectedError` so callers can tell "the signer said * no" apart from infrastructure failure. * * @param payload - The ready-to-sign transaction envelope. * @param ctx - Cancellation and intent context for this invocation. * @returns The signing outcome. See {@link SigningResult}. */ execute(payload: TransactionPayload, ctx: SigningContext): Promise; } /** * Outcome of an external `sign` callback that broadcast the transaction * itself. */ interface ExternalBroadcastOutcome { /** Hash of the transaction the caller already broadcast. */ readonly txHash: `0x${string}`; /** Optional echo of the address that authorized the transaction. */ readonly signerAddress?: `0x${string}` | undefined; } /** * Outcome of an external `sign` callback that signed but did not * broadcast — the adapter submits the bytes. */ interface ExternalSignedOutcome { /** The serialized, signed transaction ready for submission. */ readonly signedTransaction: `0x${string}`; /** The address that produced the signature. */ readonly signerAddress: `0x${string}`; } /** * What an external `sign` callback may resolve with. */ type ExternalSigningOutcome = ExternalBroadcastOutcome | ExternalSignedOutcome; /** * Outcome of an external `signTypedData` callback — a detached EIP-712 * signature, nothing broadcast. */ interface ExternalTypedDataOutcome { /** * The detached signature over the typed data. * * @remarks * Opaque bytes of any length — contract-wallet (ERC-1271) signatures * exceed the 65 bytes ECDSA allows and are passed through untouched. */ readonly signature: `0x${string}`; /** The address that produced the signature. */ readonly signerAddress: `0x${string}`; } /** * Options for {@link externalSigning}. */ interface ExternalSigningOptions { /** * The integrator's signing flow for transactions. * * @remarks * Called once per transaction, at the moment the payload becomes * buildable, with the ready-to-sign envelope and the invocation * context (intent, cancellation signal). Resolve with a * {@link ExternalSigningOutcome} matching the declared * {@link ExternalSigningOptions.broadcasts | broadcasts} mode. */ readonly sign: (payload: EvmCallsPayload, ctx: SigningContext) => Promise; /** * The integrator's signing flow for EIP-712 typed data. * * @remarks * Optional — providing it adds `evm-typed-data` to the strategy's * supported payload families, which kits need for detached-signature * authorization (EIP-2612 permits, ERC-3009 authorizations, Gateway * burn intents). When omitted, typed-data requests are rejected at * dispatch with a capability error before this strategy is invoked. */ readonly signTypedData?: ((payload: EvmTypedDataPayload, ctx: SigningContext) => Promise) | undefined; /** * Whether the `sign` callback broadcasts the transaction itself. * * @remarks * Declared up front because the adapter reads it from the manifest * before any payload exists (e.g. to allow operating without a wallet * client). The strategy enforces the declaration at runtime: a * callback that resolves with the other outcome shape is rejected * rather than risking a double-broadcast or a never-submitted * transaction. * * @defaultValue `true` */ readonly broadcasts?: boolean | undefined; /** * Whether the caller's signer executes multi-call payloads atomically. * * @remarks * Declare `true` when the signing flow behind `sign` can execute an * entire batch as one unit — a smart-contract account's * `executeBatch`, an EIP-5792 wallet, a bundler. The `sign` callback * may then receive payloads whose `calls` carry the whole batch (e.g. * approve + burn) and must resolve with the single outcome for all of * them. When omitted, every payload carries exactly one call. * * @defaultValue `false` */ readonly atomicBatch?: boolean | undefined; } /** * A {@link SigningStrategy} whose signer is the caller. * * @remarks * The simplest strategy in the family: it hands each ready-to-sign * payload to the integrator's callback and maps whatever comes back — * a `txHash` if the caller broadcast, signed bytes if the caller only * signed (the adapter then submits). It performs no cryptography and * holds no keys. * * @param options - The callback and its declared broadcast mode. * @returns A signing strategy delegating authorization to the caller. * @throws KitError when the callback's outcome contradicts the declared * `broadcasts` mode, or resolves with a malformed (non-hex) `txHash`, * signed transaction, signature, or signer address. * * @example * ```typescript * import { externalSigning } from '@core/adapter-base' * * const signing = externalSigning({ * sign: async (payload) => { * // payload = { chain, calls, approval, fromAddress } * const txHash = await myOwnFlow(payload) * return { txHash } * }, * signTypedData: async (payload) => { * // payload = { chain, typedData, fromAddress } * const signature = await myOwnTypedDataFlow(payload) * return { signature, signerAddress: payload.fromAddress } * }, * }) * * // signing.manifest.supportedPayloadFamilies === ['evm-calls', 'evm-typed-data'] * // Hand `signing` to an EVM adapter's `signing` option to authorize * // transactions through your own flow. * ``` */ declare function externalSigning(options: ExternalSigningOptions): SigningStrategy; /** * Create the canonical "the signer declined" error for a signing * strategy. * * @remarks * For human-approval signers (PIN challenges, hardware confirmation, * approval UIs) a decline is an expected outcome, not an infrastructure * failure. Strategies must throw this — not a generic error — when the * person or policy behind the signer refuses to authorize a payload, so * kits, retry policies, and UIs can distinguish a deliberate decline * from a genuine failure. Detect it with {@link isSigningRejected}. * * Uses the existing `INPUT_USER_CANCELLED` code, which kit telemetry * already classifies as a deliberate user action, and stamps a * `signingRejected` marker into the trace so {@link isSigningRejected} * can tell a signing decline apart from an unrelated cancellation. * * @param strategyName - The rejecting strategy's manifest name. * @param intent - What the signer declined, for the message and trace. * @param trace - Optional extra context (e.g. a challenge identifier). * @returns A KitError marked as a deliberate signer rejection. * * @example * ```typescript * if (challenge.status === 'DENIED') { * throw createSigningRejectedError('circle-ucw', ctx.intent, { * challengeId: challenge.id, * }) * } * ``` */ declare function createSigningRejectedError(strategyName: string, intent: SigningIntent, trace?: Record): KitError; /** * Return whether an error is a deliberate signer rejection. * * @remarks * True only for errors produced by {@link createSigningRejectedError}, * identified by the `signingRejected` marker it stamps into the trace — * not for every `INPUT_USER_CANCELLED` KitError, so a cancellation from * an unrelated path is not misread as a signer decline. The marker is * plain trace data (not an `instanceof` check), so detection still works * across a separately bundled copy of `@core/errors`. Use this at the * call site (or on a failed step's error) to branch decline UX away from * failure UX. * * @param error - The caught value to classify. * @returns `true` when the error represents the signer saying no. * * @example * ```typescript * try { * await kitFlow() * } catch (error) { * if (isSigningRejected(error)) showDeclinedToast() * else showErrorScreen(error) * } * ``` */ declare function isSigningRejected(error: unknown): boolean; /** * Base shape for an ecosystem-owned authorization payload. * * @remarks * Ecosystems define their own discriminated payload unions and bind them to * the generic authorization contract. EVM uses `TransactionPayload`; Solana * can add an independent payload later without widening the EVM union. */ interface AuthorizationPayload { /** Ecosystem-owned payload discriminator. */ readonly type: string; } /** * Optional semantic data attached to an authorization request. * * @remarks * Review kinds are stable, namespaced identifiers such as `earn.execute`. * The data remains `unknown` because adapter core cannot depend on kit types. */ interface AuthorizationReview { /** Stable, namespaced review discriminator. */ readonly kind: string; /** Kit-owned semantic review data. */ readonly data: unknown; } /** Create optional semantic review data from an isolated payload snapshot. */ type AuthorizationReviewFactory = (payload: TPayload) => AuthorizationReview | undefined | Promise; /** * Request-scoped authorization metadata supplied by a kit or provider. * * @typeParam TPayload - The ecosystem authorization payload type. */ interface AuthorizationDescriptor { /** Optional intent override for this authorization unit. */ readonly intent?: SigningIntent | undefined; /** Optional lazy semantic review factory. */ readonly createReview?: AuthorizationReviewFactory | undefined; } /** * Immutable request passed to an adapter authorization-review hook. * * @typeParam TPayload - The ecosystem authorization payload type. */ interface AuthorizationRequest { /** What the authorization unit is intended to accomplish. */ readonly intent: SigningIntent; /** Isolated snapshot of the canonical payload being authorized. */ readonly payload: TPayload; /** Optional kit-supplied semantic review data. */ readonly review?: AuthorizationReview | undefined; /** Optional cooperative cancellation signal. */ readonly signal?: AbortSignal | undefined; } /** Explicit decision returned by an authorization-review hook. */ type AuthorizationDecision = 'approve' | 'reject'; /** * Hook invoked immediately before an adapter asks a wallet or signer to * authorize a payload. * * @typeParam TPayload - The ecosystem authorization payload type. */ type OnBeforeAuthorize = (request: AuthorizationRequest) => Promise; /** * Adapter configuration for authorization review. * * @typeParam TPayload - The ecosystem authorization payload type. */ interface AuthorizationOptions { /** Optional awaited, fail-closed authorization-review hook. */ readonly onBeforeAuthorize?: OnBeforeAuthorize | undefined; } /** * Adapter-owned invocation metadata that remains request scoped. * * @remarks * This deliberately does not use `ActionOptions.meta`, which is emitted to * observability middleware and must never carry callback closures. * * @typeParam TPayload - The ecosystem authorization payload type. */ interface AdapterInvocationMeta extends InvocationMeta { /** Optional authorization descriptor for this invocation. */ readonly authorization?: AuthorizationDescriptor | undefined; } /** * Resolved adapter invocation context with request-scoped authorization data. * * @typeParam TPayload - The ecosystem authorization payload type. */ interface AdapterInvocationContext extends InvocationContext { /** Optional authorization descriptor for this invocation. */ readonly authorization?: AuthorizationDescriptor | undefined; } /** * Input accepted by an adapter context's shared authorization gate. * * @typeParam TPayload - The ecosystem authorization payload type. */ interface AuthorizeInput { /** Canonical payload that will be authorized downstream. */ readonly payload: TPayload; /** Fallback intent when the descriptor does not override it. */ readonly intent: SigningIntent; /** Optional request-scoped descriptor. */ readonly descriptor?: AuthorizationDescriptor | undefined; /** Optional cooperative cancellation signal. */ readonly signal?: AbortSignal | undefined; } /** Shared authorization gate exposed by an adapter context. */ type Authorize = (input: AuthorizeInput) => Promise; /** * Create the canonical error for an adapter authorization-review rejection. * * @param intent - What the application declined to authorize. * @returns A user-cancelled `KitError` detectable by `isSigningRejected`. * * @example * ```typescript * if (decision === 'reject') { * throw createAuthorizationRejectedError({ action: 'earn.deposit' }) * } * ``` */ declare function createAuthorizationRejectedError(intent: SigningIntent): KitError; /** * Context types for adapter operations. * * @remarks * This module provides the core context types for adapter operations. * * **Mental Model** * * - **WHAT**: `OperationContext` defines the operation target (chain, address) * - **WHO/HOW**: `InvocationContext` defines tracing and runtime (traceId, runtime, tokens, callers) * * @packageDocumentation */ /** * Defines the capabilities of an adapter. * * @remarks * The `addressContext` property determines address handling: * - `user-controlled`: Address resolved from wallet (e.g., MetaMask, private key) * - `developer-controlled`: Address must be explicitly provided (e.g., Fireblocks) */ interface AdapterCapabilities$1 { /** * Defines who controls address selection. * * @remarks * - `user-controlled`: User controls via wallet UI; address resolved automatically * - `developer-controlled`: Address must be explicitly provided per operation */ addressContext: 'user-controlled' | 'developer-controlled'; /** * The blockchain networks this adapter supports. */ supportedChains: readonly ChainDefinition[]; } /** * Adapter identification for telemetry and debugging. */ interface AdapterIdentity { /** Name of the adapter (e.g., 'viem', 'ethers'). */ readonly name: string; /** Version of the adapter. */ readonly version: string; } /** * Policy for handling wallet chain mismatches before write operations. * * @remarks * - `'prompt'` (default for user-controlled adapters): ask the wallet to * switch chains (and add the chain when supported). * - `'throw'`: fail fast with a typed `CHAIN_MISMATCH` error without * prompting. Useful for server-driven integrations that want to * surface a clean failure rather than interrupt the user flow. * - `'skip'`: disable the hook entirely. Factories for developer-controlled * or private-key signers default to this mode because the chain is * pinned by the factory itself. */ type ChainSwitchOnMismatch = 'prompt' | 'throw' | 'skip'; /** * Adapter-level configuration for the `ensureChain` hook. * * @remarks * Adapter factories forward this object to their context factory. Adapter * packages own the actual chain-switching implementation — this config is * the knob consumers use to opt in to alternative policies. * * @example * ```typescript * createViemAdapterFromProvider({ * provider: window.ethereum, * chainSwitch: { onMismatch: 'throw' }, * }) * ``` */ interface ChainSwitchConfig { /** * Policy applied when the wallet reports a different chain than the * operation targets. * * @defaultValue `'prompt'` for user-controlled factories, `'skip'` * for developer-controlled / private-key factories. */ readonly onMismatch?: ChainSwitchOnMismatch | undefined; } /** * Adapter context containing runtime, capabilities, and utilities. * * @typeParam TCapabilities - The adapter capabilities type. * * @remarks * This is the adapter's setup/configuration object. It's bound to the adapter * instance and provides access to runtime services, capabilities, identity, * and shared resources like token registry. * * @example * ```typescript * import type { AdapterContext } from '@core/adapter-base' * * const ctx: AdapterContext = { * identity: { name: 'viem', version: '1.0.0' }, * capabilities: { addressContext: 'user-controlled', supportedChains: [...] }, * runtime: createRuntime(), * tokens: createTokenRegistry(), * normalizeError: viemErrorNormalizer, * getAddress: async (chain) => wallet.getAddress(), * } * ``` */ interface AdapterContext$1 { /** Adapter identity for telemetry. */ readonly identity: AdapterIdentity; /** Adapter capabilities. */ readonly capabilities: TCapabilities; /** Complete runtime with all services. */ readonly runtime: Runtime; /** Token registry for resolving token information. */ readonly tokens: TokenRegistry; /** Error normalizer for this adapter. */ readonly normalizeError: ErrorNormalizer; /** * Get the current address for a chain. * * @remarks * Returns the address that will be used as the sender/signer for operations. * Implementation depends on the adapter type (wallet connection, key management, etc.). */ getAddress: (chain: ChainDefinition) => Promise; /** * Validate that a chain is supported by this adapter. * * @remarks * Throws a `KitError` with `INVALID_CHAIN` code if the chain is not * in `capabilities.supportedChains`. Automatically provided by * `createAdapterContext`. */ readonly validateChainSupport: (chain: ChainDefinition) => void; /** * Optional operational configuration for this adapter. * * @remarks * Contains behavioral settings like retry configuration that apply to * all operations executed through this adapter. */ readonly config?: OperationalConfig | undefined; /** * Ensure the wallet is on the target chain before a write operation. * * @remarks * Populated by adapters that can actually switch chains (EVM wallet * adapters). Read-only primitives must never invoke it — only * primitives that opt in via `ensureChainBefore: true` on their * `createPrimitive` options will trigger this hook. * * Implementations are expected to be idempotent: calling `ensureChain` * when the wallet is already on `chain` must not surface a prompt. */ readonly ensureChain?: ((chain: ChainDefinition) => Promise) | undefined; /** Review a canonical payload immediately before wallet authorization. */ readonly authorize: Authorize; } /** * Extract the address context from adapter capabilities. * * @typeParam TCapabilities - The adapter capabilities type. * @returns The address context type ('user-controlled' | 'developer-controlled'). */ type ExtractAddressContext$1 = TCapabilities extends { addressContext: infer TContext; } ? TContext : never; /** * Conditional address field based on adapter capabilities. * * @typeParam TAddressContext - The address context type. * @returns Address field type: * - `user-controlled` → `address?: never` (forbidden) * - `developer-controlled` → `address: string` (required) * - Unknown → `address?: string` (optional, legacy) */ type AddressField$1 = TAddressContext extends 'user-controlled' ? { address?: never; } : TAddressContext extends 'developer-controlled' ? { address: string; } : { address?: string; }; /** * Capability-aware operation meta that enforces address requirements. * * @typeParam TCapabilities - The adapter capabilities type. * * @remarks * Use this when you need compile-time enforcement of address requirements. * For simpler cases, use {@link OperationMeta}. */ type CapabilityAwareOperationMeta = { readonly chain: ChainIdentifier$1; } & AddressField$1>; /** * User input specifying the operation target. * * @remarks * Defines **WHAT** we're operating on: which chain and optionally which address. * This is the user-facing input type, resolved to `OperationContext` internally. * * @example * ```typescript * // Minimal input * const meta: OperationMeta = { chain: 'Ethereum' } * * // With explicit address * const meta: OperationMeta = { * chain: 'Ethereum', * address: '0x1234...', * } * ``` */ interface OperationMeta { /** * Target chain for the operation. * * @remarks * Accepts chain name (string) or full ChainDefinition. * Resolved to ChainDefinition internally. */ readonly chain: ChainIdentifier$1; /** * Sender/signer address (optional). * * @remarks * - User-controlled adapters: Resolved from wallet if not provided * - Developer-controlled adapters: Required */ readonly address?: string | undefined; } /** * Type definitions for the Amount system. * * @remarks * This module defines the core types for type-safe token amount handling. * Amounts are immutable value objects that carry the raw bigint value * and decimal precision. Token identity (symbol, address) is managed * separately by the token registry. * * @packageDocumentation */ /** * An immutable token amount value. * * @remarks * Amounts are pure numeric value objects containing the raw bigint value * (in smallest units) and decimal precision. They are immutable - all * math operations return new Amount instances. * * Token identity (symbol, contract address) is intentionally NOT included. * This separation ensures developers explicitly source token metadata from * the token registry, preventing incorrect symbol display. * * @example * ```typescript * const amount: Amount = { * raw: 1_000_000n, // 1 token in smallest units (e.g., 1 USDC) * decimals: 6, * } * ``` */ interface Amount$1 { /** * The raw value in smallest units (e.g., wei for ETH, micro-units for USDC). */ readonly raw: bigint; /** * Number of decimal places for this token. * * @remarks * Common values: * - USDC/USDT: 6 * - ETH/ERC-20: 18 */ readonly decimals: number; } /** * Configuration for creating amounts. * * @remarks * Specifies the decimal precision for the amount. Typically obtained * from the token registry. */ interface AmountConfig { /** * Number of decimal places. */ decimals: number; } /** * Options for formatting amounts as human-readable strings. */ interface FormatOptions { /** * Locale for number formatting. * * @defaultValue 'en-US' */ locale?: string; /** * Minimum fraction digits to display. * * @defaultValue 0 */ minimumFractionDigits?: number; /** * Maximum fraction digits to display. * * @defaultValue decimals */ maximumFractionDigits?: number; /** * Use grouping separators (e.g., 1,000,000). * * @defaultValue true */ useGrouping?: boolean; } /** * Options for parsing human-readable strings to amounts. * * @remarks * Currently only supports '.' as the decimal separator (en-US style). */ interface ParseOptions { /** * Number of decimal places. */ decimals: number; /** * Whether to allow negative values. * * @defaultValue false */ allowNegative?: boolean; } /** * Result of comparing two amounts. */ type ComparisonResult = -1 | 0 | 1; /** * Types that can be converted to an Amount. * * @remarks * - `bigint`: Raw value (requires config with decimals) * - `string`: Human-readable (e.g., "100.50") * - `number`: Human-readable (e.g., 100.5) - beware of precision! * - `Amount`: Existing amount or plain object with Amount shape */ type AmountLike = bigint | string | number | Amount$1; /** * JSON representation of an Amount. * * @remarks * Used for serialization. The `raw` value is stringified since JSON * does not support bigint natively. * * @example * ```typescript * const json: AmountJSON = { * raw: "1500000", * decimals: 6, * formatted: "1.5" * } * ``` */ interface AmountJSON { /** The raw value as a string (bigint serialized). */ raw: string; /** Number of decimal places. */ decimals: number; /** Human-readable formatted value. */ formatted: string; } /** * Minimal fields required for internal Amount operations. * * @remarks * This internal type mirrors the public {@link Amount} interface shape but exists * separately to avoid circular dependencies. Internal modules (math, comparison, * formatting) import this lightweight type instead of the full Amount class. * * The Amount class implements this interface, so internal helpers can operate * on either Amount instances or plain objects with the same shape. * * @see {@link ../types.ts#Amount} for the public interface * @internal */ interface AmountFields { readonly raw: bigint; readonly decimals: number; } /** * Fluent Amount class for type-safe token amount handling. * * @remarks * The `Amount` class provides an immutable, fluent API for working with token * amounts. It wraps a raw bigint value with decimal precision and provides * chainable methods for math, comparison, and formatting. * * Token identity (symbol, contract address) is intentionally NOT included. * This ensures developers explicitly source token metadata from the token * registry, preventing incorrect symbol display. * * @example * ```typescript * import { Amount } from '@core/amounts' * * // Create from raw value or parse from string * const a = Amount.of(1_000_000n, { decimals: 6 }) * const b = Amount.parse('0.5', { decimals: 6 }) * * // Fluent operations * const result = a.add(b).mul(2n).sub(Amount.parse('0.25', { decimals: 6 })) * * // Comparisons and formatting * a.gt(b) // true * a.toString() // "1" * * // For display with symbol, combine with token registry: * // `${amount.toString()} ${token.symbol}` * ``` * * @packageDocumentation */ /** * An immutable token amount with fluent API methods. * * @remarks * The `Amount` class provides a type-safe, ergonomic way to work with token * amounts. It combines the raw bigint value with decimal precision and * provides chainable methods for: * * - **Math operations**: add, sub, mul, div, abs, neg * - **Comparisons**: eq, lt, lte, gt, gte, cmp, min, max * - **Predicates**: isZero, isPositive, isNegative * - **Formatting**: formatted, toString, toJSON * - **Conversion**: toDecimals * * All operations return new Amount instances - the class is immutable. * * Token identity (symbol) is intentionally excluded. Source token metadata * from the token registry for display purposes. */ declare class Amount implements AmountFields { /** The raw value in smallest units (e.g., wei for ETH, micro-units for USDC). */ readonly raw: bigint; /** Number of decimal places for this token. */ readonly decimals: number; private constructor(); /** * Create an Amount from a raw bigint value. * * @param raw - The raw value in smallest units. * @param config - Configuration with decimals. * @returns A new immutable Amount instance. * @throws KitError If raw is not a bigint or decimals is invalid. * * @example * ```typescript * // Get decimals from token registry * const amount = Amount.of(1_000_000n, { decimals: 6 }) * ``` */ static of(raw: bigint, config: AmountConfig): Amount; /** * Parse a human-readable string or number into an Amount. * * @param input - The input to parse (e.g., "100.50" or 100.5). * @param options - Parse options including decimals. * @returns A new immutable Amount instance. * @throws KitError If input is invalid or parsing fails. * * @example * ```typescript * const amount = Amount.parse('100.50', { decimals: 6 }) * ``` */ static parse(input: string | number, options: ParseOptions): Amount; /** * Convert any AmountLike value to an Amount. * * @param input - The input (bigint, string, number, or existing Amount). * @param config - Configuration (required for non-Amount inputs). * @returns A new immutable Amount instance. * @throws KitError If conversion fails or config is missing. * * @remarks * When providing a `number` input, very large or small values may be * converted to exponential notation (e.g., `1e21`), which is not supported. * For such values, provide a `string` representation instead. * * @example * ```typescript * Amount.from(1_000_000n, { decimals: 6 }) // from bigint * Amount.from('1.00', { decimals: 6 }) // from string * Amount.from(existingAmount) // pass-through * ``` */ static from(input: AmountLike, config?: AmountConfig): Amount; /** * Create a zero Amount with the given configuration. * * @param config - Configuration with decimals. * @returns A zero Amount instance. * * @example * ```typescript * const zero = Amount.zero({ decimals: 6 }) * ``` */ static zero(config: AmountConfig): Amount; /** * Check if a value is an Amount-like object. * * @param value - The value to check. * @returns True if the value has Amount shape (raw: bigint, decimals: number). * * @example * ```typescript * Amount.isAmount({ raw: 1000000n, decimals: 6 }) // true * Amount.isAmount({ value: 100 }) // false * ``` */ static isAmount(value: unknown): value is { raw: bigint; decimals: number; }; /** * Sum an array of Amounts. * * @param amounts - The amounts to sum (must have same decimals). * @returns A new Amount with the total sum. * @throws KitError If amounts array is empty or decimals don't match. * * @example * ```typescript * const fee1 = Amount.parse('0.10', { decimals: 6 }) * const fee2 = Amount.parse('0.25', { decimals: 6 }) * const fee3 = Amount.parse('0.15', { decimals: 6 }) * * const total = Amount.sum([fee1, fee2, fee3]) * total.toString() // "0.5" * ``` */ static sum(amounts: readonly Amount[]): Amount; /** * Deserialize an Amount from a JSON object. * * @param json - The JSON object (e.g., from `JSON.parse()`). * @returns A new Amount instance. * @throws KitError If the JSON structure is invalid. * * @example * ```typescript * const json = JSON.parse('{"raw":"1500000","decimals":6}') * const amount = Amount.fromJSON(json) * ``` */ static fromJSON(json: unknown): Amount; /** * Compare this Amount with another. * * @param other - The Amount to compare with. * @returns -1 if this is less than other, 0 if equal, 1 if this is greater. * @throws KitError If amounts have different decimals. * * @example * ```typescript * const a = Amount.parse('1.5', { decimals: 6 }) * const b = Amount.parse('2.0', { decimals: 6 }) * a.compare(b) // -1 * ``` */ compare(other: Amount): ComparisonResult; /** * Check if this Amount equals another. * * @param other - The Amount to compare with. * @returns True if the raw values are equal. * @throws KitError If amounts have different decimals. * * @example * ```typescript * const a = Amount.parse('1.5', { decimals: 6 }) * const b = Amount.parse('1.5', { decimals: 6 }) * a.eq(b) // true * ``` */ eq(other: Amount): boolean; /** * Check if this Amount is less than another. * * @param other - The Amount to compare with. * @returns True if this.raw is less than other.raw. * @throws KitError If amounts have different decimals. * * @example * ```typescript * const a = Amount.parse('1.0', { decimals: 6 }) * const b = Amount.parse('2.0', { decimals: 6 }) * a.lt(b) // true * ``` */ lt(other: Amount): boolean; /** * Check if this Amount is less than or equal to another. * * @param other - The Amount to compare with. * @returns True if this.raw is less than or equal to other.raw. * @throws KitError If amounts have different decimals. * * @example * ```typescript * const a = Amount.parse('1.5', { decimals: 6 }) * const b = Amount.parse('1.5', { decimals: 6 }) * a.lte(b) // true * ``` */ lte(other: Amount): boolean; /** * Check if this Amount is greater than another. * * @param other - The Amount to compare with. * @returns True if this.raw is greater than other.raw. * @throws KitError If amounts have different decimals. * * @example * ```typescript * const a = Amount.parse('2.0', { decimals: 6 }) * const b = Amount.parse('1.0', { decimals: 6 }) * a.gt(b) // true * ``` */ gt(other: Amount): boolean; /** * Check if this Amount is greater than or equal to another. * * @param other - The Amount to compare with. * @returns True if this.raw is greater than or equal to other.raw. * @throws KitError If amounts have different decimals. * * @example * ```typescript * const a = Amount.parse('2.0', { decimals: 6 }) * const b = Amount.parse('2.0', { decimals: 6 }) * a.gte(b) // true * ``` */ gte(other: Amount): boolean; /** * Return the minimum of this Amount and another. * * @param other - The Amount to compare with. * @returns The smaller of the two amounts. * @throws KitError If amounts have different decimals. * * @example * ```typescript * const a = Amount.parse('1.0', { decimals: 6 }) * const b = Amount.parse('2.0', { decimals: 6 }) * a.min(b).toString() // "1" * ``` */ min(other: Amount): Amount; /** * Return the maximum of this Amount and another. * * @param other - The Amount to compare with. * @returns The larger of the two amounts. * @throws KitError If amounts have different decimals. * * @example * ```typescript * const a = Amount.parse('1.0', { decimals: 6 }) * const b = Amount.parse('2.0', { decimals: 6 }) * a.max(b).toString() // "2" * ``` */ max(other: Amount): Amount; /** * Add another Amount to this one. * * @param other - The Amount to add. * @returns A new Amount with the sum. * @throws KitError If amounts have different decimals. * * @example * ```typescript * const a = Amount.parse('1.5', { decimals: 6 }) * const b = Amount.parse('0.5', { decimals: 6 }) * a.add(b).toString() // "2" * ``` */ add(other: Amount): Amount; /** * Subtract another Amount from this one. * * @param other - The Amount to subtract. * @returns A new Amount with the difference. * @throws KitError If amounts have different decimals. * * @example * ```typescript * const a = Amount.parse('2.0', { decimals: 6 }) * const b = Amount.parse('0.5', { decimals: 6 }) * a.sub(b).toString() // "1.5" * ``` */ sub(other: Amount): Amount; /** * Multiply this Amount by a scalar. * * @param multiplier - The scalar to multiply by (bigint or integer number). * @returns A new Amount with the product. * * @remarks * When using a `number` as a multiplier, precision is limited by standard * JavaScript floating-point capabilities (~15-17 significant digits). For * high-precision calculations, provide the multiplier as a `bigint`. * * @example * ```typescript * const a = Amount.parse('1.5', { decimals: 6 }) * a.mul(2n).toString() // "3" * a.mul(3).toString() // "4.5" * ``` */ mul(multiplier: bigint | number): Amount; /** * Divide this Amount by a scalar. * * @param divisor - The scalar to divide by (bigint or integer number). * @returns A new Amount with the quotient (integer division). * @throws KitError If divisor is zero. * * @remarks * When using a `number` as a divisor, precision is limited by standard * JavaScript floating-point capabilities (~15-17 significant digits). For * high-precision calculations, provide the divisor as a `bigint`. * * @example * ```typescript * const a = Amount.parse('6.0', { decimals: 6 }) * a.div(2n).toString() // "3" * a.div(4).toString() // "1.5" * ``` */ div(divisor: bigint | number): Amount; /** * Get the absolute value of this Amount. * * @returns A new Amount with the absolute value. * * @example * ```typescript * const a = Amount.of(-1_500_000n, { decimals: 6 }) * a.abs().toString() // "1.5" * ``` */ abs(): Amount; /** * Negate this Amount. * * @returns A new Amount with the negated value. * * @example * ```typescript * const a = Amount.parse('1.5', { decimals: 6 }) * a.neg().raw // -1_500_000n * ``` */ neg(): Amount; /** * Check if this Amount is zero. * * @returns True if the raw value is 0n. * * @example * ```typescript * Amount.zero({ decimals: 6 }).isZero() // true * Amount.parse('0.001', { decimals: 6 }).isZero() // false * ``` */ isZero(): boolean; /** * Check if this Amount is positive. * * @returns True if the raw value is greater than 0n. * * @example * ```typescript * Amount.parse('1.0', { decimals: 6 }).isPositive() // true * Amount.zero({ decimals: 6 }).isPositive() // false * ``` */ isPositive(): boolean; /** * Check if this Amount is negative. * * @returns True if the raw value is less than 0n. * * @example * ```typescript * Amount.of(-1_000_000n, { decimals: 6 }).isNegative() // true * Amount.parse('1.0', { decimals: 6 }).isNegative() // false * ``` */ isNegative(): boolean; /** * Convert this Amount to a different decimal precision. * * @param newDecimals - The target number of decimal places. * @returns A new Amount with adjusted precision (or this if same precision). * @throws KitError If newDecimals is invalid. * * @example * ```typescript * // Scale up: 6 decimals → 18 decimals * const usdc = Amount.parse('1.5', { decimals: 6 }) * const scaled = usdc.toDecimals(18) * scaled.raw // 1_500_000_000_000_000_000n * * // Scale down: 18 decimals → 6 decimals (truncates) * const eth = Amount.of(1_500_000_000_000_000_000n, { decimals: 18 }) * eth.toDecimals(6).raw // 1_500_000n * ``` */ toDecimals(newDecimals: number): Amount; /** * Format this Amount as a human-readable string. * * @param options - Formatting options (locale, fraction digits, grouping). * @returns A formatted string (e.g., "1,000.50"). * * @example * ```typescript * const a = Amount.parse('1000.5', { decimals: 6 }) * a.formatted() // "1,000.5" * a.formatted({ minimumFractionDigits: 2 }) // "1,000.50" * a.formatted({ useGrouping: false }) // "1000.5" * ``` */ formatted(options?: FormatOptions): string; /** * Convert to a human-readable string. * * @returns A formatted string like "1.5". * * @remarks * For display with a token symbol, combine with the token registry: * ```typescript * `${amount.toString()} ${token.symbol}` * ``` * * @example * ```typescript * const a = Amount.parse('1.5', { decimals: 6 }) * a.toString() // "1.5" * ``` */ toString(): string; /** * Convert to a JSON-serializable object. * * @returns An object with raw (as string), decimals, and formatted value. * * @example * ```typescript * const a = Amount.parse('1.5', { decimals: 6 }) * a.toJSON() * // { raw: "1500000", decimals: 6, formatted: "1.5" } * ``` */ toJSON(): AmountJSON; } /** * Types for the createPrepare factory. * * @packageDocumentation */ /** * Result of sequentially executing multiple transactions. * * @remarks * When executing multiple transactions (e.g., USDT approve reset → set), * this result shows what completed and where any failure occurred. * * @typeParam TRaw - Ecosystem-specific raw data type. */ interface SequentialExecuteResult { /** * Whether ALL transactions succeeded. */ readonly success: boolean; /** * Confirmations for all transactions. * * @remarks * If success is false, the last confirmation is the failed one. */ readonly results: Confirmation[]; /** * Index of the failed transaction (0-based), if any. */ readonly failedIndex?: number | undefined; } /** * Aggregated fee estimate for multiple transactions. */ interface AggregateFeeEstimate { /** * Total fee across all transactions. * * @remarks * Access `.raw` for the bigint value. * * @example * ```typescript * const { totalFee, count } = await result.estimateTotal() * console.log(`Total: ${totalFee.toString()} for ${count} transactions`) * ``` */ readonly totalFee: Amount; /** * Number of transactions. */ readonly count: number; /** * Individual fee estimates. */ readonly estimates: FeeEstimate[]; } /** * Options for executing prepared transactions. */ interface ExecuteOptions { /** * Called after each transaction is sent (before confirmation). * * @param txId - The transaction ID/hash. * @param index - The zero-based index of the transaction in the batch. */ onTxSent?: ((txId: string, index: number) => void) | undefined; } /** * Result of preparing one or more transactions. * * @remarks * This wrapper provides a unified interface regardless of whether * the prepare function returned a single transaction or multiple. * * **Key Features:** * - `transactions` - Always an array of the specified prepared transaction type * - `single()` - Convenience for single-transaction case (returns TPrepared) * - `estimateTotal()` - Aggregate fee estimation * - `execute()` - Sequential execution with proper error handling * * **Custom Prepared Transaction Types:** * The `TPrepared` type parameter allows ecosystems to use their own prepared * transaction interfaces while maintaining type safety throughout. * * @typeParam TInput - The user-provided input type. * @typeParam TBuilt - The built transaction type. * @typeParam TRaw - The ecosystem-specific raw data type. * @typeParam TPrepared - The prepared transaction type (defaults to base PreparedTransaction). * * @example * ```typescript * // With custom prepared transaction type * const result: PrepareResult = await prepare(...) * * // single() returns PreparedEVMTransaction with proper types * const tx = result.single() * const fee = await tx.estimate() // Returns EstimateResult * ``` */ interface PrepareResult, TAuthorizationPayload extends AuthorizationPayload = AuthorizationPayload> { /** * All prepared transactions. * * @remarks * Returns array of TPrepared - the custom prepared transaction type. */ readonly transactions: TPrepared[]; /** * Operation name (for debugging/logging). */ readonly name: string; /** * Invocation metadata (passed through for downstream calls). */ readonly meta: AdapterInvocationMeta; /** * Get the single prepared transaction. * * @remarks * Throws if there are zero or multiple transactions. * Returns TPrepared with all custom method signatures intact. */ single(): TPrepared; /** * Estimate total fees across all transactions. */ estimateTotal(): Promise; /** * Execute all transactions sequentially. * * @param options - Optional execution callbacks. */ execute(options?: ExecuteOptions): Promise>; } /** * Adapter types: interface, universal result types, and prepared transactions. * * @packageDocumentation */ /** * Generic adapter interface for action bindings. * * @typeParam TContext - The adapter context type. * @typeParam TReadInput - Input type for read operations. * @typeParam TWriteInput - Input type for write/prepare operations. * @typeParam TChain - Chain definition type (defaults to ChainDefinition). */ interface Adapter$1 { /** The adapter context containing configuration, clients, and capabilities. */ readonly ctx: TContext; /** Execute a read operation. */ read(input: TReadInput, operation: OperationMeta, invocation?: AdapterInvocationMeta): Promise; /** Prepare one or more write operations. */ prepare(input: TWriteInput | TWriteInput[], operation: OperationMeta, invocation?: AdapterInvocationMeta): Promise>; /** Get the connected wallet address for a chain. */ getAddress(chain: TChain): Promise; } interface AdapterGenerics { readonly read: never; readonly write: never; readonly chain: never; readonly authorization: AuthorizationPayload; } type AdapterGenericsOf = T extends Adapter$1 ? { readonly read: TRead; readonly write: TWrite; readonly chain: TChain; readonly authorization: TAuthorizationPayload; } : AdapterGenerics; /** Extract the read input type from an adapter. */ type AdapterReadInputOf = AdapterGenericsOf['read']; /** Extract the chain type from an adapter. */ type AdapterChainOf = AdapterGenericsOf['chain']; /** Extract the authorization payload type from an adapter. */ type AdapterAuthorizationPayloadOf = AdapterGenericsOf['authorization']; /** Universal fee estimate with ecosystem-specific raw data. */ interface FeeEstimate { /** Total fee as an Amount. */ readonly fee: Amount; /** * Compute units used in the calculation. * EVM: gas units | Solana: compute units | Aptos: gas units */ readonly units: bigint; /** * Price per compute unit at time of calculation. * EVM: wei per gas | Solana: micro-lamports per CU | Aptos: octas per gas * * @remarks * **`units * unitPrice` is NOT a portable fee formula.** The * unit/price denominations differ per ecosystem and the fee * formula differs accordingly: * * | Ecosystem | `unitPrice` denomination | Fee formula | * |-----------|--------------------------|--------------------------------------------------------------------| * | EVM | wei per gas | `units * unitPrice` (modulo bufferBps) | * | Solana | micro-lamports per CU | `units * unitPrice / 1_000_000 + 5000` (priority fee + base fee) | * | Aptos | octas per gas | `units * unitPrice` (modulo bufferBps) | * * Notably on Solana the `unitPrice` is in *micro*-lamports (so * needs a `/ 1_000_000` divisor to convert to lamports) and the * 5 000-lamport base fee per signature is added separately. Always * use the precomputed `fee.raw` field if you need a number you can * compare to a wallet balance — that is the single value the * adapter has done all the unit-conversion work for. The * `units` and `unitPrice` fields are exposed for ecosystem-aware * code paths (auction-style fee bumping, observability dashboards * scoped to one ecosystem) that already understand the formula. */ readonly unitPrice: bigint; /** Full ecosystem-specific fee data. */ readonly raw: TRaw; /** * Correlation identifier for the primitive invocation that produced * this result. * * @remarks * Matches the `traceId` in the emitted lifecycle events and is safe * to log in downstream systems for end-to-end correlation. Optional * for backward compatibility with pre-2.0 adapters. */ readonly traceId?: string | undefined; } /** Universal transaction confirmation with ecosystem-specific raw data. */ interface Confirmation { /** Whether the transaction succeeded. */ readonly success: boolean; /** Transaction identifier (hash for EVM, signature for Solana, etc.). */ readonly txId: string; /** Block or slot identifier where the transaction was confirmed. */ readonly blockId: string | bigint; /** Actual cost consumed by the transaction. */ readonly costUsed: Amount; /** Full ecosystem-specific confirmation data. */ readonly raw: TRaw; /** * Correlation identifier for the primitive invocation that produced * this confirmation. * * @remarks * Matches the `traceId` in the emitted lifecycle events. Optional * for backward compatibility. */ readonly traceId?: string | undefined; } /** Universal simulation result with ecosystem-specific raw data. */ interface Simulation { /** Whether the simulation succeeded (no revert). */ readonly success: boolean; /** * Human-readable error message if simulation failed. * * @remarks * Retained for display/logging ergonomics. Agents and programmatic * callers should prefer {@link Simulation.errorInfo}, which is the * structured `KitError` payload. */ readonly error?: string | undefined; /** * Structured error information when the simulation reverts. * * @remarks * Normalized through the adapter's `normalizeError` pipeline so * callers get the same typed error shape as any other primitive * failure (`code`, `type`, `recoverability`, etc.). Present only * when `success === false`. */ readonly errorInfo?: ErrorInfo | undefined; /** Full ecosystem-specific simulation data. */ readonly raw: TRaw; /** * Correlation identifier for the primitive invocation that produced * this simulation. */ readonly traceId?: string | undefined; } /** * Result of executing a transaction. * * @remarks * Returned by `prepared.execute()` and the standalone `execute` primitive. * Two-phase shape so callers can persist a stable transaction * identifier immediately and `await` for confirmation later (or never, * if fire-and-forget). * * The `txId` field is named for cross-ecosystem symmetry rather than * the ecosystem-native term: * - On EVM (viem, ethers): `txId` is the 0x-prefixed transaction hash. * Equivalent to viem's `result.hash` and ethers' `tx.transactionHash`. * - On Solana (solana, solana-kit): `txId` is the base58 transaction * signature. * * @example * ```typescript * const prepared = await adapter.prepare({ ... }, { chain: 'Base' }) * const result = await prepared.execute() * * console.log('submitted:', result.txId) // canonical id, available now * * // Block until the transaction is mined (or rejected on-chain). * const confirmation = await result.wait() * console.log('confirmed in block:', confirmation.blockNumber) * console.log('success?', confirmation.success) * ``` * * @typeParam TRaw - Ecosystem-specific receipt shape surfaced by `wait()`. */ interface ExecuteResult { /** * Canonical transaction identifier, available immediately after the * RPC accepts the submission. Persist this before calling `wait()` * so dropped requests can be reconciled by the operator. */ readonly txId: string; /** * Resolve once the transaction is confirmed on-chain. * * @remarks * Returns a {@link Confirmation} containing the receipt (`success`, * `blockNumber`, gas used, ecosystem-native `raw` payload, etc.). * `wait()` is idempotent — call it multiple times to re-derive the * confirmation envelope without re-submitting the transaction. */ readonly wait: () => Promise>; /** * Correlation identifier for the `execute` invocation that produced * this result. * * @remarks * Matches the `traceId` on all `op.phase.*` and `rpc.call.*` events * emitted while this transaction was being built and submitted. * Persist alongside the `txId` to later reconstruct the submission * trail (e.g. for support/audit) even across process restarts. * Optional for backward compatibility. */ readonly traceId?: string | undefined; } /** * Minimal prepared transaction interface for chain-agnostic code. * * @remarks * Ecosystem adapters extend this with more specific types. */ interface PreparedTransactionBase { readonly input: unknown; readonly built: unknown; estimate(): Promise; readonly simulate?: (() => Promise) | undefined; readonly buildRaw?: (() => Promise) | undefined; execute(...args: unknown[]): Promise; } /** * Typed prepared transaction interface. * * @typeParam TInput - The user-provided input type. * @typeParam TBuilt - The built/encoded transaction type. * @typeParam TRaw - Ecosystem-specific raw data in results. */ interface PreparedTransaction extends PreparedTransactionBase { readonly input: TInput; readonly built: TBuilt; estimate(): Promise>; execute(...args: unknown[]): Promise>; } /** * A primitive function created by `createPrimitive`. * * @typeParam TInput - The input type for the primitive. * @typeParam TOutput - The output type of the primitive. * * @remarks * This is the signature of the function returned by `createPrimitive`. * It takes input, operation metadata, and optional invocation metadata. * * **Arguments:** * - `input` - The operation input * - `operation` - **WHAT**: Target chain and optional address * - `invocation` - **WHO/HOW**: Optional traceId, runtime override, callers */ type PrimitiveFunction = (input: TInput, operation: OperationMeta, invocation?: AdapterInvocationMeta) => Promise; /** * A primitive function with a generic output type. * * @remarks * This type allows callers to specify the output type at call time, * useful for operations like `read()` where the return type depends * on the contract being called. * * @typeParam TInput - The input type for the primitive. */ type GenericPrimitiveFunction = (input: TInput, operation: OperationMeta, invocation?: AdapterInvocationMeta) => Promise; /** * Result of waiting for transaction confirmation. * * @typeParam TRaw - Ecosystem-specific receipt type. */ interface WaitResult { /** Transaction success status. */ readonly success: boolean; /** Transaction identifier that was waited for. */ readonly txId: string; /** Block/slot identifier where confirmed. */ readonly blockId: string | bigint; /** Cost consumed (access `.raw` for bigint value). */ readonly costUsed: Amount; /** Ecosystem-specific raw receipt. */ readonly raw: TRaw; } /** * Type definitions for the defineAction factory. * * @remarks * **Naming Conventions:** * - `OperationMeta`: User-facing input (WHAT - chain, address) - from `@core/adapter-base/context` * - `ResolvedActionContext`: Internal resolved context (chain as ChainDefinition) * - `ActionOptions`: Invocation options (WHO/HOW - runtime, callers, skipEstimate) * * @packageDocumentation */ /** * Options for action execution. * * @remarks * Combined options object passed as the 3rd argument to actions. * Extends `InvocationMeta` with action-specific options like `skipEstimate`. * * Defines **WHO** is calling and **HOW** to observe: * - `runtime`: Runtime override for logging, events, metrics * - `callers`: Caller chain for distributed tracing * - `skipEstimate`: Skip gas estimation for faster execution * * @example * ```typescript * // Simple usage (no options) * await action(input, ctx) * * // With options * await action(input, ctx, { skipEstimate: true }) * * // Provider calling adapter (with caller chain) * await adapter.actions.approve(input, ctx, { * callers: [ * { type: 'kit', name: 'cctp-kit', version: '1.0.0' }, * { type: 'provider', name: 'evm-provider', version: '2.1.0' }, * ], * runtime: kitRuntime, * }) * ``` */ interface ActionOptions extends AdapterInvocationMeta { /** * Skip gas/cost estimation (write actions only). * * @remarks * When true, uses fallback gas values instead of estimating. * Useful when estimation is slow or unreliable. */ readonly skipEstimate?: boolean | undefined; /** * Custom tags for observability dimensions. * * @remarks * Merged into all lifecycle events, log entries, and metric labels * emitted during this action invocation. Use for caller-specific * dimensions (e.g., `{ env: 'staging', feature: 'checkout' }`). */ readonly tags?: Tags | undefined; /** * Custom metadata propagated to middleware. * * @remarks * Attached to the execution context and included in lifecycle event * payloads (e.g., `op.phase.started` `data.meta`). Use for * domain-specific context (e.g., `{ orderId: '123', source: 'api' }`). */ readonly meta?: Record | undefined; } /** * Fully resolved action context. * * @remarks * Created internally by the action system after resolving: * - Chain → ChainDefinition * - Address → derived from adapter or provided * - TraceId → generated or provided * * All fields are guaranteed to be present. */ interface ResolvedActionContext { /** Fully resolved chain definition. */ readonly chain: ChainDefinition; /** Sender/signer address (derived or provided). */ readonly address: string; /** Trace ID for correlation (generated or provided). */ readonly traceId: string; } /** * Context passed to READ action execute functions. * * @remarks * Contains resolved context fields, the adapter instance, and typed read capability. * The read input type is automatically derived from the adapter's generic parameters. * * @typeParam TAdapter - The adapter type (extends `Adapter`). * * @example * ```typescript * execute: async (input, ctx) => { * // ctx.chain - ChainDefinition * // ctx.address - string (guaranteed) * // ctx.traceId - string (guaranteed) * // ctx.adapter - the adapter instance * // ctx.read - typed read function (input derived from adapter) * * const balance = await ctx.read({ * address: input.token, * abi: ERC20_ABI, * functionName: 'balanceOf', * args: [input.wallet], * }) * * return { raw: balance } * } * ``` */ interface ExecuteContext extends ResolvedActionContext { /** * Fully resolved chain definition, narrowed to the adapter's chain type. * * @remarks * For example, `ExecuteContext` yields `EVMChainDefinition` here, * so adapter methods like `getAddress(chain)` receive the correct type without * a cast. Falls back to the base `ChainDefinition` when the adapter's chain * type cannot be inferred. */ readonly chain: AdapterChainOf extends ChainDefinition ? AdapterChainOf : ChainDefinition; /** * The adapter instance. * * @remarks * Provides access to the adapter context via `ctx.adapter.ctx`, * and allows calling adapter methods directly if needed. */ readonly adapter: TAdapter; /** * Token registry for resolving token information. * * @remarks * Use in bound read actions to resolve token addresses or decimals * (e.g. `ctx.tokens.resolve('USDC', ctx.chain.name).locator`). */ readonly tokens: TokenRegistry; /** Resolved request-scoped invocation metadata. */ readonly invocation: AdapterInvocationContext>; /** * Execute a read operation. * * @remarks * The input type is automatically derived from the adapter's TReadInput parameter. * * @typeParam T - Expected return type. * @param input - Adapter-specific read input. * @returns The read result. */ read(input: AdapterReadInputOf): Promise; } /** * Bound read action - callable function that returns output directly. * * @typeParam TInput - Input type. * @typeParam TOutput - Output type. */ interface ReadAction { /** * Execute the read action. * * @param input - Action input. * @param operation - Operation metadata (chain, address?). * @param options - Optional action options (callers, runtime override). * @returns Action output. */ (input: TInput, operation: OperationMeta, options?: ActionOptions): Promise; /** Action name. */ readonly name: string; /** * Human-readable description of what the action does. * * @remarks * Carried over from the {@link ActionDefinition} so consumers (logs, * progress UIs, tracing tools) can show what is happening without * decoding the `name`. Always present for actions produced by * `defineAction`; optional only because derived/synthetic actions may be * constructed without going through a definition. */ readonly description?: string | undefined; /** Action type discriminator. */ readonly type: 'read'; } /** * Result of executing a write action (fast path). * * @remarks * - `txId`: The primary (last) transaction ID, available immediately * - `txIds`: All transaction IDs in execution order * - `wait()`: Lazily waits for the last transaction's on-chain confirmation * * For single-tx actions (95% of cases), `txId === txIds[0]`. * For multi-tx actions (e.g. USDT safe-approve), previous txs are already * confirmed by the time the result is returned. */ interface WriteResult { /** Primary transaction ID (last in sequence). */ readonly txId: string; /** All transaction IDs in execution order. */ readonly txIds: readonly string[]; /** Wait for the last transaction to confirm. */ readonly wait: () => Promise; } /** * What `.prepare()` returns — a `PrepareResult` with the build output attached. * * @typeParam TOutput - Semantic output from the build phase. */ type PreparedWriteAction = PrepareResult & { /** Semantic output computed during the build phase. May be undefined if build returns undefined. */ readonly output: TOutput | undefined; }; /** * Bound write action — callable function with `.prepare()` method. * * @typeParam TInput - Input type. * @typeParam TOutput - Semantic build output type (optional, defaults to void). */ interface WriteAction { /** * Execute the write action (fast path). * * @remarks * Prepares, estimates, executes sequentially, and returns the result. * Use `{ skipEstimate: true }` in options to skip fee estimation. */ (input: TInput, operation: OperationMeta, options?: ActionOptions): Promise; /** * Prepare the action for inspection before execution. * * @remarks * Returns a `PrepareResult` (with `output` attached) giving full control: * - `estimateTotal()` — aggregate fee estimates * - `transactions[]` — inspect individual prepared transactions * - `single()` — get the single transaction (throws if multiple) * - `execute()` — sequential execution with confirmations */ prepare(input: TInput, operation: OperationMeta, options?: ActionOptions): Promise>; /** Action name. */ readonly name: string; /** * Human-readable description of what the action does. * * @remarks * Carried over from the {@link ActionDefinition} so consumers (logs, * progress UIs, tracing tools) can show what is happening without * decoding the `name`. Always present for actions produced by * `defineAction`; optional only because derived/synthetic actions may be * constructed without going through a definition. */ readonly description?: string | undefined; /** Action type discriminator. */ readonly type: 'write'; } /** * Standard output containing an Amount. * * @remarks * Used by all read actions that return a quantity (balance, allowance) * and all write actions that confirm a quantity (approve, transfer). */ interface AmountOutput { /** The resolved amount (includes raw bigint + decimals). */ readonly amount: Amount; } /** Output for balance queries. */ type BalanceOutput = AmountOutput; /** Output for allowance queries. */ type AllowanceOutput = AmountOutput; /** Output for approve transactions. */ type ApproveOutput = AmountOutput; /** * Input for token.balanceOf action. */ interface TokenBalanceOfInput { /** * The token to query. * * @remarks * Accepts a known symbol (`'USDC'`), a raw address (`'0xA0b8...'`), * or an object `{ locator, decimals? }` for arbitrary tokens. * * Known symbols resolve decimals from the token registry (no RPC call). * Raw addresses may trigger an on-chain `decimals()` call if decimals * are not provided. */ readonly token: RegistryTokenSelector; /** * The wallet address to check balance for. * * @remarks * Defaults to the connected address if not provided. */ readonly walletAddress?: string | undefined; } /** * Input for token.allowance action. */ interface TokenAllowanceInput { /** * The token to query allowance for. * * @remarks * Accepts a known symbol (`'USDC'`), a raw address, or `{ locator, decimals? }`. */ readonly token: RegistryTokenSelector; /** * The wallet address whose allowance is being queried. * * @remarks * Defaults to the connected address if not provided. */ readonly walletAddress?: string | undefined; /** * The delegate address authorized to spend tokens. */ readonly delegate: string; } /** * Input for token.approve action. */ interface TokenApproveInput { /** * The token to approve spending for. * * @remarks * Accepts a known symbol (`'USDC'`), a raw address, or `{ locator, decimals? }`. */ readonly token: RegistryTokenSelector; /** * The delegate address to authorize. */ readonly delegate: string; /** * The amount to approve (in smallest units). */ readonly amount: bigint; } /** * Input for token.transfer action. */ interface TokenTransferInput { /** * The token to transfer. * * @remarks * Accepts a known symbol (`'USDC'`), a raw address, or `{ locator, decimals? }`. */ readonly token: RegistryTokenSelector; /** * The recipient address. */ readonly to: string; /** * The amount to transfer (in smallest units). */ readonly amount: bigint; } /** * Input for token.increaseAllowance action. * * @remarks * Same shape as token.approve — the difference is the contract function called. * Not all tokens support this (it's an ERC20 extension). */ interface TokenIncreaseAllowanceInput { readonly token: RegistryTokenSelector; readonly delegate: string; readonly amount: bigint; } /** * Input for token.name action. */ interface TokenNameInput { /** * The token to query. * * @remarks * Accepts a known symbol (`'USDC'`), a raw address (`'0xA0b8...'`), * or an object `{ locator, decimals? }` for arbitrary tokens. * * Resolution matches every other `token.*` action, so a known symbol * comes from the token registry and a raw address is used as given. */ readonly token: RegistryTokenSelector; } /** * Output for token.name action. * * @remarks * The value is wrapped in an object so the shape stays stable if the action * grows more fields. The legacy-compat bridge unwraps the single key, so * callers on the old `prepareAction` surface receive the bare string. */ interface TokenNameOutput { /** * The on-chain name of the token contract. * * @remarks * For USDC this is also the EIP-712 domain name, for example `'USD Coin'`. * Permit and authorize signing flows use it to build the domain separator. */ readonly name: string; } /** * Input for native.balanceOf action. */ interface NativeBalanceOfInput { /** * The wallet address to check balance for. * * @remarks * Defaults to the connected address if not provided. */ readonly walletAddress?: string | undefined; } /** * Input for native.transfer action. */ interface NativeTransferInput { /** The recipient address. */ readonly to: string; /** The amount to transfer (in smallest units, e.g. wei). */ readonly amount: bigint; } /** * EIP-2612 permit parameters for gasless token approvals. * * @remarks * Enables a user to approve a token transfer via an off-chain signature, allowing * for gasless approvals in compliant smart contracts. */ interface PermitParams$1 { /** Expiry timestamp for the permit signature (in seconds since epoch). */ readonly deadline: bigint; /** Signature recovery ID (EIP-2098/ECDSA 'v' value). */ readonly v: number; /** ECDSA signature 'r' value (as 0x-prefixed hex string). */ readonly r: string; /** ECDSA signature 's' value (as 0x-prefixed hex string). */ readonly s: string; } /** * Input for cctp.v2.depositForBurn action. * * @remarks * Burns USDC on the source chain to initiate a cross-chain transfer. */ interface CCTPDepositForBurnInput { /** Amount of USDC to burn (in smallest units). */ readonly amount: bigint; /** Recipient address on the destination chain. */ readonly mintRecipient: string; /** Optional caller restriction on destination chain. */ readonly destinationCaller?: string | undefined; /** Maximum fee for the transfer. */ readonly maxFee: bigint; /** Minimum finality threshold (block confirmations). */ readonly minFinalityThreshold: number; /** Destination chain where tokens will be minted. */ readonly toChain: ChainIdentifier$1; } /** * Input for cctp.v2.receiveMessage action. * * @remarks * Receives and mints USDC on the destination chain using the attestation. */ interface CCTPReceiveMessageInput { /** Event nonce from the burn transaction (0x-prefixed hex string). */ readonly eventNonce: string; /** Attestation from Circle's attestation service. */ readonly attestation: string; /** CCTP message bytes from the burn event. */ readonly message: string; /** Source chain where the burn occurred. */ readonly fromChain: ChainIdentifier$1; /** Optional destination address override. */ readonly destinationAddress?: string | undefined; /** Optional mint recipient override. */ readonly mintRecipient?: string | undefined; } /** * Input for cctp.v2.customBurn action. * * @remarks * Uses a custom bridge contract with preapproval funnel for protocol fees. */ interface CCTPCustomBurnInput extends CCTPDepositForBurnInput { /** Optional protocol fee amount. */ readonly protocolFee?: bigint | undefined; /** Optional fee recipient address. */ readonly feeRecipient?: string | undefined; /** Optional EIP-2612 permit parameters for gasless approval. */ readonly permitParams?: PermitParams$1 | undefined; } /** * Input for cctp.v2.depositForBurnWithHook action. * * @remarks * Same as depositForBurn but includes hookData for Circle's Orbit relayer * to auto-relay/mint on the destination chain. */ interface CCTPDepositForBurnWithHookInput extends CCTPDepositForBurnInput { /** Hex-encoded hook data for the Orbit relayer (0x-prefixed). */ readonly hookData: string; } /** * Input for cctp.v2.customBurnWithHook action. * * @remarks * Same as customBurn but includes hookData for Circle's Orbit relayer * to auto-relay/mint on the destination chain. */ interface CCTPCustomBurnWithHookInput extends CCTPCustomBurnInput { /** Hex-encoded hook data for the Orbit relayer (0x-prefixed). */ readonly hookData: string; } interface GatewayDepositInput { readonly token: string; readonly value: bigint; } interface GatewayDepositForInput { readonly token: string; readonly depositor: string; readonly value: bigint; } interface GatewayDepositWithPermitInput { readonly token: string; readonly owner: string; readonly value: bigint; readonly deadline: bigint; readonly signature?: string; readonly v?: number; readonly r?: string; readonly s?: string; } interface GatewayDepositWithAuthorizationInput { readonly token: string; readonly from: string; readonly value: bigint; readonly validAfter: bigint; readonly validBefore: bigint; readonly nonce: string; readonly signature?: string; readonly v?: number; readonly r?: string; readonly s?: string; } interface GatewayAddDelegateInput { readonly token: string; readonly delegate: string; } interface GatewayRemoveDelegateInput { readonly token: string; readonly delegate: string; } interface GatewayIsDelegateInput { readonly token: string; readonly depositor: string; readonly delegate: string; } interface GatewayInitiateWithdrawalInput { readonly token: string; readonly value: bigint; } interface GatewayWithdrawInput { readonly token: string; } interface GatewayWithdrawingBalanceInput { readonly token: string; readonly depositor: string; } interface GatewayWithdrawalBlockInput { readonly token: string; readonly depositor: string; } interface GatewayBurnInput { readonly calldataBytes: `0x${string}`; readonly signature: `0x${string}`; } interface GatewayMintInput { readonly attestation: `0x${string}`; readonly signature: `0x${string}`; } interface GatewaySignBurnIntentsInput { readonly typedData: unknown; } /** Output for isDelegate read action. */ interface GatewayIsDelegateOutput { readonly isDelegate: boolean; } /** * Output for `gateway.v1.withdrawingBalance` read action. * * @remarks * The pending withdrawal balance is returned as a decimal-encoded u64 string * in base units (for example, `'5000000'` for 5 USDC on a 6-decimal token). * Bindings stringify the raw u64 rather than returning a `bigint` so that * the shape is stable across the legacy-compat bridge (`adaptReadAction`) * and downstream consumers such as `providers/gateway.v1`. */ interface GatewayWithdrawingBalanceOutput { readonly balance: string; } /** * Output for `gateway.v1.withdrawalBlock` read action. * * @remarks * The block number at which a pending withdrawal may be completed, returned * as a `bigint` because block numbers are u64 quantities and are not * balance-like values. The legacy-compat bridge (`adaptReadAction`) * unwraps this field transparently for callers that still consume the * legacy `PreparedChainRequest.execute()` surface. */ interface GatewayWithdrawalBlockOutput { readonly blockNumber: bigint; } /** Output for signBurnIntents action. */ interface GatewaySignatureOutput { readonly signature: string; /** * Whether Gateway must validate `signature` with ERC-1271 rather than * `ecrecover`, i.e. whether the signer is a contract account. Absent on * ecosystems where ERC-1271 does not apply (Solana) and on wirings that * cannot classify the signer, both of which mean "validate as an EOA". */ readonly contractSigner?: boolean; } /** * Permit signature standards for gasless token approvals used by the Earn * Adapter Contract. * * @remarks * - `NONE` (0): Tokens must be pre-approved via a separate transaction. * - `EIP2612` (1): Standard ERC-20 permit (USDC and most modern tokens). */ declare enum EarnPermitType { /** No permit required — tokens must be pre-approved. */ NONE = 0, /** EIP-2612 standard permit. */ EIP2612 = 1 } /** * Token input with optional permit signature for use with the Earn Adapter * Contract. * * @remarks * Passed verbatim to the contract's `execute(executeParams, tokenInputs, * signature)` call. Today the earn provider always uses `NONE` (pre-approval * path); the permit fields are reserved for a future gasless approval path. */ interface EarnTokenInput { /** Type of permit to execute. */ readonly permitType: EarnPermitType; /** Token contract address. */ readonly token: `0x${string}`; /** Amount of tokens in the smallest unit. */ readonly amount: bigint; /** ABI-encoded permit calldata (empty bytes for `NONE`). */ readonly permitCalldata: `0x${string}`; } /** * Input for all three earn action keys. * * @remarks * `executeParams` is intentionally typed as `Record` to * preserve the raw service-signed payload unchanged — the Adapter Contract * ABI-decodes it on-chain. * * @example * ```typescript * import type { EarnExecuteInput } from '@core/adapter-base' * * const input: EarnExecuteInput = { * executeParams: { * instructions: [], * tokens: [], * execId: 1n, * deadline: 0n, * metadata: '0x', * }, * tokenInputs: [], * signature: '0x' + 'ab'.repeat(65), * } * ``` */ interface EarnExecuteInput { /** * Service-signed execution parameters. * * Kept as an opaque record so the adapter forwards the service-signed * struct unchanged. */ readonly executeParams: Record; /** * Token inputs with permit signatures for gasless approvals. * * Populated by the earn provider after deciding how token spending is * authorised. Today deposit uses a separate token allowance transaction and * passes `PermitType.NONE`; a future permit-enabled path can populate this * field without a breaking change. */ readonly tokenInputs: readonly EarnTokenInput[]; /** * EIP-712 signature from the earn service proxy. * * The Adapter Contract verifies this signature on-chain before executing * the requested operation. */ readonly signature: `0x${string}`; } /** * Permit signature standards for gasless token approvals. * * @remarks * - `NONE` (0): No permit, tokens must be pre-approved via separate transaction. * - `EIP2612` (1): Standard ERC-20 permit (USDC, DAI v2, and most modern tokens). */ declare enum SwapPermitType { /** No permit required — tokens must be pre-approved. */ NONE = 0, /** EIP-2612 standard permit. */ EIP2612 = 1 } /** * Token input with permit signature for gasless approval. * * @remarks * The Adapter Contract uses this to pull tokens from the user's wallet using * permit signatures instead of requiring a separate approval transaction. */ interface SwapTokenInput { /** Type of permit to execute. */ readonly permitType: SwapPermitType; /** Token contract address to pull from user. */ readonly token: `0x${string}`; /** Amount of tokens to pull via permit. */ readonly amount: bigint; /** ABI-encoded permit calldata. */ readonly permitCalldata: `0x${string}`; } /** * Single instruction to execute within the Adapter Contract. * * @remarks * Each instruction represents a contract call (swap, fee collection, etc.) * with pre-execution approval and post-execution validation. */ interface SwapInstruction { /** Target contract address to call. */ readonly target: `0x${string}`; /** ABI-encoded calldata for the target contract. */ readonly data: `0x${string}`; /** Native value to send with the call (for native token operations). */ readonly value: bigint; /** Token to approve to target before executing instruction. */ readonly tokenIn: `0x${string}`; /** Amount of tokenIn to approve to target before executing instruction. */ readonly amountToApprove: bigint; /** Token to validate minimum balance after instruction. */ readonly tokenOut: `0x${string}`; /** Minimum required balance of tokenOut after instruction. */ readonly minTokenOut: bigint; } /** * Token recipient for residual sweep. * * @remarks * After all instructions complete, the Adapter Contract sweeps any remaining * balances to the specified beneficiaries. */ interface SwapTokenRecipient { /** Token contract address to sweep. */ readonly token: `0x${string}`; /** Address to receive swept tokens. */ readonly beneficiary: `0x${string}`; } /** * Execution parameters for the Adapter Contract. * * @remarks * Signed via EIP-712 by Circle's proxy service and verified on-chain. These * values are supplied by the stablecoin-service and must be forwarded to the * Adapter Contract exactly as received (no modification). */ interface SwapExecuteParams { /** Array of instructions to execute sequentially. */ readonly instructions: readonly SwapInstruction[]; /** Token recipients for residual sweep. */ readonly tokens: readonly SwapTokenRecipient[]; /** Unique execution identifier for replay protection. */ readonly execId: bigint; /** Execution deadline timestamp (Unix seconds). */ readonly deadline: bigint; /** Optional metadata for tracking and analytics. */ readonly metadata: `0x${string}`; } /** * Parameters for executing a swap via the EVM Adapter Contract. * * @remarks * The SDK calls `AdapterContract.execute(executeParams, tokenInputs, signature)`, * which pulls tokens via permits, executes swaps, validates outputs, and * sweeps residuals — all atomically in a single transaction. */ interface ExecuteSwapEVMInput { /** Execution parameters from the stablecoin-service (EIP-712 signed). */ readonly executeParams: SwapExecuteParams; /** Token inputs with permit signatures for gasless approvals. */ readonly tokenInputs: readonly SwapTokenInput[]; /** EIP-712 signature from the Circle proxy service. */ readonly signature: `0x${string}`; /** Swap input amount in base units (tx.value for native swaps). */ readonly inputAmount: bigint; /** Token address being swapped from (native sentinel triggers value transfer). */ readonly tokenInAddress: `0x${string}`; } /** * Parameters for executing a swap on Solana. * * @remarks * The stablecoin-service returns a fully built `VersionedTransaction` that * already contains routing, fees, slippage, ATA setup and Address Lookup * Tables. The SDK deserialises, signs, and submits it unchanged. */ interface ExecuteSwapSolanaInput { /** Base64-encoded serialized Solana transaction. */ readonly serializedTransaction: string; } /** * Union of all chain-specific swap inputs. * * @remarks * Each ecosystem binding narrows this union at runtime using a * property-based guard (EVM has `executeParams`/`tokenInputs`, Solana has * `serializedTransaction`). */ type ExecuteSwapInput = ExecuteSwapEVMInput | ExecuteSwapSolanaInput; /** * Action registry types and composition helpers. * * @remarks * Provides: * - `BaseActionRegistry` — the minimum set of actions every adapter must expose * - `StablecoinXxxInput` — input types for stablecoin wrappers (token omitted) * - `withToken` / `withTokenWrite` — compose a stablecoin action from a generic * token action by pre-filling the `token` field * * Ecosystem packages (`@core/adapter-evm-base`, etc.) extend `BaseActionRegistry` * with chain-specific actions and use the composition helpers to create defaults. * * @packageDocumentation */ /** Input for stablecoin balance queries (token pre-filled). */ type StablecoinBalanceOfInput = Omit; /** Input for stablecoin allowance queries (token pre-filled). */ type StablecoinAllowanceInput = Omit; /** Input for stablecoin approve actions (token pre-filled). */ type StablecoinApproveInput = Omit; /** Input for stablecoin transfer actions (token pre-filled). */ type StablecoinTransferInput = Omit; /** Input for stablecoin increaseAllowance actions (token pre-filled). */ type StablecoinIncreaseAllowanceInput = Omit; /** Input for stablecoin name queries (token pre-filled). */ type StablecoinNameInput = Omit; /** * The minimum set of actions every adapter must expose. * * @remarks * Ecosystem packages extend this with chain-specific actions * (e.g. CCTP v2 bridge actions). */ interface BaseActionRegistry { 'token.balanceOf': ReadAction; 'token.allowance': ReadAction; 'token.approve': WriteAction; 'token.transfer': WriteAction; 'token.increaseAllowance': WriteAction; 'token.name': ReadAction; 'native.balanceOf': ReadAction; 'native.transfer': WriteAction; /** * @deprecated Use `token.balanceOf` with `{ token: 'USDC' }` instead. * These USDC convenience aliases will move to `@core/adapter-compat`. */ 'usdc.balanceOf': ReadAction; /** * @deprecated Use `token.allowance` with `{ token: 'USDC' }` instead. * These USDC convenience aliases will move to `@core/adapter-compat`. */ 'usdc.allowance': ReadAction; /** * @deprecated Use `token.approve` with `{ token: 'USDC' }` instead. * These USDC convenience aliases will move to `@core/adapter-compat`. */ 'usdc.approve': WriteAction; /** * @deprecated Use `token.transfer` with `{ token: 'USDC' }` instead. * These USDC convenience aliases will move to `@core/adapter-compat`. */ 'usdc.transfer': WriteAction; /** * @deprecated Use `token.increaseAllowance` with `{ token: 'USDC' }` instead. * These USDC convenience aliases will move to `@core/adapter-compat`. */ 'usdc.increaseAllowance': WriteAction; /** * @deprecated Use `token.name` with `{ token: 'USDC' }` instead. * These USDC convenience aliases will move to `@core/adapter-compat`. */ 'usdc.name': ReadAction; } /** * Overloaded action dispatch signature. * * @typeParam TRegistry - The action registry mapping keys to functions. * * @remarks * - `action(key)` returns the typed action object * - `action(key, ...args)` calls the action directly (fast path) */ interface ActionDispatch$1 unknown>> { (key: K): TRegistry[K]; (key: K, ...args: Parameters): ReturnType; } /** * Strongly-typed event subscription interface for adapters. * * @remarks * Mirrors the `kit.on` / `kit.off` pattern from BridgeKit but uses * the augmentable {@link KitEventMap} from `@core/runtime`. Packages * can extend `KitEventMap` via module augmentation to register * adapter-specific events. * * The `on` method returns an unsubscribe function (modern pattern). * The `off` method accepts the original handler reference and * removes it (convenience for callers who prefer explicit cleanup). * * @typeParam M - The event map (defaults to {@link KitEventMap}). * * @example * ```typescript * const adapter = createViemAdapterFromPrivateKey({ privateKey }) * * // Subscribe to a specific event * adapter.on('op.phase.failed', (event) => { * console.error('Operation failed:', event.data) * }) * * // Subscribe with pattern matching * adapter.on('op.phase.*', (event) => { * console.log(event.name, event.data) * }) * * // Explicit unsubscribe via off() * const handler = (event) => console.log(event) * adapter.on('op.phase.started', handler) * adapter.off('op.phase.started', handler) * * // Or use the returned unsubscribe function * const unsub = adapter.on('op.phase.succeeded', (event) => { * console.log('Succeeded:', event.data) * }) * unsub() // unsubscribe * ``` */ interface AdapterEventSubscription { /** * Subscribe to all events. * * @param handler - Handler receiving any event as a discriminated union. * @returns Unsubscribe function. */ on(handler: TypedEventHandler>): () => void; /** * Subscribe to a specific event by exact name. * * @typeParam K - The event name literal. * @param name - The event name. * @param handler - Handler receiving the typed event. * @returns Unsubscribe function. */ on>(name: K, handler: TypedEventHandler>): () => void; /** * Subscribe to events matching a glob pattern. * * @typeParam P - The pattern string (supports `*` and `**` wildcards). * @param pattern - The pattern to match event names against. * @param handler - Handler receiving matched events as a discriminated union. * @returns Unsubscribe function. */ on

(pattern: P, handler: TypedEventHandler, P>>>): () => void; /** * Remove a previously registered handler. * * @param handler - The handler function reference to remove. */ off(handler: TypedEventHandler>): void; /** * Remove a handler registered for a specific event name. * * @typeParam K - The event name literal. * @param name - The event name. * @param handler - The handler function reference to remove. */ off>(name: K, handler: TypedEventHandler>): void; } /** * Types for the {@link assembleAdapter} factory. * * @packageDocumentation */ /** * Generic function bound for type parameters in adapter assembly. * * @remarks * TypeScript requires `any` in function type bounds for correct * contravariant parameter inference. Concrete types are always inferred * from call sites — `any` never leaks to consumers. */ type AnyFunction = (...args: any[]) => any; /** * Generic record bound for action registries. * * @remarks * TypeScript interfaces lack implicit index signatures, so * `Record unknown>` rejects them. * This alias centralises the necessary `any` escape. */ type AnyRecord = Record; /** * The core shape returned by {@link assembleAdapter}. * * @remarks * Contains all the fields that every adapter must expose. When * `extras` are provided, they are intersected onto this shape. * * This type satisfies `StandardAdapter` from `@core/adapter-compat` * so the result can be passed directly to `withLegacyCompat`. * * @typeParam TCtx - The adapter-specific context type. * @typeParam TChainType - Literal chain-type string. * @typeParam TRead - Read primitive type (preserved from input). * @typeParam TPrepare - Prepare primitive type (preserved from input). * @typeParam TWait - WaitForTransaction primitive type (preserved from input). * @typeParam TActions - Action registry type. */ interface AssembledAdapterShape, TChainType extends string, TRead extends AnyFunction, TPrepare extends AnyFunction, TWait extends AnyFunction, TActions extends AnyRecord> extends AdapterEventSubscription { /** Adapter context (clients, capabilities, runtime, etc.). */ readonly ctx: TCtx; /** * Adapter capabilities (shortcut for `ctx.capabilities`). * * @remarks * Derived from `TCtx['capabilities']` so the concrete capability literal * (e.g. `addressContext: 'developer-controlled'`) is preserved through * assembly. This is what lets kits infer address-context from * `typeof adapter` instead of seeing the widened `AdapterCapabilities`. */ readonly capabilities: TCtx['capabilities']; /** Chain ecosystem type. */ readonly chainType: TChainType; /** Read primitive. */ readonly read: TRead; /** Prepare primitive. */ readonly prepare: TPrepare; /** Get the connected wallet address for a chain. */ readonly getAddress: TCtx['getAddress']; /** * Type-safe action dispatch. * * @remarks * - `action(key)` — returns the typed action object * - `action(key, ...args)` — calls the action directly (fast path) */ readonly action: ActionDispatch$1>; /** Wait for a transaction to be confirmed. */ readonly waitForTransaction: TWait; /** Validate that a chain is supported by this adapter. */ readonly validateChainSupport: (chain: ChainDefinition) => void; } /** * Shared EVM types for all EVM adapters (viem, ethers, etc.). * * @packageDocumentation */ /** * Input for reading from an EVM smart contract. * * @remarks * This is the standard input format for contract read operations * across all EVM adapters. */ interface EVMReadInput { /** Contract address (hex string). */ readonly address: `0x${string}`; /** Contract ABI (array of ABI items). */ readonly abi: readonly unknown[]; /** Function name to call. */ readonly functionName: string; /** Function arguments. */ readonly args?: readonly unknown[] | undefined; } /** * Input for writing to an EVM smart contract or performing a native transfer. * * @remarks * This is the standard input format for contract write operations * across all EVM adapters. * * **Native transfers**: When `abi` is empty and `functionName` is `''`, * the write represents a plain native-token transfer (ETH, MATIC, etc.) * rather than a contract call. Use {@link isNativeTransfer} to detect * this case in adapter implementations. Use {@link createNativeTransferWrite} * to construct native transfer inputs. */ interface EVMWriteInput { /** Contract address or transfer recipient (hex string). */ readonly address: `0x${string}`; /** Contract ABI (array of ABI items). Empty for native transfers. */ readonly abi: readonly unknown[]; /** Function name to call. Empty string for native transfers. */ readonly functionName: string; /** Function arguments. */ readonly args?: readonly unknown[] | undefined; /** Native token value to send (in wei). */ readonly value?: bigint | undefined; } /** Canonical payload reviewed by phase-one EVM authorization hooks. */ type EvmAuthorizationPayload = TransactionPayload; /** Adapter configuration bound to the canonical EVM payload union. */ type EvmAuthorizationOptions = AuthorizationOptions; /** * Base EVM adapter interface. * * @remarks * All EVM adapters (viem, ethers, etc.) implement this interface. * It extends the generic `Adapter` with EVM-specific input types. * * Native balance is provided via `createEVMActions({ getNativeBalance })`, * not as an adapter method. * * @typeParam TContext - The adapter-specific context type. * * @example * ```typescript * import type { EVMAdapter } from '@core/adapter-evm-base' * import type { AdapterContext } from '@core/adapter-base' * * type MyEVMAdapter = EVMAdapter * ``` */ type EVMAdapter = AdapterContext$1> = Adapter$1; /** Callback to fetch the native token balance for a given wallet on an EVM chain. */ type GetNativeBalance = (chain: EVMChainDefinition, wallet: string) => Promise; /** * Gateway v1 EVM action bindings. * * @remarks * Each binding maps a chain-agnostic Gateway action definition to the EVM * contract call pattern: resolve the Gateway Wallet / Minter address from * the chain, then queue a `ctx.write()` or execute a `ctx.read()`. * * @packageDocumentation */ /** * Callback shape for EIP-712 typed-data signing, injected by the ecosystem * adapter (viem, ethers, …) into the gateway `signBurnIntents` binding. * * @remarks * `signBurnIntents` is an off-chain signing operation — not a contract call — * so it cannot go through the generic `ctx.read` / `ctx.write` surface used * by the other gateway bindings. Instead, the ecosystem adapter provides * its `signTypedData` primitive via {@link BindGatewaySignBurnIntentsOptions} * and the binding delegates to it. * * The callback receives the raw `typedData` payload plus the resolved * `ExecuteContext` so the ecosystem adapter can route the signature through * its wallet client, preserving observability and error-normalization. * * Accepts the typed data as `unknown` because Gateway burn-intents omit * `chainId`/`verifyingContract` from the domain — structural shape is * validated by the ecosystem adapter's own EIP-712 schema (e.g. viem's * `assertTypedData`) rather than the base binding. */ type EVMSignTypedDataCallback = (typedData: unknown, ctx: ExecuteContext) => Promise<`0x${string}`>; /** * Callback shape for an EVM `eth_getCode` lookup, injected by the ecosystem * adapter (viem, ethers, …) into the gateway `signBurnIntents` binding. * * @remarks * Used by {@link isContractSigner} to detect on-chain bytecode at the * prospective signer address. Gateway validates EOA signatures with * `ecrecover` and contract-account signatures with ERC-1271, but it does not * infer which one applies — the transfer request has to declare it, and this * hook is how the binding works out which to declare. * * Implementations should return `'0x'` (or `undefined`) for an EOA and the * raw deployed bytecode for a contract account. Errors are caught at the call * site so an RPC failure falls back to the EOA path rather than blocking * signing. */ type EVMReadBytecodeCallback = (address: string, chain: ChainDefinition) => Promise<`0x${string}` | undefined>; /** * EVM action factory. * * @remarks * Composes individual token and native bindings into a full `EVMActionRegistry`. * Each binding lives in its own `bindings/` subfolder; this file only assembles them. * * @packageDocumentation */ /** * EVM action registry extending base actions with CCTP v2 bridge operations. * * @example * ```typescript * import type { EVMActionRegistry } from '@core/adapter-evm-base' * * const actions: EVMActionRegistry = createEVMActions(adapter, { getNativeBalance }) * await actions['cctp.v2.depositForBurn'].prepare({ amount: 1000000n, ... }, { chain }) * ``` */ interface EVMActionRegistry extends BaseActionRegistry { 'cctp.v2.depositForBurn': WriteAction; 'cctp.v2.receiveMessage': WriteAction; 'cctp.v2.customBurn': WriteAction; 'cctp.v2.depositForBurnWithHook': WriteAction; 'cctp.v2.customBurnWithHook': WriteAction; 'gateway.v1.deposit': WriteAction; 'gateway.v1.depositFor': WriteAction; 'gateway.v1.depositWithPermit': WriteAction; 'gateway.v1.depositWithAuthorization': WriteAction; 'gateway.v1.addDelegate': WriteAction; 'gateway.v1.removeDelegate': WriteAction; 'gateway.v1.isDelegate': ReadAction; 'gateway.v1.initiateWithdrawal': WriteAction; 'gateway.v1.withdraw': WriteAction; 'gateway.v1.withdrawingBalance': ReadAction; 'gateway.v1.withdrawalBlock': ReadAction; 'gateway.v1.gatewayBurn': WriteAction; 'gateway.v1.gatewayMint': WriteAction; 'gateway.v1.signBurnIntents': ReadAction; 'swap.execute': WriteAction; 'usdt.transfer': WriteAction<{ to: string; amount: bigint; }, void, EvmAuthorizationPayload>; 'earn.deposit': WriteAction; 'earn.withdraw': WriteAction; 'earn.claimRewards': WriteAction; } /** * Configuration options for creating EVM actions. * * @remarks * `getNativeBalance` is the only factory option because native-token balance * queries are platform-specific: viem calls `getBalance`, ethers calls * `provider.getBalance`, etc. All other actions go through the generic * `ctx.read` abstraction (contract calls via ABI), so no injection is needed. * * @example * ```typescript * import type { CreateEVMActionsOptions } from '@core/adapter-evm-base' * * const options: CreateEVMActionsOptions = { * getNativeBalance: async (chain, wallet) => 1000000000000000000n, * } * ``` */ interface CreateEVMActionsOptions { /** * Fetch the native token balance for a wallet. * * @remarks * Injected here because native balance queries bypass the ERC-20 ABI path * and require a platform-specific RPC call (e.g. `eth_getBalance`). */ readonly getNativeBalance: GetNativeBalance; /** * Sign EIP-712 typed data via the ecosystem adapter's wallet surface. * * @remarks * Injected here because off-chain signing bypasses the `ctx.read` / * `ctx.write` ABI paths — ecosystem adapters (viem, ethers, …) each * expose their own `signTypedData` primitive. When provided, the * `gateway.v1.signBurnIntents` action delegates to this callback; when * omitted, the action returns a no-op sentinel signature (`'0x'`) to * preserve backward-compat with callers that have not wired a signer. * * @example * ```typescript * createEVMActions(adapter, { * getNativeBalance, * signTypedData: async (typedData, ctx) => { * return adapter.signTypedData(typedData, ctx) * }, * }) * ``` */ readonly signTypedData?: EVMSignTypedDataCallback; /** * Read raw on-chain bytecode (`eth_getCode`) for a given address and chain. * * @remarks * Injected here so the `gateway.v1.signBurnIntents` binding can classify * the signer before producing a Gateway burn-intent signature. Gateway * validates EOA signatures with `ecrecover` and contract-account * signatures with ERC-1271, but it does not infer which one applies — the * transfer request has to declare it. * * When provided alongside `signTypedData`, the binding fetches bytecode for * the resolved signer address and reports `contractSigner: true` when * bytecode is present (and is not an EIP-7702 delegation prefix). When * omitted, the signer is reported as an EOA. * * @example * ```typescript * createEVMActions(deps, { * getNativeBalance, * signTypedData, * readBytecode: async (address, chain) => { * const publicClient = await deps.ctx.getPublicClient(chain) * return (await publicClient.getCode({ address })) ?? '0x' * }, * }) * ``` */ readonly readBytecode?: EVMReadBytecodeCallback; } /** * Create a complete EVM action registry from an adapter. * * @remarks * Uses `createActionBindings` for the base token/native actions and USDC * aliases, then adds EVM-specific CCTP v2 bridge actions. * * @param adapter - Any EVM adapter (viem, ethers, etc.). * @param options - Platform-specific callbacks for native operations. * @returns A fully-typed action registry. */ declare function createEVMActions(adapter: EVMAdapter, options: CreateEVMActionsOptions): EVMActionRegistry; /** * Shared EVM Zod schemas and types. * * @packageDocumentation */ /** * Zod type for any hex-prefixed string (`0x...`). * * @example * ```typescript * hexString.parse('0xdeadbeef') // ✓ * hexString.parse('deadbeef') // ✗ — missing 0x prefix * ``` */ declare const hexString: z.ZodType<`0x${string}`, z.ZodTypeDef, `0x${string}`>; /** * Zod type for EVM addresses (`0x` + 40 hex chars). * * @example * ```typescript * hexAddress.parse('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48') // ✓ * hexAddress.parse('0xshort') // ✗ — not 40 hex characters * ``` */ declare const hexAddress: z.ZodType<`0x${string}`, z.ZodTypeDef, `0x${string}`>; /** * Zod type for 32-byte hex values (`0x` + 64 hex chars). * * @remarks * Used for values like transaction hashes, message hashes, and domain IDs. * * @example * ```typescript * hexBytes32.parse('0x' + 'ab'.repeat(32)) // ✓ * hexBytes32.parse('0x1234') // ✗ — not 64 hex characters * ``` */ declare const hexBytes32: z.ZodType<`0x${string}`, z.ZodTypeDef, `0x${string}`>; /** A raw EVM transaction ready for estimation, simulation, or execution. */ interface RawTransaction { /** The recipient contract or wallet address. */ readonly to: `0x${string}`; /** The sender (signer) address. */ readonly from: `0x${string}`; /** ABI-encoded calldata for the transaction. */ readonly data: `0x${string}`; /** Native token value to send with the transaction (in wei). */ readonly value: bigint; } /** * Gas and nonce overrides for transaction execution. * * @remarks * Two pricing models are supported — use one or the other, not both: * * - **EIP-1559** (`maxFeePerGas` + `maxPriorityFeePerGas`): Preferred * for most modern EVM chains (mainnet, L2s). Set the maximum total * fee you're willing to pay and the tip for validators. * * - **Legacy** (`gasPrice`): For chains that don't support EIP-1559 * (some L2s, sidechains). Sets a flat per-gas price. * * In most cases you should **not** set these — the gas pricing resolver * handles estimation automatically with a buffer for reliable inclusion. * Only override when you have specific requirements (e.g. priority * transactions, fixed-price environments). */ interface GasOverrides { /** Gas limit override. When omitted, estimated automatically. */ readonly gas?: bigint | undefined; /** EIP-1559 max fee per gas (in wei). Mutually exclusive with `gasPrice`. */ readonly maxFeePerGas?: bigint | undefined; /** EIP-1559 max priority fee per gas (in wei). Mutually exclusive with `gasPrice`. */ readonly maxPriorityFeePerGas?: bigint | undefined; /** Legacy gas price (in wei). Mutually exclusive with `maxFeePerGas`/`maxPriorityFeePerGas`. */ readonly gasPrice?: bigint | undefined; /** Transaction nonce override. When omitted, resolved from the network. */ readonly nonce?: number | undefined; } /** Raw transaction with optional gas/nonce overrides. */ interface ExecuteTransactionInput { /** The raw transaction to execute. */ readonly raw: RawTransaction; /** Optional gas and nonce overrides. */ readonly overrides?: GasOverrides | undefined; } /** * Fee calculation raw data for EVM transactions. * * @example * ```typescript * import type { EVMFeeRaw } from '@core/adapter-evm-base' * * const feeRaw: EVMFeeRaw = { * gasEstimate: 21000n, * maxFeePerGas: 30_000_000_000n, * maxPriorityFeePerGas: 2_000_000_000n, * } * ``` */ interface EVMFeeRaw { readonly gasEstimate?: bigint | undefined; readonly maxFeePerGas?: bigint | undefined; readonly maxPriorityFeePerGas?: bigint | undefined; readonly gasPrice?: bigint | undefined; } /** * EVM fee estimation result with required gas estimate. * * @remarks * Extends {@link EVMFeeRaw} with a mandatory `gasEstimate` field. * * @example * ```typescript * import type { EVMEstimateRaw } from '@core/adapter-evm-base' * * const estimateRaw: EVMEstimateRaw = { * gasEstimate: 21000n, * maxFeePerGas: 30_000_000_000n, * maxPriorityFeePerGas: 2_000_000_000n, * } * ``` */ interface EVMEstimateRaw extends EVMFeeRaw { readonly gasEstimate: bigint; } /** * Input parameters for EVM transaction fee estimation. * * @example * ```typescript * import type { EstimateInput } from '@core/adapter-evm-base' * * const input: EstimateInput = { * to: '0x1234567890123456789012345678901234567890', * from: '0x9876543210987654321098765432109876543210', * data: '0xabcdef', * value: 0n, * } * ``` */ type EstimateInput = RawTransaction; /** * Fee estimation result for EVM transactions. * * @example * ```typescript * import type { EstimateResult } from '@core/adapter-evm-base' * * const result: EstimateResult = { * raw: { gasEstimate: 21000n, maxFeePerGas: 30_000_000_000n }, * cost: Amount.of(630_000_000_000_000n, { decimals: 18 }), * } * ``` */ type EstimateResult = FeeEstimate; /** * Primitive function type for estimating EVM transaction fees. * * @example * ```typescript * import type { EstimatePrimitive } from '@core/adapter-evm-base' * * const estimate: EstimatePrimitive = async (input) => ({ * raw: { gasEstimate: 21000n }, * cost: Amount.of(0n, { decimals: 18 }), * }) * ``` */ type EstimatePrimitive = PrimitiveFunction; /** Raw simulation result from an EVM transaction simulation. */ interface EVMSimulateRaw { readonly returnData?: `0x${string}` | undefined; } /** Input for EVM transaction simulation (raw transaction). */ type SimulateInput = RawTransaction; /** Result of an EVM transaction simulation. */ type SimulateResult = Simulation; /** Primitive function signature for EVM transaction simulation. */ type SimulatePrimitive = PrimitiveFunction; /** * Input for a pre-built raw EVM transaction. * * @remarks * Use this variant when you already have an encoded transaction * (e.g. from a third-party aggregator or multisig builder) and * want the prepare pipeline to handle estimation, simulation, * and execution without re-encoding. * * @example * ```typescript * const input: PrepareRawInput = { * type: 'raw', * raw: { * to: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', * from: '0x1234567890123456789012345678901234567890', * data: '0xa9059cbb000000000000000000000000...', * value: 0n, * }, * } * ``` */ interface PrepareRawInput { /** Discriminator — must be `'raw'` for pre-built transactions. */ readonly type: 'raw'; /** The pre-built raw transaction. */ readonly raw: RawTransaction; } /** * Input for a plain native-token transfer (ETH send). * * @remarks * Carries only the recipient address and value — there is no ABI, function * name, or calldata. Use this variant (or {@link createNativeTransferWrite}) * to transfer the chain's native currency without triggering a smart-contract * call. The prepare pipeline emits a `sendTransaction` (no `data`) for this * variant. * * @example * ```typescript * const input: PrepareNativeInput = { * type: 'native', * address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', * value: 1_000_000_000_000_000n, * } * ``` */ interface PrepareNativeInput { /** Discriminator — must be `'native'` for native-token transfers. */ readonly type: 'native'; /** Recipient address. */ readonly address: `0x${string}`; /** Amount in wei (or the chain's smallest unit). */ readonly value: bigint; } /** * Chain-native finality requirement. * * @remarks * For EVM chains this is the number of block confirmations. * * @example * ```typescript * import type { ChainNativeFinality } from '@core/adapter-evm-base' * * const finality: ChainNativeFinality = 12 // 12 block confirmations * ``` */ type ChainNativeFinality = number | string; /** * Input for waiting for transaction confirmation. * * @example * ```typescript * import type { WaitForTransactionInput } from '@core/adapter-evm-base' * * const input: WaitForTransactionInput = { * txId: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', * finality: 12, * timeout: 60_000, * } * ``` */ interface WaitForTransactionInput { readonly txId: string; readonly finality?: ChainNativeFinality | undefined; readonly timeout?: number | undefined; } /** * 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; }; /** * Initiate a prepaid cross-chain USDC transfer through the `TokenMessengerWithFees` wrapper. * * Burn USDC on the source chain while collecting all fees up front against a * signed quote. The wrapper collects the fee via `FeeManager`, then delegates * to the unmodified `TokenMessengerV2`. When `hookData` is provided (the * GenericExecutor FORWARD path) the wrapper's `depositForBurnWithHookAndFees` * contract method is used; otherwise `depositForBurnWithFees` is used. * * @remarks * SDK/contract naming: this SDK action is `depositForBurnWithFees` but, when a * `hookData` is present, it dispatches to the `depositForBurnWithHookAndFees` * contract method on `TokenMessengerWithFees` (NOT on `TokenMessengerV2`). * * Fee payment channel (must match the quote's `feeToken`): * - Native fee (`feeToken` is the zero address): exactly `feeTotalAmount` is * attached as `msg.value`. * - ERC-20 fee (e.g. USDC): no value is attached; the caller must first approve * the wrapper for `feeTotalAmount` (see the provider's fee approval helper). * * @remarks * Unlike `depositForBurn`, the `TokenMessengerWithFees` contract methods do NOT * take `maxFee` or `minFinalityThreshold` — fee and finality behavior are * derived from the signed quote — so those fields are omitted from this action. * * @example * ```typescript * await adapter.action('cctp.v2.depositForBurnWithFees', { * amount: BigInt('1000000'), * mintRecipient: executorAddress, // GenericExecutor (bytes32) * destinationCaller: executorAddress, // GenericExecutor (bytes32) * fromChain: ethereum, * toChain: arc, * hookData: geForwardHookData, // cctp-forward-wrapped GenericExecutor blob * claim: { signedQuote: '0x...', refundAddress: '0x...' }, * feeToken: '0x0000000000000000000000000000000000000000', // native * feeTotalAmount: 3500000n, * }) * ``` */ depositForBurnWithFees: Omit & { /** * Optional hex-encoded hook data for the GenericExecutor FORWARD path. * * When present, the `depositForBurnWithHookAndFees` contract method is used * and the blob must be wrapped in the `cctp-forward` envelope (the wrapper * rejects a FORWARD fee quote whose hook lacks it). When omitted, the plain * `depositForBurnWithFees` contract method is used. */ hookData?: string; /** * Signed fee quote claim passed to the `TokenMessengerWithFees` wrapper. * * `signedQuote` is the `[uint8 0x01][abi.encode(Quote)]` blob returned by the * Fee Quote service; `refundAddress` receives any fee overpayment refund. */ claim: QuoteClaim; /** * Fee token from the signed quote. * * The zero address (`0x000…0`) means the fee is paid in native currency and * is attached as `msg.value`. Any other address (e.g. USDC) means an ERC-20 * fee that must be approved to the wrapper beforehand. This is independent of * `burnToken`, which is always USDC. */ feeToken: string; /** * Total fee amount from the signed quote, in `feeToken` minor units. * * Firm only until the quote's `expiresAt`. For a native fee this is the exact * `msg.value`; for an ERC-20 fee this is the amount approved to the wrapper. */ feeTotalAmount: bigint; }; } /** * Signed fee quote claim consumed by the `TokenMessengerWithFees` wrapper. * * Mirrors the on-chain `IFeeManager.QuoteClaim` struct. * * @example * ```typescript * const claim: QuoteClaim = { * signedQuote: '0x01...', // [uint8 0x01][abi.encode(Quote)] * refundAddress: '0xUserWallet...', * } * ``` */ interface QuoteClaim { /** * Opaque signed quote bytes (`0x` hex) from the fee-quote service * (`SignedFeeQuote.signedQuote` returned by `fetchFeeQuote`). Pass verbatim; * do not decode. * * The quote binds the FORWARD fee item to the on-chain call via `argsHash`; * passing a quote that does not match the burn args reverts `QuoteArgsMismatch`. */ signedQuote: string; /** * Address that receives any refund of overpaid fees. * * Typically the user wallet that authorized the burn. */ refundAddress: 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; } /** * Action map for Circle's CCTPx protocol operations. * * Define the parameter schemas for CCTPx actions that enable cross-chain transfers * of registered tokens (Circle-issued or otherwise) through Circle's `CrossChainTokenService` * (CCTS) contract. * * @remarks * CCTPx is a service-level protocol layered on top of CCTP v2's message-passing layer. * The CCTS contract coordinates token locking/burning, fee collection, and cross-chain * message dispatch. The SDK obtains a signed fee quote from IRIS, then calls * `crossChainTransfer` on CCTS with the quote bytes verbatim and the native fee as * `msg.value`. The auto-relay flow handled by Circle's Orbit relayer (paid for via the * `FORWARD` fee component included in the signed quote) means no separate * `receiveMessage` step is required on the destination. * * USDC and EURC bridging continues to use CCTP v2 (`cctp.v2.*`) actions, not CCTPx. * * @example * ```typescript * import type { ActionPayload } from '@core/adapter' * * const transferParams: ActionPayload<'cctpx.crossChainTransfer'> = { * tokenId: '0xabc123...', * amount: 1_000_000n, * destinationDomain: 1, * destinationAddress: '0xRecipient', * destinationCaller: '0x0000000000000000000000000000000000000000000000000000000000000000', * minFinalityThreshold: 1000, * claim: { signedQuote: '0xdeadbeef...', refundAddress: '0xSenderEOA...' }, * autoExecuteHookData: false, * hookData: '0x', * serviceAddress: '0xCCTSProxy...', * nativeFeeAmount: 100_000n, * fromChain, * } * ``` */ interface CCTPXActionMap { /** * Initiate a CCTPx cross-chain transfer through the `CrossChainTokenService` contract. * * Encode and submit a `crossChainTransfer(...)` call to the CCTS proxy on the source * chain, passing the IRIS-signed fee quote bytes verbatim and the native fee as * `msg.value`. The contract emits CCTP v2's `MessageSent` event, which IRIS attests * to before Circle's Orbit relayer auto-executes the destination mint. * * @remarks * The caller (typically `CCTPXBridgingProvider`) is responsible for: * - Resolving `tokenId` and the per-chain `tokenAddress` from the IRIS token registry * - Approving the per-token `TokenManager` for `amount` before this call * - Fetching `claim.signedQuote` and computing `nativeFeeAmount` from IRIS * * This action only encodes and submits the on-chain call; it does not perform any * off-chain orchestration. */ crossChainTransfer: ActionParameters & { /** * The CCTPx tokenId for the asset being transferred. * * Provided as a 32-byte hex string assigned by CCTPx at registration time. * The same `tokenId` is used across all chains for a given token; per-chain * `tokenAddress` is resolved from the IRIS token registry separately. */ tokenId: string; /** * Amount of the token to transfer, in the token's smallest units. */ amount: bigint; /** * CCTP domain identifier of the destination chain. * * CCTPx reuses CCTP v2 domain numbering; pass `dstChain.cctp.domain`. */ destinationDomain: number; /** * Recipient address on the destination chain, encoded as bytes. * * For EVM destinations this is a 20-byte address encoded as a hex string. */ destinationAddress: string; /** * `bytes32` value restricting which address may execute on the destination. * * Omit (or pass the 32-byte zero hash) to allow permissionless relay — the * default for auto-relayed CCTPx transfers. When omitted, the handler * substitutes the zero hash. * * @defaultValue `ZERO_HASH` — permissionless relay */ destinationCaller?: string; /** * Minimum finality threshold for attestation eligibility. * * Use `1000` for FAST transfers (pre-finality) or `2000` for SLOW transfers * (full finality). For FAST, the `claim.signedQuote` must include a * `PRE_FINALITY` item; otherwise the on-chain call reverts. */ minFinalityThreshold: number; /** * The CCTS fee-quote claim — maps 1:1 to the on-chain * `IFeeManager.QuoteClaim` tuple. * * The contract requires a tuple here, not a flat bytes blob. Encoding the * signed quote without the tuple wrapper produces a different function * selector and the call will revert. */ claim: { /** * IRIS-signed fee quote bytes, passed verbatim to the contract. * * Obtained from `POST /v1/quote/cctpx/{tokenId}/{src}/{dst}`. Contains the * version-prefixed ABI-encoded `Quote` struct and Circle's signature; the * `FeeManager` contract validates the signature against the quote items. */ signedQuote: string; /** * Address that receives any native-fee refund from `FeeManager`. * * Forwarded verbatim to `FeeManager` for refund attribution. The contract * accepts `address(0)` (the zero address) to disable refunds, so omitting * this field is safe; the handler will substitute the zero address. * * @defaultValue `ZERO_ADDRESS` — refunds disabled */ refundAddress?: string; }; /** * Whether the destination chain should auto-execute the hook data. * * For basic transfers this is `false`. Reserved for advanced integrations * that bundle a post-mint hook on the destination. */ autoExecuteHookData: boolean; /** * Optional hook data bytes passed through to the destination handler. * * Pass `'0x'` (empty bytes) for basic transfers. */ hookData: string; /** * The `CrossChainTokenService` proxy address on the source chain. * * Used as the transaction `to` field. Typically sourced from * `srcChain.cctpx.serviceAddress` — but is passed as an explicit parameter * so the action does not depend on chain-config narrowing at the call site. */ serviceAddress: string; /** * Native gas amount to send as `msg.value`. * * Must exactly equal `sum(quote.items[].amount)` when `feeToken` is the * native currency (the P0 default). The contract verifies the value against * the signed quote; do not over-send. */ nativeFeeAmount: bigint; /** * Source chain definition. * * Provides the adapter with chain context (chainId, RPC, etc.) for the call. */ fromChain: ChainDefinition; }; } /** * 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: 'TEST_API_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: 'TEST_API_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; }; /** * Get the on-chain name of the token contract. * * This is a read-only operation. For USDC the value is also the EIP-712 * domain name, which permit and authorize signing flows need. */ name: ActionParameters & { /** * The contract address of the token. */ tokenAddress: string; }; } /** * 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; /** CCTPx operations (CrossChainTokenService) for cross-chain transfers of registered tokens (Circle-issued or otherwise). */ readonly cctpx: CCTPXActionMap; /** 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; } /** * Canonical list of actions that do not prepare or submit transactions. * * @internal */ declare const READ_ACTION_KEYS: readonly ["token.allowance", "token.balanceOf", "token.name", "native.balanceOf", "usdc.allowance", "usdc.balanceOf", "usdc.name", "gateway.v1.isDelegate", "gateway.v1.withdrawingBalance", "gateway.v1.withdrawalBlock", "gateway.v1.signBurnIntents"]; /** * Action keys that execute without preparing or submitting a transaction. * * @remarks * Derive this type from the canonical runtime list so compile-time and runtime * classification cannot drift. `gateway.v1.signBurnIntents` is included * because the action system models off-chain signing as a read action: it does * not prepare a chain request. * * @example * ```typescript * import type { ReadActionKey } from '@core/adapter' * * const action: ReadActionKey = 'token.allowance' * ``` */ type ReadActionKey = (typeof READ_ACTION_KEYS)[number]; /** * 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; /** * Execute a non-transaction action without routing through transaction preparation. * * @remarks * Use this seam for balance, allowance, contract-state, and other actions * classified as reads. It never calls {@link Adapter.prepareAction}, so * transaction authorization wrappers only observe actions that can produce a * signable chain request. * * @typeParam TActionKey - The read action key. * @param action - The read action to execute. * @param params - The parameters for the read action. * @param ctx - The operation context. * @returns The raw action response. * @throws {KitError} When the key is not a read action or no handler is registered. * @throws Error When the operation context or action handler fails. * * @example * ```typescript * import { Ethereum } from '@core/chains' * * const balance = await adapter.readAction( * 'token.balanceOf', * { tokenAddress, walletAddress }, * { chain: Ethereum }, * ) * ``` * * @internal */ readAction(action: TActionKey, params: ActionPayload, ctx: OperationContext): Promise; /** * Read the current token allowance a delegate holds over an owner's tokens. * * @remarks * Perform a network read through {@link Adapter.readAction}. This method * never routes through {@link Adapter.prepareAction}. On chains without an * allowance model, such as Solana, return the maximum uint256 value. * * @param params - The token to query and the delegate whose allowance is being read. * @param ctx - Operation context with compile-time validated address requirements. * @returns A promise resolving to the current allowance in the token's base units. * @throws {KitError} When the adapter does not register a `token.allowance` handler. * @throws Error When the operation context or action handler fails. * * @example * ```typescript * import type { Adapter } from '@core/adapter' * import { Ethereum } from '@core/chains' * * declare const adapter: Adapter * * const allowance = await adapter.getTokenAllowance( * { * tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', * delegate: '0x1111111111111111111111111111111111111111', * }, * { chain: Ethereum }, * ) * console.log(allowance) // 1000000n * ``` */ getTokenAllowance(params: ActionPayload<'token.allowance'>, 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; } /** * Types for the legacy compatibility layer. * * @remarks * Defines: * - `StandardAdapter` — the new clean adapter contract all adapters implement * - `LegacyCompatConfig` — ecosystem-specific configuration for the HOC * - `LegacyCompatibleAdapterBase` — concrete `Adapter` subclass that the HOC instantiates * - `LegacyCompatibleAdapter` — result type that combines new methods with the `Adapter` class * - Legacy return types (`LegacyEstimatedGas`, `LegacyPreparedChainRequest`, `LegacyWaitForTransactionResponse`) * * @packageDocumentation */ /** * The new clean adapter contract that all adapters implement. * * @remarks * This interface captures the canonical adapter shape. Ecosystem adapters * (viem, ethers, solana) may extend it with additional methods (e.g. `signTypedData`), * but this is the minimum surface that the compat layer wraps. * * @typeParam TContext - The adapter context type (e.g. viem's `AdapterContext`). * @typeParam TActions - The action registry type (e.g. `EVMActionRegistry`). * @typeParam TChain - The chain definition type. */ interface StandardAdapter { /** Adapter capabilities. */ readonly capabilities: AdapterCapabilities$1; /** Chain ecosystem type (e.g. `'evm'`, `'solana'`). */ readonly chainType: ChainType; /** Validate that a chain is supported by this adapter. */ readonly validateChainSupport: (chain: ChainDefinition) => void; /** * Wait for a transaction to be confirmed (new 2-arg signature). */ readonly waitForTransaction: (input: { txId: string; finality?: number | string; timeout?: number; }, meta: OperationMeta) => Promise; /** * Action dispatch — resolve an action by key. * * @remarks * Used by the legacy `prepareAction` wrapper to look up individual * actions without requiring the full action registry. */ readonly action: (key: string) => unknown; /** * Read a token's decimal precision on-chain. * * @remarks * Declared here (rather than left as an ecosystem-specific extra) so the * type system — not naming convention — ties each adapter's * implementation to the {@link LegacyCompatibleAdapterBase.getTokenDecimals} * stub that `withLegacyCompat` falls back to. Every adapter passed to * `withLegacyCompat` must supply this, or the call fails to typecheck. * * @param tokenAddress - The token's contract address or mint. * @param chain - The chain the token lives on. * @returns The token's decimal precision. */ readonly getTokenDecimals: (tokenAddress: string, chain: ChainDefinition) => Promise; } /** * Legacy estimated gas result. * * @deprecated Prefer the structured `FeeEstimate` returned by `calculateFee`. */ interface LegacyEstimatedGas { /** Gas/compute units. */ gas: bigint; /** Price per unit. */ gasPrice: bigint; /** Total fee as a decimal string. */ fee: string; } /** * Legacy prepared chain request returned by `prepareAction`. * * @remarks * The optional `type` field enables noop detection: when a write action * calls `ctx.noop()`, the compat layer sets `type: 'noop'` so that * downstream consumers (e.g. `executePreparedChainRequest`) can skip * execution without calling `estimate()` or `execute()`. For EVM * ecosystems the enricher hook may additionally set `type: 'evm'` and * expose `getCallData()` so batched-execution consumers can access the * raw `{ to, data, value }` tuple. * * @deprecated Use the action-based API instead. */ interface LegacyPreparedChainRequest { /** * Discriminator. * * - `'noop'` for no-op results. * - `'evm'` when an EVM enricher has attached `getCallData()`. */ readonly type?: 'noop' | 'evm' | undefined; estimate(): Promise; /** * Submit the prepared transaction and return the tx identifier * **without waiting for confirmation**. * * @remarks * Mirrors the legacy `EvmPreparedChainRequest.execute(overrides?)` / * `SolanaPreparedChainRequest.execute(overrides?)` contract: the * promise resolves with the submitted txId as soon as the wallet * accepts the transaction. Callers (typically AppKit and the swap * provider) perform their own confirmation wait via * `adapter.waitForTransaction(...)`. * * The `overrides` argument forwards an ecosystem-specific override * record. The compat layer normalizes legacy field names (e.g. EVM * `gasLimit` → `gas`) before handing it to the underlying primitive. */ execute(overrides?: unknown): Promise; /** * Access the raw call tuple for EIP-5792 batched execution. * * @remarks * Populated by an ecosystem-specific enricher (see * {@link LegacyCompatConfig.enrichWriteRequest}) for single-transaction * EVM write actions. Adapters that don't implement calldata extraction * (or multi-step write actions) will omit this accessor. * * @returns The raw `{ to, data, value }` tuple. * @since 2.0.0 */ getCallData?(): { to: `0x${string}`; data: `0x${string}`; value?: bigint | undefined; }; } /** * The minimal shape of a prepared write result the enricher can read from. * * @remarks * Matches the structural shape produced by `@core/adapter-base`'s write * action `.prepare()` — just enough for ecosystem-specific enrichers to * peek at the first built transaction without taking a hard dependency * on `PrepareResult<...>`'s generics. */ interface LegacyEnrichablePreparedResult { /** The prepared transactions. Ecosystem enrichers narrow this structurally. */ readonly transactions: readonly unknown[]; } /** * Hook used by the compat layer to enrich a single-transaction legacy * prepared request with ecosystem-specific fields (e.g. EVM `type: 'evm'` * and `getCallData()`). * * @param legacy - The base legacy prepared request (estimate + execute only). * @param prepared - The underlying new-style prepare result (read-only). * @returns The enriched legacy prepared request, or the unchanged input. */ type LegacyWriteRequestEnricher = (legacy: LegacyPreparedChainRequest, prepared: LegacyEnrichablePreparedResult) => LegacyPreparedChainRequest; type InvocationAwarePrepareActionFn = (action: string, params: Record, ctx: OperationMeta, invocation?: AdapterInvocationMeta) => Promise>; /** * Concrete `Adapter` subclass used by the `withLegacyCompat` HOC. * * @remarks * This class provides the **`extends Adapter`** relationship that makes the * compat layer work across bundled `.d.ts` files. When the DTS bundler inlines * `Adapter` into each package bundle, an intersection type like `T & Adapter` * creates two nominally distinct `Adapter` copies that TypeScript can't reconcile. * A real `extends` chain avoids this — the same pattern all existing adapters use. * * The abstract methods are implemented with placeholder stubs. At runtime, * `withLegacyCompat` uses `Object.assign` to overwrite them with real * delegating implementations. */ declare class LegacyCompatibleAdapterBase extends Adapter { chainType: ChainType | 'hybrid'; prepare(_params: PreparedChainRequestParams, _ctx: OperationContext): Promise; getAddress(_chain: ChainDefinition): Promise; switchToChain(_chain: ChainDefinition): Promise; waitForTransaction(_txHash: string, _config: WaitForTransactionConfig | undefined, _chain: ChainDefinition): Promise; calculateTransactionFee(_baseComputeUnits: bigint, _bufferBasisPoints: bigint | undefined, _chain: ChainDefinition): Promise; /** * Reject because the wrapped adapter supplies no `getTokenDecimals`. * * @remarks * Unlike the stubs above, `withLegacyCompat` never replaces this method. * The wrapped adapter must expose `getTokenDecimals` as an extra of * `assembleAdapter`, otherwise this stub reaches production and every token * outside the registry fails. The rejection is a `KitError` so the failure * stays inside the SDK error contract instead of escaping it as a plain * `Error`. * * The error message only tells the caller that the lookup is unavailable. * It does not name `assembleAdapter`, `extra`, or `withLegacyCompat` — this * stub can surface through App Kit or Swap Kit, and those internal wiring * names mean nothing to a consumer of the adapter. Adapter authors: supply * a `getTokenDecimals` extra to `assembleAdapter` before you wrap the * adapter with `withLegacyCompat`, so production code never reaches this * stub in the first place. * * @param _tokenAddress - The token address, ignored by the stub. * @param _chain - The chain definition, ignored by the stub. * @returns A promise that always rejects. * @throws `KitError` with `INPUT_UNSUPPORTED_ACTION` on every call. * * @example * ```typescript * import { LegacyCompatibleAdapterBase } from '@core/adapter-compat' * import { Solana } from '@core/chains' * * const stub = new LegacyCompatibleAdapterBase() * await stub * .getTokenDecimals('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', Solana) * .catch((error: unknown) => console.error(error)) * ``` */ getTokenDecimals(_tokenAddress: string, _chain: ChainDefinition): Promise; } /** * The public result type of `withLegacyCompat`. * * @remarks * Combines the new adapter's unique methods (those NOT on `Adapter`) with * `LegacyCompatibleAdapterBase extends Adapter`. This avoids the intersection * conflicts that occur when both `TAdapter` and `Adapter` define the same * method with different signatures (e.g. `waitForTransaction`). * * **`prepare` is exposed as an overload** of `TAdapter['prepare']` AND the * legacy `Adapter.prepare(params, ctx)` signature. At runtime * `Object.assign(instance, adapter, legacyMethods)` overwrites the legacy * `Adapter.prepare(...)` stub with the new `prepare(input, meta) => * PrepareResult<...>` signature from `TAdapter`, but consumers still treat * the wrapped adapter as a structural `Adapter` in many places (e.g. the * compass-e2e fork helpers). Using the intersection * `TAdapter['prepare'] & LegacyCompatibleAdapterBase['prepare']` preserves * assignability to the legacy `Adapter` shape **and** lets the new README * examples (`prepared.transactions`, `prepared.single()`, * `prepared.estimateTotal()`, `prepared.execute()`) compile under strict * mode without `as any`. See #853 round-2 review (finding N9). * * Other properties overlapping with `Adapter` (like `chainType`, * `capabilities`, `waitForTransaction`, `getAddress`, `validateChainSupport`, * `calculateTransactionFee`) come from the `Adapter` side via the class. * New adapter-specific properties (`ctx`, `read`, `signTypedData`, `action`, * `calculateFee`) are preserved from `TAdapter`. * * At runtime, `Object.assign` copies all methods from `TAdapter` onto the * class instance, so both old and new calling conventions work. */ type LegacyCompatibleAdapter = Omit & Omit & { /** * Adapter capabilities, preserved from the wrapped adapter (A1). * * @remarks * Sourced from `TAdapter['capabilities']` rather than the legacy * `Adapter` base so the concrete `addressContext` literal (e.g. * `'developer-controlled'`) survives compat wrapping. This is what lets * kits infer address-context requirements from `typeof adapter`. A * narrowed capability type is still structurally assignable to the legacy * `Adapter['capabilities']` shape, so existing consumers are unaffected. */ readonly capabilities: TAdapter['capabilities']; prepare: ('prepare' extends keyof TAdapter ? TAdapter['prepare'] : LegacyCompatibleAdapterBase['prepare']) & LegacyCompatibleAdapterBase['prepare']; /** Prepare an action while preserving request-scoped adapter metadata. */ prepareAction: InvocationAwarePrepareActionFn> & LegacyCompatibleAdapterBase['prepareAction']; }; /** * Configuration options for creating a Viem adapter context. * * @typeParam TCapabilities - The adapter capabilities type. * * @example * ```typescript * import { createAdapterContext } from '@circle-fin/adapter-viem-v2/next' * import { createPublicClient, createWalletClient, http, custom } from 'viem' * import { Ethereum, Base } from '@circle-fin/bridge-kit/chains' * * const options: AdapterContextOptions = { * capabilities: { * addressContext: 'user-controlled', * supportedChains: [Ethereum, Base], * }, * getPublicClient: ({ chain }) => createPublicClient({ chain, transport: http() }), * getWalletClient: ({ chain }) => createWalletClient({ chain, transport: custom(window.ethereum) }), * } * * const ctx = createAdapterContext(options) * ``` */ interface AdapterContextOptions extends EvmAuthorizationOptions { /** * Adapter capabilities defining address control and supported chains. */ readonly capabilities: TCapabilities; /** * Factory function to create PublicClient instances for read operations. * * @remarks * Called when a PublicClient is needed for a specific chain. * Results are cached per chain ID. * * @param params - Parameters including the target viem Chain * @returns PublicClient instance (or promise resolving to one) */ getPublicClient: (params: { chain: Chain; }) => Promise | PublicClient; /** * Factory function to create WalletClient instances for signing/write operations. * * @remarks * Called when a WalletClient is needed for a specific chain. * - For `user-controlled` adapters: Called fresh each time (no caching) * - For `developer-controlled` adapters: Results are cached per chain ID * * Optional when a {@link AdapterContextOptions.signing | signing} strategy is * provided (the strategy owns authorization, so the adapter runs keyless) or * for a pure read-only adapter (reads, gas estimation, and simulation use only * the PublicClient). When omitted, any operation that needs a WalletClient * fails with a clear error. * * @param params - Parameters including the target viem Chain * @returns WalletClient instance (or promise resolving to one) */ getWalletClient?: (params: { chain: Chain; }) => Promise | WalletClient; /** * Pluggable signing strategy for transaction authorization. * * @remarks * When provided, write operations (`execute`, `signTypedData`, `batchExecute`) * are routed through the strategy instead of a WalletClient: the adapter * builds each ready-to-sign payload and the strategy decides how it gets * authorized — in-process, by the caller's own infrastructure (see * `externalSigning` from `@core/adapter-base`), or by a remote service. * Read operations, gas estimation, pre-flight simulation, and receipt waiting * are unaffected and keep using the PublicClient. * * When the strategy returns an already-broadcast transaction hash the adapter * does not submit anything itself; when it returns signed bytes the adapter * validates the echoed signer against the expected sender and submits them via * `eth_sendRawTransaction`. Dispatch is capability-gated by the strategy's * manifest — a payload family the manifest does not declare fails with a clear * error before the strategy is invoked. */ signing?: SigningStrategy | undefined; /** * A fixed address to bind to a keyless or read-only adapter. * * @remarks * For `user-controlled` adapters that have no WalletClient (a read-only * adapter, or one whose signing is owned by a {@link * AdapterContextOptions.signing | signing} strategy), there is no wallet to * resolve the current address from. Set this so `getAddress()` — and the * per-operation address resolution that read verbs rely on — returns a * concrete sender without a hand-rolled wallet stub. Ignored for * `developer-controlled` adapters, which always take the address per call. */ address?: `0x${string}` | undefined; /** * Runtime services or configuration options. * * @remarks * Pass a pre-created `Runtime` to share the same event bus and logger * across the adapter and transport instrumentation, or pass * `RuntimeOptions` to let the context create one. */ runtime?: Runtime | RuntimeOptions; /** * Optional token registry. */ tokens?: TokenRegistry; /** * Optional operational configuration (retry, etc.). */ config?: OperationalConfig; /** * Force-enable WalletClient caching regardless of `addressContext`. * * @remarks * By default, WalletClients are only cached for `developer-controlled` * adapters (because user-controlled wallets may switch accounts). * * Set to `true` for adapters with a deterministic account (e.g. private * key) where the account never changes and reusing the client is safe and * necessary for viem's internal nonce tracking (`nonceManager`). * * @defaultValue Inferred from `capabilities.addressContext` */ cacheWalletClients?: boolean; /** * Optional chain-switch behaviour applied before write operations. * * @remarks * When provided, the context exposes an `ensureChain` hook that * `createPrimitive` invokes (via `ensureChainBefore: true`) before * running a primitive. The hook reads the wallet's current chain * and reconciles it with the target chain according to the chosen * `onMismatch` policy. * * Defaults applied by the factory helpers: * - `createViemAdapterFromProvider` — `'prompt'` * (user-controlled wallets should be asked to switch). * - `createViemAdapterFromPrivateKey` — `'skip'` * (no wallet UI exists; transport pins the chain). */ chainSwitch?: ChainSwitchConfig; } /** * Extended adapter context with Viem-specific client accessors. * * @typeParam TCapabilities - The adapter capabilities type. * * @remarks * Extends the base `AdapterContext` with methods to access Viem clients * (PublicClient, WalletClient) with proper caching and lifecycle management. * * @example * ```typescript * import { createAdapterContext } from '@circle-fin/adapter-viem-v2/next' * import { Ethereum } from '@circle-fin/bridge-kit/chains' * * const ctx = createAdapterContext({ capabilities, getPublicClient, getWalletClient }) * * // Get cached PublicClient * const publicClient = await ctx.getPublicClient(Ethereum) * const balance = await publicClient.getBalance({ address: '0x...' }) * * // Clear cache on account change * ctx.resetClients() * ``` */ interface AdapterContext extends AdapterContext$1 { /** Fixed address configured for keyless or read-only authorization. */ readonly boundAddress?: `0x${string}` | undefined; /** * Get a PublicClient for the specified chain. * * @remarks * PublicClients are cached per chain ID for efficiency. * They are stateless and safe to cache. * * @param chain - The EVM chain definition * @returns Promise resolving to the PublicClient */ getPublicClient: (chain: EVMChainDefinition) => Promise; /** * Get a WalletClient for the specified chain. * * @remarks * Caching behavior depends on `addressContext`: * - `developer-controlled`: Cached per chain ID (address is explicit) * - `user-controlled`: Not cached (user may switch accounts) * * @param chain - The EVM chain definition * @returns Promise resolving to the WalletClient */ getWalletClient: (chain: EVMChainDefinition) => Promise; /** * Clear all cached clients. * * @remarks * Call this when you need to force re-initialization of clients, * for example after detecting an account or network change. */ resetClients: () => void; /** * Whether a WalletClient factory was configured. * * @remarks * `true` when the adapter can produce a WalletClient (wallet-backed); * `false` for read-only adapters and for keyless adapters that route * authorization through a {@link AdapterContext.signing | signing} strategy. * Capability probes such as `supportsSignTypedData` consult this so a * read-only adapter never claims a signing capability it cannot fulfil. */ readonly hasWalletClient: boolean; /** * The signing strategy that owns transaction authorization, when one was * configured. Primitives route write operations through it instead of the * WalletClient. Absent for wallet-backed and read-only adapters. */ readonly signing?: SigningStrategy | undefined; } /** * Create a Viem adapter context with client caching and lifecycle management. * * @typeParam TCapabilities - The adapter capabilities type. * @param options - Configuration options for the context. * @returns An AdapterContext instance (exported as `ViemAdapterContext` publicly). * * @remarks * This factory creates an adapter context that: * - Caches PublicClients per chain (always safe) * - Conditionally caches WalletClients based on `addressContext` * - Provides a `resetClients` method for cache invalidation * - Integrates with `@core/adapter-base` infrastructure * * **Client Caching Strategy:** * * | Client Type | `user-controlled` | `developer-controlled` | * |-------------|-------------------|------------------------| * | PublicClient | Cached | Cached | * | WalletClient | Not cached | Cached | * * For `user-controlled` adapters, WalletClients are not cached because * the user may switch accounts in their wallet UI at any time. * * @example * ```typescript * import { createAdapterContext } from '@adapters/viem.v2/next' * import { createPublicClient, createWalletClient, http, custom } from 'viem' * import { Ethereum, Base } from '@circle-fin/bridge-kit/chains' * * const ctx = createAdapterContext({ * capabilities: { * addressContext: 'user-controlled', * supportedChains: [Ethereum, Base], * }, * getPublicClient: ({ chain }) => createPublicClient({ chain, transport: http() }), * getWalletClient: ({ chain }) => createWalletClient({ chain, transport: custom(window.ethereum) }), * }) * * // Use the context * const publicClient = await ctx.getPublicClient(Ethereum) * const walletClient = await ctx.getWalletClient(Ethereum) * ``` */ declare function createAdapterContext(options: AdapterContextOptions): AdapterContext; /** * Resolve a viem {@link Chain} from a `ChainDefinition` object — the * Phase-D, enum-free replacement for `getViemChainByEnum` used by the * `/next` adapter surface. * * @packageDocumentation */ /** * Resolve the viem `Chain` for an EVM chain definition. * * @remarks * Resolution order: * 1. Look up a curated viem `Chain` by `definition.chainId`. * 2. Otherwise synthesize a `Chain` from the definition's `chainId`, `name`, * `nativeCurrency`, and `rpcEndpoints`. * * This is the `/next` (Phase D) replacement for `getViemChainByEnum`: it * resolves *any* well-formed `ChainDefinition`, including custom chains that * are not members of the `Blockchain` enum, without a `switch` over enum * members. * * @param definition - The EVM chain definition to resolve. * @returns The corresponding viem `Chain`. * @throws KitError `INPUT_UNRECOGNIZED_CHAIN` (1012) when the definition is * missing a numeric `chainId` or has no RPC endpoints. The message names * the `getPublicClient` escape hatch. * * @example * ```typescript * import { resolveViemChain } from '@circle-fin/adapter-viem-v2/next' * import { Ethereum } from '@circle-fin/bridge-kit/chains' * * const chain = resolveViemChain(Ethereum) * console.log(chain.id) // 1 * ``` * * @example * ```typescript * // A fully custom chain (not in the Blockchain enum) resolves too: * const anvil = resolveViemChain({ * type: 'evm', * name: 'Anvil', * chainId: 31337, * nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, * explorerUrl: '', * rpcEndpoints: ['http://127.0.0.1:8545'], * } as EVMChainDefinition) * console.log(anvil.id) // 31337 * ``` */ declare function resolveViemChain(definition: EVMChainDefinition): Chain; /** * Input for reading a contract function. * * @remarks * Mirrors viem's `readContract` parameters with type-safe address format. */ interface ReadContractInput { /** * The contract address to read from. * * @example '0x1234567890abcdef1234567890abcdef12345678' */ readonly address: `0x${string}`; /** * The contract ABI. * * @remarks * Can be a full ABI array or a narrowed subset containing just the * function you're calling. */ readonly abi: Abi$1; /** * The function name to call. * * @example 'balanceOf' */ readonly functionName: string; /** * Optional function arguments. * * @remarks * Arguments are passed in order and must match the function signature. */ readonly args?: readonly unknown[] | undefined; } /** * The read primitive function type. * * @remarks * Returns a generic primitive that allows callers to specify the output type. */ type ReadPrimitive = GenericPrimitiveFunction; /** * Creates a read contract primitive for the viem adapter. * * @param ctx - The viem adapter context containing client accessors. * @returns A read primitive function. * * @remarks * This primitive reads data from smart contracts without sending transactions. * It's used for: * - Querying token balances (`balanceOf`) * - Reading contract state * - Fetching configuration values * * The return type is generic - callers specify the expected type at call time. * * @example * ```typescript * const ctx = createAdapterContext({ ... }) * const read = createRead(ctx) * * // Read a balance (returns bigint) * const balance = await read( * { * address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', * abi: erc20Abi, * functionName: 'balanceOf', * args: ['0x1234...'], * }, * { chain: Ethereum }, * ) * * // Read a string value * const name = await read( * { * address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', * abi: erc20Abi, * functionName: 'name', * }, * { chain: Ethereum }, * ) * ``` */ declare function createRead(ctx: AdapterContext): ReadPrimitive; /** * EVM-specific raw receipt data. */ interface EVMReceiptRaw { /** Full viem transaction receipt. */ readonly receipt: TransactionReceipt; } /** * Result type for waitForTransaction. */ type WaitForTransactionResult = WaitResult; /** * The waitForTransaction primitive function type. */ type WaitForTransactionPrimitive = PrimitiveFunction; /** * Creates a waitForTransaction primitive for the viem adapter. * * @param ctx - The viem adapter context containing client accessors. * @returns A waitForTransaction primitive function. * * @example * ```typescript * const waitForTransaction = createWaitForTransaction(ctx) * * const result = await waitForTransaction( * { txId: '0x1234...' }, * { chain: Ethereum }, * ) * * if (result.success) { * console.log(`Confirmed in block ${result.blockId}`) * console.log(`Gas cost: ${result.costUsed}`) * } * * // With finality from chain config * const result2 = await waitForTransaction( * { txId: '0x5678...', finality: 65 }, * { chain: Ethereum }, * ) * ``` */ declare function createWaitForTransaction(ctx: AdapterContext): WaitForTransactionPrimitive; /** * Viem-adapter-specific raw data for fee estimates. * * @remarks * Contains both EIP-1559 and legacy fee data, depending on chain support. * Distinct from `EVMFeeRaw` in `@core/adapter-evm-base` where `gasEstimate` * is optional — here it is always present on the calculate-fee result. * * @example * ```typescript * import type { ViemFeeRaw } from '@circle-fin/adapter-viem-v2/next' * * const feeRaw: ViemFeeRaw = { * gasEstimate: 21000n, * maxFeePerGas: 30_000_000_000n, * maxPriorityFeePerGas: 2_000_000_000n, * } * ``` */ interface ViemFeeRaw { /** Gas estimate (same as units, included for convenience). */ readonly gasEstimate: bigint; /** Max fee per gas (EIP-1559 chains). */ readonly maxFeePerGas?: bigint | undefined; /** Max priority fee per gas (EIP-1559 chains). */ readonly maxPriorityFeePerGas?: bigint | undefined; /** Legacy gas price (pre-EIP-1559 chains or fallback). */ readonly gasPrice?: bigint | undefined; } /** * Input for the calculateFee primitive. * * @remarks * Use this when you know the gas units (from historical data, constants, * or a previous estimate) and want to calculate the current fee. */ interface CalculateFeeInput { /** * Gas units for the transaction. * * @remarks * This is the gas limit or estimated gas consumption. */ readonly gasUnits: bigint; /** * Buffer to add to the fee in basis points. * * @remarks * Default is 2 000 (20%). This accounts for gas price fluctuations * between estimation and execution, especially with free/public RPCs. * * Cascade: per-call value → `config.transaction.feePriceBufferBps` → 2 000. * * @example * - 500 = 5% buffer * - 2000 = 20% buffer (default) * - 5000 = 50% buffer */ readonly bufferBasisPoints?: bigint | undefined; } /** * EVM fee estimate type alias. * * @remarks * Uses the universal `FeeEstimate` type with EVM-specific raw data. * The `fee` Amount uses 18 decimals (native token precision for EVM chains). */ type EVMFeeEstimate = FeeEstimate; /** * The calculateFee primitive function type. */ type CalculateFeePrimitive = (input: CalculateFeeInput, operation: OperationMeta, invocation?: AdapterInvocationMeta) => Promise; /** * Creates a calculateFee primitive for the viem adapter. * * @param ctx - The viem adapter context containing client accessors. * @returns A calculateFee primitive function. * * @remarks * This primitive calculates transaction fees for a known gas amount. * It supports both EIP-1559 and legacy fee estimation: * * - **EIP-1559 chains**: Uses `maxFeePerGas` for calculation * - **Legacy chains**: Falls back to `gasPrice` * * The primitive includes an optional buffer (default 20%) to account * for gas price fluctuations between estimation and execution. * * @example * ```typescript * const ctx = createAdapterContext({ ... }) * const calculateFee = createCalculateFee(ctx) * * // Calculate fee for known gas amount * const result = await calculateFee( * { gasUnits: 21000n }, * { chain: Ethereum }, * ) * * // Raw bigint via Amount.raw * console.log(`Fee: ${result.fee.raw} wei`) * console.log(`Unit price: ${result.unitPrice} wei/gas`) * * // Amount for display (uses 18 decimals for native token) * console.log(`Fee: ${result.fee.toString()} ETH`) * console.log(`Fee: ${result.fee.formatted()} ETH`) * ``` * * @example * ```typescript * // Amount provides fluent operations * const result1 = await calculateFee({ gasUnits: 21000n }, { chain: Ethereum }) * const result2 = await calculateFee({ gasUnits: 65000n }, { chain: Ethereum }) * * // Compare fees * if (result1.fee.lt(result2.fee)) { * console.log('First operation is cheaper') * } * * // Sum fees * const total = result1.fee.add(result2.fee) * console.log(`Total: ${total.toString()} ETH`) * ``` * * @example * ```typescript * // Access EIP-1559 data via raw * const result = await calculateFee( * { gasUnits: 65000n }, * { chain: Polygon }, * ) * * if (result.raw.maxFeePerGas) { * console.log('EIP-1559 max fee:', result.raw.maxFeePerGas) * } else { * console.log('Legacy gas price:', result.raw.gasPrice) * } * ``` */ declare function createCalculateFee(ctx: AdapterContext): CalculateFeePrimitive; /** * Create an estimate primitive for calculating EVM transaction fees. * * @param ctx - The Viem adapter context. * @param calculateFee - The calculateFee primitive for fee calculation. * @returns An estimate primitive function. * * @remarks * Combines gas estimation with fee calculation (EIP-1559 + legacy support). * The result includes both universal fields and EVM-specific raw data. * * @example * ```typescript * const calculateFee = createCalculateFee(ctx) * const estimate = createEstimate(ctx, calculateFee) * * const fee = await estimate( * { to: '0x...', from: '0x...', data: '0x...', value: 0n }, * { chain: Ethereum }, * ) * * console.log(`Fee: ${fee.fee}`) * console.log(`Gas: ${fee.raw.gasEstimate}`) * ``` */ declare function createEstimate(ctx: AdapterContext, calculateFee: CalculateFeePrimitive): EstimatePrimitive; /** * Create a simulate primitive for testing EVM transaction execution. * * @param ctx - The Viem adapter context. * @returns A simulate primitive function. * * @remarks * Uses `eth_call` to simulate transaction execution without sending. * This is useful for: * - Checking if a transaction would revert * - Getting the return value of a transaction * - Validating transaction parameters * * @example * ```typescript * const simulate = createSimulate(ctx) * * const result = await simulate( * { to: '0x...', from: '0x...', data: '0x...', value: 0n }, * { chain: Ethereum }, * ) * * if (!result.success) { * console.error(`Would revert: ${result.error}`) * } * ``` */ declare function createSimulate(ctx: AdapterContext): SimulatePrimitive; /** * Execute primitive for sending EVM transactions. * * @packageDocumentation */ /** * EVM-specific execution raw data. */ interface EVMExecuteRaw { /** Transaction receipt (available after waiting). */ readonly receipt?: TransactionReceipt | undefined; } /** * Execute result type. */ type EVMExecuteResult = ExecuteResult; /** * Execute primitive function type. */ type ExecutePrimitive = PrimitiveFunction; /** * Create an execute primitive for sending EVM transactions. * * @param ctx - The Viem adapter context. * @param waitForTransaction - The waitForTransaction primitive for confirmations. * @returns An execute primitive function. * * @remarks * Sends a transaction to the network and returns immediately with the * transaction ID and a `wait()` function for confirmation. * * Supports optional gas and nonce overrides via the `overrides` field. * * **Cancellation semantics (A4):** if `invocation.signal` is already aborted * when `execute` runs, it throws `NETWORK_ABORTED` *before* signing or * broadcasting. Once the transaction is broadcast it is irreversible — * aborting the signal afterwards only stops the `wait()` promise, never the * on-chain transaction. * * @example * ```typescript * const waitForTransaction = createWaitForTransaction(ctx) * const execute = createExecute(ctx, waitForTransaction) * * // Basic execution (gas auto-estimated) * const { txId, wait } = await execute( * { raw: { to: '0x...', from: '0x...', data: '0x...', value: 0n } }, * { chain: Ethereum }, * ) * * // With gas overrides * const { txId, wait } = await execute( * { * raw: { to: '0x...', from: '0x...', data: '0x...', value: 0n }, * overrides: { gas: 100000n, maxFeePerGas: 50_000_000_000n }, * }, * { chain: Ethereum }, * ) * * const confirmation = await wait() * if (confirmation.success) { * console.log(`Confirmed in block ${confirmation.blockId}`) * } * ``` */ declare function createExecute(ctx: AdapterContext, waitForTransaction: WaitForTransactionPrimitive): ExecutePrimitive; /** * Field definition for EIP-712 typed data. * * Each field describes a property in a struct, including its name and Solidity type. * * @example * ```typescript * const field: TypedDataField = { name: "owner", type: "address" } * ``` */ interface TypedDataField { /** Name of the struct field */ name: string; /** Solidity type of the struct field (e.g., "address", "uint256") */ type: string; } /** * Input for the signTypedData primitive. * * @typeParam Types - Mapping of struct names to their field definitions. * @typeParam Message - The message payload type. * * @remarks * Follows the EIP-712 typed data structure for secure off-chain signing. * The signature can be used for permits, meta-transactions, and other * gasless operations. */ interface SignTypedDataInput = Record, Message extends Record = Record> { /** * EIP-712 domain separator. * * @remarks * Identifies the dApp/contract and prevents replay attacks across chains. */ readonly domain: { readonly name?: string | undefined; readonly version?: string | undefined; readonly chainId?: number | undefined; readonly verifyingContract?: `0x${string}` | undefined; readonly salt?: `0x${string}` | undefined; }; /** * Mapping of struct names to their field definitions. * * @remarks * Defines the structure of the message being signed. * Must include all types referenced in the message. */ readonly types: Types; /** * The root struct type being signed. * * @remarks * Must be a key in the `types` mapping. */ readonly primaryType: keyof Types & string; /** * The message payload to be signed. * * @remarks * Must conform to the structure defined in `types[primaryType]`. */ readonly message: Message; } /** * Result of signing typed data. * * @remarks * Returns the full signature as a hex string. Use `parseSignature` from * `@core/adapter-evm` to extract (v, r, s) components if needed. */ type SignTypedDataResult = `0x${string}`; /** * The signTypedData primitive function type. */ type SignTypedDataPrimitive = PrimitiveFunction; /** * Creates a signTypedData primitive for the viem adapter. * * @param ctx - The viem adapter context containing client accessors. * @returns A signTypedData primitive function. * * @remarks * This primitive signs EIP-712 typed data using the connected wallet. * It's commonly used for: * * - **EIP-2612 Permits**: Gasless token approvals * - **Meta-transactions**: Off-chain authorization for on-chain execution * - **Typed signatures**: Any structured data that needs to be signed * * The primitive requires an address in the operation context, which is * used to determine the signer account. * * @example * ```typescript * const ctx = createAdapterContext({ ... }) * const signTypedData = createSignTypedData(ctx) * * // Sign an EIP-2612 permit * const signature = await signTypedData( * { * domain: { * name: 'USD Coin', * version: '2', * chainId: 1, * verifyingContract: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', * }, * types: { * Permit: [ * { name: 'owner', type: 'address' }, * { name: 'spender', type: 'address' }, * { name: 'value', type: 'uint256' }, * { name: 'nonce', type: 'uint256' }, * { name: 'deadline', type: 'uint256' }, * ], * }, * primaryType: 'Permit', * message: { * owner: '0x1234...', * spender: '0x5678...', * value: 1000000n, * nonce: 0n, * deadline: 1700000000n, * }, * }, * { chain: Ethereum, address: '0x1234...' }, * ) * * console.log(signature) // '0x...' (65 bytes) * ``` * * @example * ```typescript * // Parse signature into (v, r, s) components * import { parseSignature } from '@core/adapter-evm' * * const signature = await signTypedData(typedData, { chain, address }) * const { v, r, s } = parseSignature(signature) * * // Use components for contract calls * await contract.permit(owner, spender, value, deadline, v, r, s) * ``` */ declare function createSignTypedData(ctx: AdapterContext): SignTypedDataPrimitive; /** * Prepare primitive for EVM write operations. * * @remarks * Composes standalone primitives (estimate, simulate, execute) into a single * prepare pipeline. Each prepared transaction delegates to the primitives * rather than reimplementing their logic. * * Supports two input modes via the {@link PrepareInput} discriminated union: * - `PrepareContractInput` — provide ABI + function name; calldata is encoded internally. * - `PrepareRawInput` — provide a pre-built {@link RawTransaction}; encoding is skipped. * * @packageDocumentation */ /** * Input for a contract write (state-changing call). * * @remarks * Mirrors viem's `writeContract` parameters with type-safe address format. * The optional `type` discriminator enables the {@link PrepareInput} union. * Omitting `type` (or passing `'contract'`) is equivalent — existing * callers that do not provide `type` continue to work unchanged. * * @example * ```typescript * import type { PrepareContractInput } from '@circle-fin/adapter-viem-v2/next' * import { erc20Abi } from 'viem' * * const input: PrepareContractInput = { * address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', * abi: erc20Abi, * functionName: 'transfer', * args: ['0x...', 1000000n], * value: 0n, * } * ``` */ interface PrepareContractInput { /** * Discriminator for the prepare input union. * * @defaultValue `'contract'` (inferred when omitted) */ readonly type?: 'contract' | undefined; /** The contract address. */ readonly address: `0x${string}`; /** The contract ABI. */ readonly abi: readonly unknown[]; /** The function name to call. */ readonly functionName: string; /** Function arguments. */ readonly args?: readonly unknown[] | undefined; /** Native token value to send with the call (in wei). */ readonly value?: bigint | undefined; } /** * Discriminated union of all EVM prepare input types. * * @remarks * - `PrepareContractInput` — provide ABI + function name + args; the adapter * encodes calldata internally via viem's `encodeFunctionData`. * - `PrepareRawInput` — provide a pre-built {@link RawTransaction}; the * adapter skips encoding and passes it directly to estimate / simulate / execute. * - `PrepareNativeInput` — native-token transfer; emits `sendTransaction` with * no calldata. */ type PrepareInput = PrepareContractInput | PrepareRawInput | PrepareNativeInput; /** * A prepared EVM transaction with properly typed methods. * * @remarks * Each method delegates to the corresponding standalone primitive: * - `estimate()` → EstimatePrimitive * - `simulate()` → SimulatePrimitive * - `execute(overrides?)` → ExecutePrimitive * * @example * ```typescript * import { createPrepare } from '@circle-fin/adapter-viem-v2/next' * import { Ethereum } from '@circle-fin/bridge-kit/chains' * * const prepare = createPrepare({ ctx, estimate, simulate, execute }) * const result = await prepare(input, { chain: Ethereum, address: '0x...' }) * const tx = result.single() * * // Use the prepared transaction methods * const fee = await tx.estimate() * const sim = await tx.simulate() * const exec = await tx.execute({ gas: 100000n }) * ``` */ interface PreparedEVMTransaction extends PreparedTransactionBase { readonly input: PrepareInput; readonly built: RawTransaction; estimate(): Promise; simulate(): Promise; execute(overrides?: GasOverrides): Promise; } /** * The result type returned by the prepare primitive. * * TRaw is `unknown` because TypeScript's conditional type inference cannot * extract it from `execute(overrides?: GasOverrides)`. Individual transaction * `execute()` calls still return fully-typed `EVMExecuteResult`. */ type EVMPrepareResult = PrepareResult; /** * The prepare primitive function type. * * @remarks * Accepts both contract call inputs and pre-built raw transactions * via the {@link PrepareInput} discriminated union. */ type PreparePrimitive = (input: PrepareInput | PrepareInput[], operation: OperationMeta, invocation?: AdapterInvocationMeta) => Promise; /** * Options for creating a prepare primitive. * * @remarks * The prepare primitive composes standalone primitives. Each adapter creates * these primitives from its context and passes them here. */ interface CreatePrepareOptions { /** The adapter context. */ readonly ctx: AdapterContext; /** Estimate primitive for fee calculation. */ readonly estimate: EstimatePrimitive; /** Simulate primitive for dry-run testing. */ readonly simulate: SimulatePrimitive; /** Execute primitive for sending transactions. */ readonly execute: ExecutePrimitive; } /** * Creates a prepare primitive that composes estimate, simulate, and execute. * * @remarks * Accepts both contract call inputs and pre-built raw transactions. * For contract inputs the adapter encodes calldata via `encodeFunctionData`; * for raw inputs the provided {@link RawTransaction} is used as-is. * * @param options - Adapter context and standalone primitives. * @returns A prepare primitive function. * * @example * ```typescript * const prepare = createPrepare({ ctx, estimate, simulate, execute }) * * // Contract call (existing behavior) * const result = await prepare( * { address: '0x...', abi, functionName: 'transfer', args: [...] }, * { chain: Ethereum, address: '0xsender...' }, * ) * * // Raw transaction (new) * const rawResult = await prepare( * { type: 'raw', raw: { to: '0x...', from: '0x...', data: '0x...', value: 0n } }, * { chain: Ethereum, address: '0xsender...' }, * ) * ``` */ declare function createPrepare({ ctx: adapterContext, estimate, simulate, execute, }: CreatePrepareOptions): PreparePrimitive; /** * EIP-5792 batched execution primitive for the `/next` Viem adapter. * * @remarks * Exposes two methods — `supportsAtomicBatch(chain)` and * `batchExecute(calls, chain, options?)` — that together mirror the legacy * `ViemAdapter` public API and can be structurally consumed by providers * (e.g. `@circle-fin/provider-cctp.v2`) that gate batched approve+burn on * these capabilities. * * @packageDocumentation */ /** * Per-call receipt information returned by {@link BatchExecuteAPI.batchExecute}. * * @since 2.0.0 */ interface BatchExecuteReceipt { /** The on-chain transaction hash for this call. */ readonly txHash: string; /** Whether the call succeeded or failed on-chain. */ readonly status: 'success' | 'error'; } /** * Result of a batched EIP-5792 `wallet_sendCalls` execution. * * @remarks * Once `wallet_sendCalls` has been accepted by the wallet the batch has * been submitted — errors during polling are captured as an empty * `receipts` array (with the triggering error stashed on `error`) so * callers never retry a batch that was already sent. * * @since 2.0.0 * * @example * ```typescript * const result: BatchExecuteResult = { * batchId: '0xabc123', * receipts: [ * { txHash: '0x111...', status: 'success' }, * { txHash: '0x222...', status: 'success' }, * ], * } * ``` */ interface BatchExecuteResult { /** The wallet-assigned identifier for the batched call bundle. */ readonly batchId: string; /** Per-call receipt information in the same order as the submitted calls. */ readonly receipts: BatchExecuteReceipt[]; /** * The error that occurred during polling, if any. * * @remarks * Present when `receipts` is empty due to a polling timeout or failure * after the batch was already submitted. Callers can inspect this to * distinguish "wallet doesn't support status polling" (`error` is * `undefined`, `receipts` is `[]`) from "polling timed out" (`error` * is a {@link KitError}). */ readonly error?: unknown; /** * The raw EIP-5792 `statusCode` returned by `wallet_getCallsStatus` * when a terminal status was reached. * * Per the EIP-5792 specification, valid terminal codes are: * - `200` — batch confirmed onchain * - `400` — batch failed offchain (not included onchain) * - `500` — batch reverted completely onchain * - `600` — batch reverted partially onchain * * Absent when polling timed out before a terminal status was reached, * when the wallet does not support status polling, or when the batch * was confirmed via a non-numeric `status` string returned by viem. * * @since 2.0.0 */ readonly statusCode?: number; } /** * Options for {@link BatchExecuteAPI.batchExecute}. * * @since 2.0.0 * * @example * ```typescript * const result = await adapter.batchExecute(calls, Base, { * timeout: 60_000, * pollingInterval: 1_000, * }) * ``` */ interface BatchExecuteOptions { /** * Maximum time in milliseconds to wait for all calls to be confirmed. * @defaultValue 120_000 (2 minutes) */ readonly timeout?: number | undefined; /** * Interval in milliseconds between `wallet_getCallsStatus` polls. * @defaultValue 2_000 (2 seconds) */ readonly pollingInterval?: number | undefined; /** * Whether to require the wallet to execute every call atomically — all * succeed or all revert (EIP-5792 `atomicRequired`, surfaced to viem as * `forceAtomic`). * * @remarks * Defaults to `true`. EIP-5792 `wallet_sendCalls` itself defaults to * NON-atomic execution, so without this the wallet MAY run calls * sequentially with independent failure — e.g. a CCTP approve+burn batch * could leave the approve confirmed and the burn failed. Set to `false` * only when partial execution is acceptable for the flow. * * @defaultValue true */ readonly atomicRequired?: boolean | undefined; /** * The address expected to authorize the batch. * * @remarks * Required when execution is routed through a signing strategy — there is no * wallet client account to read the sender from. Ignored on the wallet-client * (EIP-5792) path, which uses the wallet's own account. * * @since 2.0.0 */ readonly fromAddress?: `0x${string}` | undefined; /** * Cancellation signal forwarded to the signing strategy. * * @remarks * Threaded into the strategy's `SigningContext` on the signing-strategy batch * path so a caller can cancel a pending authorization, matching the * single-transaction `execute` primitive. Ignored on the wallet-client * (EIP-5792) path, which has no interruptible await before submission. * * @since 2.0.0 */ readonly signal?: AbortSignal | undefined; /** Request-scoped intent and lazy semantic review data for this batch. */ readonly authorization?: AuthorizationDescriptor | undefined; } /** * The shape returned by {@link createBatchExecute}. * * @remarks * Spread onto the adapter via `extras` so consumers see top-level * `adapter.supportsAtomicBatch` and `adapter.batchExecute` methods matching * the legacy `ViemAdapter` signatures. */ interface BatchExecuteAPI { /** * Report whether atomic batching is available on the given chain. * * @remarks * When a `SigningStrategy` is configured, the answer comes from the * strategy's `atomicBatch` manifest capability — no wallet is consulted. * Otherwise it detects EIP-5792 support on the connected wallet. * * Expressed as a property so consumers can destructure without tripping * `@typescript-eslint/unbound-method` — the returned functions are * closure-bound and do not reference `this`. */ readonly supportsAtomicBatch: (chain: EVMChainDefinition) => Promise; /** * Submit multiple EVM calls as a single atomic batch. * * @remarks * When a `SigningStrategy` is configured, the batch is authorized through * the strategy (one authorization for all calls) and requires * `options.fromAddress`. Otherwise it is sent to the connected wallet via * EIP-5792 `wallet_sendCalls`, polling `wallet_getCallsStatus` until all * calls are confirmed (or the poll times out). */ readonly batchExecute: (calls: EvmCallData[], chain: EVMChainDefinition, options?: BatchExecuteOptions) => Promise; } /** * Adapter factory: composes all primitives, then builds the adapter. * * @packageDocumentation */ type AdapterOptions = AdapterContextOptions; /** * The clean viem adapter interface (new API). * * @remarks * Derived from {@link AssembledAdapterShape} — the standard adapter shape * returned by `assembleAdapter` — plus viem-specific extras. * * Legacy methods (`prepareAction`, `calculateTransactionFee`, * 3-arg `waitForTransaction`) are injected by `withLegacyCompat` * and are not part of this type. */ type ViemAdapterCore = AssembledAdapterShape, 'evm', GenericPrimitiveFunction, PreparePrimitive, WaitForTransactionPrimitive, EVMActionRegistry> & { /** Calculate fees using the new fee primitive. */ readonly calculateFee: CalculateFeePrimitive; /** Sign EIP-712 typed data. */ readonly signTypedData: SignTypedDataPrimitive; /** * Read deployed bytecode for signer classification. * * @remarks * `chain` stays `EVMChainDefinition` on purpose. This member is not part of * `StandardAdapter`, so nothing forces it to the wider `ChainDefinition` * that `getTokenDecimals` takes, and the narrower type keeps the * compile-time check. Do not widen it for symmetry. * * @param address - EVM account address to inspect. * @param chain - Source chain on which to inspect the address. * @returns Deployed bytecode, or `'0x'` when the address is an EOA. */ readonly readBytecode: (address: string, chain: EVMChainDefinition) => Promise<`0x${string}` | undefined>; /** * Read and validate an ERC-20 token's decimal precision. * * @remarks * Takes the ecosystem-agnostic `ChainDefinition` (not `EVMChainDefinition`) * so this member's signature matches `StandardAdapter.getTokenDecimals` * from `@core/adapter-compat`. `resolveEVMTokenDecimalsFromContract` * validates at runtime that `chain` is actually an EVM chain. * * @param tokenAddress - ERC-20 token contract address. * @param chain - Chain containing the token contract. Must be an EVM chain. * @returns The validated decimal precision. * @throws `KitError` with `INPUT_INVALID_CHAIN` for a non-EVM chain. * @throws `KitError` with `INPUT_INVALID_ADDRESS` for an invalid address. * @throws `KitError` with `RPC_ENDPOINT_ERROR` when the read fails. * @throws `KitError` with `INPUT_VALIDATION_FAILED` when the decoded value is * not a uint8. */ readonly getTokenDecimals: (tokenAddress: string, chain: ChainDefinition) => Promise; /** * Report whether this adapter can produce EIP-712 typed-data signatures. * * @remarks * Implements the `SignTypedDataAdapter` capability contract from * `@core/adapter-evm` (consulted by `canSignTypedData`). When a signing * strategy is configured the answer comes from its manifest — a strategy * that does not declare the `evm-typed-data` payload family reports `false`, * letting callers choose an on-chain alternative instead of a permit the * capability gate would reject. Wallet-backed adapters report `true`. */ readonly supportsSignTypedData: () => boolean; /** * Detect whether the connected wallet supports EIP-5792 atomic batching * on the given chain. */ readonly supportsAtomicBatch: BatchExecuteAPI['supportsAtomicBatch']; /** * Submit multiple EVM calls as a single batched wallet request via * EIP-5792 `wallet_sendCalls`, polling for completion. */ readonly batchExecute: BatchExecuteAPI['batchExecute']; }; /** * The public viem adapter type — clean interface + legacy compat methods. * * @remarks * Consumers get the new API plus deprecated legacy methods for backward * compatibility with older providers/kits. * * @typeParam TCapabilities - The adapter capabilities type. Preserved so * address-context-aware operation metadata can be inferred from * `typeof adapter`. */ type ViemAdapter = LegacyCompatibleAdapter>; /** * Create the adapter from options (context is created internally). * * @typeParam TCapabilities - The adapter capabilities type. * @param options - The adapter configuration options. * @returns A fully assembled viem adapter with legacy compatibility. * * @example * ```typescript * import { createAdapter } from '@circle-fin/adapter-viem-v2/next' * import { createPublicClient, createWalletClient, http, custom } from 'viem' * import { Ethereum, Base } from '@circle-fin/bridge-kit/chains' * * const adapter = createAdapter({ * capabilities: { * addressContext: 'user-controlled', * supportedChains: [Ethereum, Base], * }, * getPublicClient: ({ chain }) => createPublicClient({ chain, transport: http() }), * getWalletClient: ({ chain }) => * createWalletClient({ chain, transport: custom(window.ethereum) }), * }) * ``` */ declare function createAdapter(options: AdapterOptions): ViemAdapter; /** * Create the adapter from an existing context. * * @remarks * Composes all standalone primitives, then assembles the adapter * and wraps it with legacy compatibility methods. * * @param ctx - An initialized adapter context. * @returns A fully assembled viem adapter with legacy compatibility. * * @example * ```typescript * import { createAdapterContext, createAdapterFromContext } from '@circle-fin/adapter-viem-v2/next' * import { Ethereum } from '@circle-fin/bridge-kit/chains' * * const ctx = createAdapterContext({ capabilities, getPublicClient, getWalletClient }) * const adapter = createAdapterFromContext(ctx) * ``` */ declare function createAdapterFromContext(ctx: AdapterContext): ViemAdapter; /** * Adapter action bindings. * * @remarks * The only framework-specific piece is `getNativeBalance`. * Everything else is standardized in `@core/adapter-evm-base`. * * @packageDocumentation */ /** * Minimal shape the action factory needs from the adapter. * * @internal */ interface ActionFactoryDeps { readonly ctx: AdapterContext; readonly read: GenericPrimitiveFunction; readonly prepare: PreparePrimitive; readonly getAddress: (chain: EVMChainDefinition) => Promise; readonly signTypedData: SignTypedDataPrimitive; } /** * Create all EVM actions from adapter primitives. * * @param deps - Adapter context and the primitives needed by actions. * @returns A typed action registry. * * @example * ```typescript * import { createActionsFromAdapter } from '@circle-fin/adapter-viem-v2/next' * import { createAdapterContext } from '@circle-fin/adapter-viem-v2/next' * import { Ethereum } from '@circle-fin/bridge-kit/chains' * * const ctx = createAdapterContext({ capabilities, getPublicClient, getWalletClient }) * const actions = createActionsFromAdapter({ * ctx, * read, * prepare, * getAddress: ctx.getAddress, * }) * ``` */ declare function createActionsFromAdapter(deps: ActionFactoryDeps): EVMActionRegistry; /** * Overloaded action dispatch signature. * * @remarks * - `action(key)` returns the typed action object (for `.prepare()`, `.name`, etc.) * - `action(key, ...args)` calls the action directly (fast path) */ interface ActionDispatch { (key: K): EVMActionRegistry[K]; (key: K, ...args: Parameters): ReturnType; } /** * Create a type-safe action dispatch function from an action registry. * * @param actions - The EVM action registry. * @returns An overloaded dispatch function. * * @example * ```typescript * import { createActionDispatch, createActionsFromAdapter } from '@circle-fin/adapter-viem-v2/next' * import { Ethereum } from '@circle-fin/bridge-kit/chains' * * const actions = createActionsFromAdapter(deps) * const dispatch = createActionDispatch(actions) * * // Get action object * const balanceOf = dispatch('native.balanceOf') * console.log(balanceOf.name) // 'native.balanceOf' * * // Call action directly * const result = await dispatch('native.balanceOf', { walletAddress: '0x...' }, { chain: Ethereum }) * ``` */ declare function createActionDispatch(actions: EVMActionRegistry): ActionDispatch; /** * Shared base parameters for all adapter factories. * * @remarks * Every adapter factory accepts these fields. Factory-specific parameters * (e.g., `privateKey`, `provider`) are added by extending this interface. * * @example * ```typescript * import { createPublicClient, http } from 'viem' * import { mainnet } from 'viem/chains' * import type { BaseAdapterParams } from '@circle-fin/adapter-viem-v2/next' * * const params: BaseAdapterParams = { * getPublicClient: ({ chain }) => createPublicClient({ chain, transport: http() }), * capabilities: { addressContext: 'user-controlled' }, * } * ``` */ interface BaseAdapterParams extends EvmAuthorizationOptions { /** Optional custom public client factory. Defaults to HTTP transport with vetted RPC endpoints. */ getPublicClient?: (params: { chain: Chain; }) => Promise | PublicClient; /** Partial capabilities (defaults to user-controlled, all EVM chains). */ capabilities?: Partial; /** Runtime services or configuration options (logger, metrics). */ runtime?: Runtime | RuntimeOptions; /** Token registry for resolving token selectors. */ tokens?: TokenRegistry; /** Operational config (retry policy, etc.). */ config?: OperationalConfig; /** * Chain-switch policy for write operations. * * @remarks * Forwarded to `createAdapterContext` which installs the `ensureChain` * hook. Each factory applies its own sensible default when this field * is omitted (`prompt` for browser/provider flows, `skip` for * private-key/server flows). */ chainSwitch?: ChainSwitchConfig; } /** * Factory for creating the new Viem adapter from a private key. * * @packageDocumentation */ /** * Parameters for creating a Viem adapter from a private key. * * @example * ```typescript * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2/next' * * const adapter = createViemAdapterFromPrivateKey({ * privateKey: '0xabc...def', * }) * * // With custom RPC endpoints * const adapter = createViemAdapterFromPrivateKey({ * privateKey: '0xabc...def', * getPublicClient: ({ chain }) => createPublicClient({ chain, transport: http('https://my-rpc.com') }), * getWalletClient: ({ chain, account }) => createWalletClient({ chain, account, transport: http('https://my-rpc.com') }), * }) * ``` */ interface CreateViemAdapterFromPrivateKeyParams extends BaseAdapterParams { /** Private key (hex string, with or without `0x` prefix). */ privateKey: string; /** * Optional custom wallet client factory. * Receives the derived account so it can be attached to the client. * Defaults to HTTP transport with the derived account. */ getWalletClient?: (params: { chain: Chain; account: PrivateKeyAccount; }) => Promise | WalletClient; } /** * Create a new Viem adapter from a private key. * * @remarks * The derived account is used for signing and address resolution. * Private key adapters are always `user-controlled` because the address * is deterministically derived from the key. * * @param params - Private key and optional overrides. * @returns A configured `ViemAdapter`. * @throws KitError If the private key is invalid or capabilities are incompatible. * * @example * ```typescript * import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2/next' * * const adapter = createViemAdapterFromPrivateKey({ * privateKey: '0xabc...def', * }) * ``` */ declare function createViemAdapterFromPrivateKey(params: CreateViemAdapterFromPrivateKeyParams): ViemAdapter; /** * Factory for creating the new Viem adapter from an EIP-1193 provider. * * @packageDocumentation */ /** * Parameters for creating a Viem adapter from an EIP-1193 provider. * * @example * ```typescript * import { createViemAdapterFromProvider } from '@circle-fin/adapter-viem-v2/next' * * // Browser wallet (MetaMask, WalletConnect, etc.) * const adapter = createViemAdapterFromProvider({ * provider: window.ethereum, * }) * * // Developer-controlled (server-side JSON-RPC) * const adapter = createViemAdapterFromProvider({ * provider: myJsonRpcProvider, * capabilities: { addressContext: 'developer-controlled' }, * }) * ``` */ interface CreateViemAdapterFromProviderParams extends BaseAdapterParams { /** EIP-1193 compatible provider (e.g., `window.ethereum`). */ provider: EIP1193Provider; } /** * Create a new Viem adapter from an EIP-1193 provider. * * @remarks * **Account resolution behaviour:** * * - `user-controlled` (default): The account is lazily resolved on the * first `getWalletClient` call by calling `requestAddresses()` on the * provider. The resolved account is cached for subsequent calls. * * - `developer-controlled`: No account resolution is performed. The * provider is used as a signing transport only — the address for each * operation is provided explicitly via the operation context. This * avoids unnecessary `requestAddresses()` calls on server-side * JSON-RPC providers that may not support them. * * @param params - Provider and optional overrides. * @returns A configured `ViemAdapter`. * @throws KitError If provider validation fails. * * @example * ```typescript * import { createViemAdapterFromProvider } from '@circle-fin/adapter-viem-v2/next' * * const adapter = createViemAdapterFromProvider({ * provider: window.ethereum, * }) * ``` */ declare function createViemAdapterFromProvider(params: CreateViemAdapterFromProviderParams): ViemAdapter; export { createActionDispatch, createActionsFromAdapter, createAuthorizationRejectedError, createCalculateFee, createEVMActions, createEstimate, createExecute, createPrepare, createRead, createRuntime, createSignTypedData, createSigningRejectedError, createSimulate, createTokenRegistry, createAdapter as createViemAdapter, createAdapterContext as createViemAdapterContext, createAdapterFromContext as createViemAdapterFromContext, createViemAdapterFromPrivateKey, createViemAdapterFromProvider, createWaitForTransaction, externalSigning, hexAddress, hexBytes32, hexString, isSigningRejected, resolveViemChain }; export type { ActionDispatch, AdapterCapabilities$1 as AdapterCapabilities, AddressField$1 as AddressField, AuthorizationDecision, AuthorizationDescriptor, AuthorizationRequest, AuthorizationReview, BaseAdapterParams, BroadcastedSigningResult, CalculateFeeInput, CalculateFeePrimitive, CapabilityAwareOperationMeta, ChainDefinition, CreateViemAdapterFromPrivateKeyParams, CreateViemAdapterFromProviderParams, EVMActionRegistry, EVMChainDefinition, EVMExecuteResult, EstimatePrimitive, EstimateResult, EvmApprovalContext, EvmAuthorizationOptions, EvmAuthorizationPayload, EvmCall, EvmCallsPayload, EvmTypedData, EvmTypedDataDomain, EvmTypedDataField, EvmTypedDataPayload, ExecutePrimitive, ExternalBroadcastOutcome, ExternalSignedOutcome, ExternalSigningOptions, ExternalSigningOutcome, ExternalTypedDataOutcome, ExtractAddressContext$1 as ExtractAddressContext, GasOverrides, OnBeforeAuthorize, OperationalConfig, PrepareContractInput, PreparePrimitive, PreparedEVMTransaction, RawTransaction, ReadContractInput, ReadPrimitive, RetryOptions, Runtime, RuntimeOptions, SignTypedDataInput, SignTypedDataPrimitive, SignTypedDataResult, SignatureSigningResult, SignedSigningResult, SigningContext, SigningIntent, SigningPayloadFamily, SigningResult, SigningStrategy, SigningStrategyManifest, SimulatePrimitive, SimulateResult, TokenDefinition, TokenRegistry, TokenRegistryOptions, TransactionConfig, TransactionPayload, ViemAdapter, AdapterContext as ViemAdapterContext, AdapterContextOptions as ViemAdapterContextOptions, ViemAdapterCore, WaitForTransactionInput, WaitForTransactionPrimitive, WaitForTransactionResult };