@peachprojects/aggregator-sdk 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.d.ts +282 -21
- package/dist/index.mjs +880 -525
- package/package.json +3 -2
- package/dist/index.cjs.map +0 -1
- package/dist/index.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peachprojects/aggregator-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "TypeScript SDK for DEX Aggregator on BSC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
|
-
"dist"
|
|
17
|
+
"dist",
|
|
18
|
+
"!dist/*.map"
|
|
18
19
|
],
|
|
19
20
|
"publishConfig": {
|
|
20
21
|
"access": "public"
|
package/dist/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/types/index.ts","../src/clients/ApiClient.ts","../src/utils/wallet.ts","../src/clients/PeachClient.ts","../src/clients/RouteDiscovery.ts","../src/builders/SwapBuilder.ts"],"sourcesContent":["/**\n * Peach Aggregator SDK Types\n *\n * Simplified design: only supports linear execution + topological sort + pool merging\n */\n\n// ============ Constants ============\n\nexport const DEFAULT_API_URL = \"https://api.peach.ag\";\nexport const DEFAULT_SLIPPAGE_BPS = 50; // 0.5%\nexport const BPS_DENOMINATOR = 10000n;\nexport const DEFAULT_DEADLINE_SECONDS = 1200; // 20 minutes\nexport const DEFAULT_EXECUTE_TIMEOUT_MS = 60_000; // 60 seconds\nexport const DEFAULT_TRANSACTION_RESPONSE_POLL_INTERVALS_MS = [50, 100, 200, 400, 800, 1200] as const;\n\n/**\n * Sentinel address indicating native token (e.g. BNB on BSC).\n * Pass this as srcToken/dstToken to distinguish native BNB from WBNB ERC20.\n */\nexport const NATIVE_TOKEN_ADDRESS = \"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE\";\n\n/**\n * Check if an address is the native token sentinel address.\n */\nexport function isNativeTokenAddress(address: string): boolean {\n return address.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase();\n}\n\n// ============ Error Codes ============\n\n/** API returned route paths where all paths have zero amounts */\nexport const ERR_ZERO_AMOUNT_PATHS = 4001;\n\n// ============ Protocol Types ============\n\nexport enum ProtocolType {\n PancakeV2 = \"PancakeV2\",\n PancakeV3 = \"PancakeV3\",\n PancakeInfinityCl = \"Pancake_Infinity_Cl\",\n UniswapV3 = \"UniswapV3\",\n UniswapV4 = \"UniswapV4\",\n Dodo = \"Dodo\",\n Thena = \"Thena\",\n}\n\n// ============ Configuration ============\n\nexport interface AdapterConfig {\n protocol: ProtocolType;\n address: string;\n}\n\nexport interface PeachConfig {\n chainId: number;\n rpcUrl: string;\n /** Router address override. If not provided, uses the address from API response. */\n routerAddress?: string;\n weth: string;\n adapters: AdapterConfig[];\n}\n\n// ============ Pool Info ============\n\nexport interface PoolInfo {\n address: string;\n token0: string;\n token1: string;\n protocol: ProtocolType;\n // V2 specific\n reserve0?: bigint;\n reserve1?: bigint;\n // V3 specific\n fee?: number;\n liquidity?: bigint;\n sqrtPriceX96?: bigint;\n tick?: number;\n}\n\n// ============ Routes ============\n\nexport interface RouteStep {\n pool: PoolInfo;\n tokenIn: string;\n tokenOut: string;\n amountIn: bigint;\n amountOut: bigint;\n}\n\nexport interface Route {\n steps: RouteStep[];\n amountIn: bigint;\n amountOut: bigint;\n gasEstimate: bigint;\n}\n\nexport interface SplitRoute {\n routes: Route[];\n percentages: number[]; // Percentage for each route (BPS, sum = 10000)\n totalAmountIn: bigint;\n totalAmountOut: bigint;\n totalGasEstimate: bigint;\n}\n\n// ============ Swap Parameters (Contract Format) ============\n\nexport interface SwapStep {\n adapter: string;\n pool: string;\n tokenIn: string;\n tokenOut: string;\n amountIn: bigint; // 0 means consume all\n extraData: string;\n}\n\nexport interface SwapParams {\n srcToken: string;\n dstToken: string;\n amountIn: bigint;\n amountOutMin: bigint;\n steps: SwapStep[];\n intermediateTokens: string[];\n deadline: bigint;\n quoteId: string;\n expectAmountOut: bigint;\n}\n\n// ============ Quote ============\n\nexport interface Quote {\n srcToken: string;\n dstToken: string;\n amountIn: bigint;\n amountOut: bigint;\n priceImpact: number;\n route: SplitRoute;\n params: SwapParams;\n gasEstimate: bigint;\n /** Router contract address from quote API (contracts.router). When set, simulate/execute use this instead of config.routerAddress. */\n routerAddress?: string;\n /** True when the original srcToken was the native token sentinel address */\n srcNative?: boolean;\n /** True when the original dstToken was the native token sentinel address */\n dstNative?: boolean;\n}\n\n// ============ Swap Result ============\n\nexport interface SwapResult {\n txHash: string;\n amountIn: bigint;\n amountOut: bigint;\n gasUsed: bigint;\n}\n\n// ============ BSC Mainnet Preset Config ============\n\nexport const BSC_MAINNET_CONFIG: PeachConfig = {\n chainId: 56,\n rpcUrl: \"https://bsc-dataseed.binance.org\",\n weth: \"0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c\", // WBNB\n adapters: [],\n};\n\n// ============ BSC Testnet Preset Config ============\n\nexport const BSC_TESTNET_CONFIG: PeachConfig = {\n chainId: 97,\n rpcUrl: \"https://bsc-testnet-rpc.publicnode.com\",\n weth: \"0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd\", // WBNB Testnet\n adapters: [],\n};\n\n// ============ API Request Types ============\n\n/**\n * Known DEX providers supported by the SDK.\n */\nexport type KnownProvider =\n | \"PANCAKEV2\"\n | \"PANCAKEV3\"\n | \"PANCAKE_INFINITY_CL\"\n | \"UNISWAPV3\"\n | \"UNISWAPV4\"\n | \"DODO\"\n | \"THENA\";\n\n/**\n * Supported DEX providers.\n * Use getAvailableProviders() to get the list dynamically.\n */\nexport type Provider = KnownProvider | (string & {});\n\n/**\n * API request parameters for find_routes\n * Note: The following parameters are ignored by SDK:\n * - liquidity_change\n * - apikey\n * - gas\n * - with_sign\n * - cal_path_limit\n */\nexport interface ApiFindRouteRequest {\n /** Input token address (required) */\n from: string;\n /** Output token address (required) */\n target: string;\n /** Trade amount, cannot be 0 (required) */\n amount: string;\n /** true: calculate output from input; false: calculate input from output (default: true) */\n by_amount_in?: boolean;\n /** Route search depth / max hops (default: 3) */\n depth?: number;\n /** Trade split count for large trades optimization (default: 20) */\n split_count?: number;\n /** DEX providers, comma-separated (default: \"PANCAKEV2,PANCAKEV3\") */\n providers?: string;\n /** Client version (required >= 1001500 for V3) */\n v?: number;\n}\n\n/**\n * SDK-level quote request options\n */\nexport interface QuoteOptions {\n /** true: calculate output from input; false: calculate input from output (default: true) */\n byAmountIn?: boolean;\n /** Route search depth / max hops (default: 3) */\n depth?: number;\n /** Trade split count for large trades optimization (default: 20) */\n splitCount?: number;\n /** DEX providers to use (default: [\"PANCAKEV2\", \"PANCAKEV3\"]) */\n providers?: Provider[];\n /** Transaction deadline in seconds from now (default: 1200 = 20 min) */\n deadlineSeconds?: number;\n}\n\n// ============ Execute Options ============\n\nexport type ExecuteTimeoutStage = \"wallet_send\" | \"provider_index\";\n\nexport class ExecuteTimeoutError extends Error {\n readonly txHash?: string;\n readonly stage: ExecuteTimeoutStage;\n\n constructor(message: string, stage: ExecuteTimeoutStage, txHash?: string) {\n super(message);\n this.name = \"ExecuteTimeoutError\";\n this.stage = stage;\n this.txHash = txHash;\n }\n}\n\nexport interface SwapOptions {\n /** Slippage tolerance in basis points (e.g. 50 = 0.5%). Required. */\n slippageBps: number;\n /** Gas price in wei. Applied to the swap tx and, if present, the approval tx. */\n gasPrice?: bigint;\n /** Gas limit for the swap tx. */\n gasLimit?: bigint;\n}\n\nexport interface SwapTxRequest {\n to: string;\n data: string;\n value: bigint;\n gasPrice?: bigint;\n gasLimit?: bigint;\n}\n\nexport interface SwapApprovalRequest {\n token: string;\n owner: string;\n spender: string;\n currentAllowance: bigint;\n requiredAmount: bigint;\n approveAmount: bigint;\n tx: SwapTxRequest;\n}\n\nexport interface SwapRequest {\n routerAddress: string;\n method: \"swap\" | \"swapETH\";\n tx: SwapTxRequest;\n approval?: SwapApprovalRequest;\n}\n\nexport interface ExecuteOptions extends SwapOptions {\n /** Skip the preflight eth_call simulation before sending (default: false).\n * Set to true only if you've already called simulate() and confirmed the route is valid. */\n skipPreflight?: boolean;\n /** Timeout in milliseconds for the wallet to sign & broadcast the transaction.\n * If the wallet does not settle the Promise within this duration, execute() rejects\n * with a timeout error so the caller is not stuck on \"Pending Wallet Signature\" forever.\n * Default: 60_000 (60 seconds). Set to 0 to disable. */\n timeoutMs?: number;\n /** Polling intervals for getTransaction(hash) after the wallet returns a tx hash.\n * Defaults to [50, 100, 200, 400, 800, 1200]ms and then repeats the last value. */\n transactionResponsePollingIntervalsMs?: readonly number[];\n}\n\n/** Result of findFailingStep: which step index and step caused the revert */\nexport interface FindFailingStepResult {\n stepIndex: number;\n step: SwapStep;\n error: unknown;\n /** Revert reason when simulating only up to this step (may differ from full route) */\n revertMessage?: string;\n /** Revert reason from the full-route simulation, if fullRouteError was passed */\n fullRouteRevertMessage?: string;\n}\n\n/**\n * Default values for API parameters\n */\nexport const API_DEFAULTS = {\n /** Default route search depth */\n depth: 3,\n /** Default trade split count */\n splitCount: 20,\n /** Default DEX providers */\n providers: [\"PANCAKEV2\", \"PANCAKEV3\", \"PANCAKE_INFINITY_CL\", \"UNISWAPV3\", \"UNISWAPV4\", \"DODO\", \"THENA\"] as Provider[],\n /** Default client version for V3 API */\n clientVersion: 1001500,\n} as const;\n\n// ============ API Response Types ============\n\n/**\n * Aggregator API response wrapper\n */\nexport interface ApiResponse<T> {\n code: number;\n msg: string;\n data: T;\n}\n\n/**\n * Chainflow sync status for a provider\n */\nexport interface ChainflowStatus {\n /** Provider name (e.g., \"PANCAKEV3\") */\n provider: string;\n /** Current sync transaction cursor (tx hash) */\n tx_cursor: string | null;\n /** Sync version info */\n version: {\n /** Latest synced block number */\n latest_block_number: number;\n /** Latest synced transaction index in that block */\n latest_transaction_index: number;\n };\n /** Last update timestamp in milliseconds */\n update_at: number;\n}\n\n/**\n * Status API response data\n */\nexport interface ApiStatusData {\n /** Available liquidity providers */\n providers: string[];\n /** Chain sync status for each provider */\n chainflows: ChainflowStatus[];\n}\n\n/**\n * Full status response\n */\nexport type ApiStatusResponse = ApiResponse<ApiStatusData>;\n\n/**\n * Contract addresses for EVM\n */\nexport interface ApiContractAddresses {\n /** PeachAggregator router address */\n router: string;\n /** Adapter addresses by provider name */\n adapters: Record<string, string>;\n}\n\n/**\n * Route path from aggregator API (EVM format)\n */\nexport interface ApiRoutePath {\n /** Pool contract address */\n pool: string;\n /** Provider name (e.g., \"PANCAKEV3\") */\n provider: Provider;\n /** Adapter contract address */\n adapter: string;\n /** Input token address */\n token_in: string;\n /** Output token address */\n token_out: string;\n /** Swap direction (true = token0 -> token1) */\n direction: boolean;\n /** Fee rate (e.g., \"0.0005\" for 0.05%) */\n fee_rate: string;\n /** Input amount (string to support u128) */\n amount_in: string;\n /** Output amount (string to support u128) */\n amount_out: string;\n /** Extra data for adapter (hex encoded) */\n extra_data?: string;\n}\n\n/**\n * Find route response data\n */\nexport interface ApiFindRouteData {\n request_id: string;\n /** Total input amount (string to support u128) */\n amount_in: string;\n /** Total output amount (string to support u128) */\n amount_out: string;\n deviation_ratio: string;\n paths: ApiRoutePath[];\n /** Contract addresses for building transactions */\n contracts: ApiContractAddresses;\n /** Estimated gas */\n gas: number;\n}\n\n/**\n * Full find route response\n */\nexport type ApiFindRouteResponse = ApiResponse<ApiFindRouteData>;\n","/**\n * ApiClient - Peach Aggregator API client\n *\n * Route discovery and status access via the aggregator API.\n */\n\nimport {\n ApiFindRouteResponse,\n ApiFindRouteData,\n ApiStatusResponse,\n ApiStatusData,\n API_DEFAULTS,\n DEFAULT_API_URL,\n Provider,\n} from \"../types\";\n\nexport interface ApiClientConfig {\n /** API base URL (default: https://api.peach.ag) */\n baseUrl?: string;\n /** Request timeout in ms (default: 10000) */\n timeout?: number;\n}\n\nconst DEFAULT_TIMEOUT = 10000;\n\nexport class ApiClient {\n private baseUrl: string;\n private timeout: number;\n\n constructor(config: ApiClientConfig = {}) {\n this.baseUrl = config.baseUrl || DEFAULT_API_URL;\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n }\n\n /**\n * Find optimal routes via API\n */\n async findRoutes(params: {\n from: string;\n target: string;\n amount: bigint;\n byAmountIn?: boolean;\n depth?: number;\n splitCount?: number;\n providers?: Provider[];\n }): Promise<ApiFindRouteData> {\n const {\n from,\n target,\n amount,\n byAmountIn = true,\n depth = API_DEFAULTS.depth,\n splitCount = API_DEFAULTS.splitCount,\n providers = API_DEFAULTS.providers,\n } = params;\n\n const queryParams = new URLSearchParams({\n from,\n target,\n amount: amount.toString(),\n by_amount_in: byAmountIn.toString(),\n depth: depth.toString(),\n split_count: splitCount.toString(),\n providers: providers.join(\",\"),\n v: API_DEFAULTS.clientVersion.toString(),\n });\n\n const url = `${this.baseUrl}/router/find_routes?${queryParams}`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n },\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new ApiError(\n `API request failed: ${response.status} ${response.statusText}`,\n response.status\n );\n }\n\n const json = (await response.json()) as ApiFindRouteResponse;\n\n if (json.code !== 200) {\n throw new ApiError(json.msg || \"Route not found\", json.code);\n }\n\n if (!json.data || !json.data.paths || json.data.paths.length === 0) {\n throw new ApiError(\"No routes found\", 404);\n }\n\n return json.data;\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new ApiError(\"API request timeout\", 408);\n }\n throw new ApiError(`API request failed: ${error.message}`, 0);\n }\n\n throw new ApiError(\"Unknown API error\", 0);\n }\n }\n\n /**\n * Get service status including available providers\n */\n async getStatus(): Promise<ApiStatusData> {\n const url = `${this.baseUrl}/router/status`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n },\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new ApiError(\n `API request failed: ${response.status} ${response.statusText}`,\n response.status\n );\n }\n\n const json = (await response.json()) as ApiStatusResponse;\n\n if (json.code !== 200) {\n throw new ApiError(json.msg || \"Failed to get status\", json.code);\n }\n\n return json.data;\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new ApiError(\"API request timeout\", 408);\n }\n throw new ApiError(`API request failed: ${error.message}`, 0);\n }\n\n throw new ApiError(\"Unknown API error\", 0);\n }\n }\n\n /**\n * Get list of available providers\n */\n async getAvailableProviders(): Promise<string[]> {\n const status = await this.getStatus();\n return status.providers;\n }\n\n /**\n * Update API base URL\n */\n setBaseUrl(url: string): void {\n this.baseUrl = url;\n }\n\n /**\n * Get current API base URL\n */\n getBaseUrl(): string {\n return this.baseUrl;\n }\n}\n\n/**\n * API error class\n */\nexport class ApiError extends Error {\n public readonly code: number;\n\n constructor(message: string, code: number) {\n super(message);\n this.name = \"ApiError\";\n this.code = code;\n }\n}\n","import { DEFAULT_EXECUTE_TIMEOUT_MS, ExecuteTimeoutError } from \"../types\";\n\nexport async function withWalletSendTimeout<T>(\n promise: Promise<T>,\n timeoutMs = DEFAULT_EXECUTE_TIMEOUT_MS\n): Promise<T> {\n if (timeoutMs <= 0) {\n return promise;\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(\n new ExecuteTimeoutError(\n `Wallet did not settle sendTransaction within ${timeoutMs}ms.`,\n \"wallet_send\"\n )\n );\n }, timeoutMs);\n }),\n ]);\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n}\n","/**\n * PeachClient - Peach Aggregator SDK main entry point\n *\n * Uses the aggregator API for route discovery.\n */\n\nimport { ethers } from \"ethers\";\nimport {\n PeachConfig,\n Quote,\n SwapParams,\n SwapStep,\n SplitRoute,\n ProtocolType,\n BPS_DENOMINATOR,\n DEFAULT_DEADLINE_SECONDS,\n DEFAULT_EXECUTE_TIMEOUT_MS,\n DEFAULT_TRANSACTION_RESPONSE_POLL_INTERVALS_MS,\n QuoteOptions,\n SwapOptions,\n SwapApprovalRequest,\n SwapRequest,\n SwapTxRequest,\n ExecuteTimeoutError,\n ExecuteOptions,\n API_DEFAULTS,\n ApiFindRouteData,\n ApiRoutePath,\n KnownProvider,\n Provider,\n FindFailingStepResult,\n isNativeTokenAddress,\n} from \"../types\";\nimport { ApiClient, ApiClientConfig, ApiError } from \"./ApiClient\";\nimport { withWalletSendTimeout } from \"../utils/wallet\";\n\n// PeachRouter ABI (simplified)\nconst PEACH_ROUTER_ABI = [\n \"function swap((address srcToken, address dstToken, uint256 amountIn, uint256 amountOutMin, (address adapter, address pool, address tokenIn, address tokenOut, uint256 amountIn, bytes extraData)[] steps, address[] intermediateTokens, uint256 deadline, bytes32 quoteId, uint256 expectAmountOut) params) external returns (uint256 amountOut)\",\n \"function swapETH((address srcToken, address dstToken, uint256 amountIn, uint256 amountOutMin, (address adapter, address pool, address tokenIn, address tokenOut, uint256 amountIn, bytes extraData)[] steps, address[] intermediateTokens, uint256 deadline, bytes32 quoteId, uint256 expectAmountOut) params) external payable returns (uint256 amountOut)\",\n \"function isAdapterRegistered(address adapter) external view returns (bool)\",\n \"function WETH() external view returns (address)\",\n];\n\nconst ERC20_ABI = [\n \"function approve(address spender, uint256 amount) external returns (bool)\",\n \"function allowance(address owner, address spender) external view returns (uint256)\",\n \"function balanceOf(address account) external view returns (uint256)\",\n \"function decimals() external view returns (uint8)\",\n \"function symbol() external view returns (string)\",\n];\n\ntype EncodedSwapStep = {\n adapter: string;\n pool: string;\n tokenIn: string;\n tokenOut: string;\n amountIn: bigint;\n extraData: string;\n};\n\ntype EncodedSwapParams = {\n srcToken: string;\n dstToken: string;\n amountIn: bigint;\n amountOutMin: bigint;\n steps: EncodedSwapStep[];\n intermediateTokens: string[];\n deadline: bigint;\n quoteId: string;\n expectAmountOut: bigint;\n};\n\n/** BSC CLPoolManager contract address for Pancake Infinity (V4). */\nconst PANCAKE_INFINITY_CL_POOL_MANAGER = \"0xa0FfB9c1CE1Fe56963B0321B32E7A0302114058b\";\n\nconst PROVIDER_TO_PROTOCOL: Record<KnownProvider, ProtocolType> = {\n PANCAKEV3: ProtocolType.PancakeV3,\n PANCAKEV2: ProtocolType.PancakeV2,\n PANCAKE_INFINITY_CL: ProtocolType.PancakeInfinityCl,\n UNISWAPV3: ProtocolType.UniswapV3,\n UNISWAPV4: ProtocolType.UniswapV4,\n DODO: ProtocolType.Dodo,\n THENA: ProtocolType.Thena,\n};\n\nexport interface PeachClientOptions {\n /** API client configuration. Required for getQuote(). */\n api?: ApiClientConfig;\n}\n\nexport class PeachClient {\n private provider: ethers.Provider;\n private config: PeachConfig;\n private routerContract: ethers.Contract;\n private apiClient: ApiClient;\n\n constructor(\n config: PeachConfig,\n provider?: ethers.Provider,\n options?: PeachClientOptions\n ) {\n this.config = config;\n this.provider = provider || new ethers.JsonRpcProvider(config.rpcUrl);\n // routerContract is used for ABI encoding/decoding; address may be overridden per-quote\n this.routerContract = new ethers.Contract(\n config.routerAddress || ethers.ZeroAddress,\n PEACH_ROUTER_ABI,\n this.provider\n );\n\n // Create API client (uses default API URL if no baseUrl provided)\n this.apiClient = new ApiClient(options?.api);\n }\n\n /**\n * Get the effective router address for a quote.\n */\n private getRouterAddress(quote: Quote): string {\n const addr = quote.routerAddress || this.config.routerAddress;\n if (!addr || addr === ethers.ZeroAddress) {\n throw new Error(\"No router address available. Provide routerAddress in config or use API-based getQuote.\");\n }\n return addr;\n }\n\n /**\n * Apply slippage to swap params, returning a new SwapParams with adjusted amountOutMin\n */\n private applySlippage(params: SwapParams, slippageBps: number): SwapParams {\n if (slippageBps < 0 || slippageBps > 10000) {\n throw new Error(\"slippageBps must be between 0 and 10000\");\n }\n const amountOutMin =\n (params.amountOutMin * (BPS_DENOMINATOR - BigInt(slippageBps))) / BPS_DENOMINATOR;\n return { ...params, amountOutMin };\n }\n\n /**\n * Build transaction requests for an approval (if needed) and the swap itself.\n */\n async swap(\n quote: Quote,\n ownerAddress: string,\n options: SwapOptions\n ): Promise<SwapRequest> {\n const routerAddress = this.getRouterAddress(quote);\n const { tx, method } = this.buildSwapTransactionRequest(quote, options);\n\n let approval: SwapApprovalRequest | undefined;\n if (!quote.srcNative) {\n approval = await this.buildApprovalRequest(\n quote.srcToken,\n ownerAddress,\n quote.amountIn,\n routerAddress,\n options\n );\n }\n\n return {\n routerAddress,\n method,\n tx,\n approval,\n };\n }\n\n /**\n * Execute swap using the legacy signer-managed flow.\n *\n * @deprecated Prefer swap(), then send the returned tx request with your wallet/client.\n */\n async execute(\n quote: Quote,\n signer: ethers.Signer,\n options: ExecuteOptions\n ): Promise<ethers.TransactionResponse> {\n const signerAddress = await signer.getAddress();\n const prepared = await this.swap(quote, signerAddress, options);\n\n // Preflight: run eth_call before sending to surface clear revert reasons instead of\n // the opaque \"cannot estimate gas\" error that ethers throws when estimateGas fails.\n // if (!options.skipPreflight) {\n // await this.simulate(quote, options.slippageBps, signerAddress);\n // }\n\n try {\n if (prepared.approval) {\n const approvalTx = await this.sendTransactionWithTimeout(\n signer,\n prepared.approval.tx,\n options\n );\n await approvalTx.wait();\n }\n\n return await this.sendTransactionWithTimeout(signer, prepared.tx, options);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n const isEstimateGas = /estimateGas/i.test(msg);\n const isMissingRevertData =\n /missing revert data/i.test(msg) ||\n (msg.includes(\"reason=null\") && msg.includes(\"data=null\"));\n if (isEstimateGas && (isMissingRevertData || /reason=null|data=null/.test(msg))) {\n const friendly =\n \"Transaction reverted during gas estimation and the RPC did not return a revert reason. Try: 1) Get a fresh quote and confirm immediately 2) Switch network or RPC 3) Increase slippage.\";\n const e = new Error(`${friendly} (estimateGas/missing revert data)`) as Error & { cause?: unknown };\n e.cause = err;\n throw e;\n }\n throw err;\n }\n }\n\n /**\n * Encode parameters to contract format\n */\n private encodeParams(params: SwapParams): EncodedSwapParams {\n return {\n srcToken: params.srcToken,\n dstToken: params.dstToken,\n amountIn: params.amountIn,\n amountOutMin: params.amountOutMin,\n steps: params.steps.map((step) => ({\n adapter: step.adapter,\n pool: step.pool,\n tokenIn: step.tokenIn,\n tokenOut: step.tokenOut,\n amountIn: step.amountIn,\n extraData: step.extraData,\n })),\n intermediateTokens: params.intermediateTokens,\n deadline: params.deadline,\n quoteId: params.quoteId,\n expectAmountOut: params.expectAmountOut,\n };\n }\n\n private getProtocolForProvider(provider: Provider): ProtocolType {\n if (provider in PROVIDER_TO_PROTOCOL) {\n return PROVIDER_TO_PROTOCOL[provider as KnownProvider];\n }\n throw new Error(`Unsupported provider: ${provider}`);\n }\n\n /**\n * Encode swap calldata for the Peach Aggregator contract\n * Useful for simulation or building custom transactions\n *\n * @param quote - Quote object from getQuote\n * @param slippageBps - Slippage tolerance in basis points (e.g. 50 = 0.5%). Required.\n * @returns Encoded calldata and transaction info (to address, value)\n */\n encodeSwapCalldata(quote: Quote, slippageBps: number): {\n to: string;\n data: string;\n value: bigint;\n method: 'swap' | 'swapETH';\n } {\n const routerAddress = quote.routerAddress ?? this.config.routerAddress ?? this.routerContract.target as string;\n const swapParams = this.applySlippage(quote.params, slippageBps);\n const useSwapETH = quote.srcNative === true || quote.dstNative === true;\n const encodedParams = this.encodeParams(swapParams);\n\n if (useSwapETH) {\n const data = this.routerContract.interface.encodeFunctionData('swapETH', [encodedParams]);\n return {\n to: routerAddress,\n data,\n value: quote.srcNative ? quote.amountIn : 0n,\n method: 'swapETH',\n };\n } else {\n const data = this.routerContract.interface.encodeFunctionData('swap', [encodedParams]);\n return {\n to: routerAddress,\n data,\n value: 0n,\n method: 'swap',\n };\n }\n }\n\n private buildSwapTransactionRequest(\n quote: Quote,\n options: SwapOptions\n ): { tx: SwapTxRequest; method: \"swap\" | \"swapETH\" } {\n const { to, data, value, method } = this.encodeSwapCalldata(quote, options.slippageBps);\n return {\n method,\n tx: this.applyTxOverrides({ to, data, value }, options, true),\n };\n }\n\n private async buildApprovalRequest(\n token: string,\n owner: string,\n amount: bigint,\n spender: string,\n options: SwapOptions\n ): Promise<SwapApprovalRequest | undefined> {\n const allowance = await this.getAllowance(token, owner, spender);\n if (allowance >= amount) {\n return undefined;\n }\n\n return {\n token,\n owner,\n spender,\n currentAllowance: allowance,\n requiredAmount: amount,\n approveAmount: ethers.MaxUint256,\n tx: this.buildApprovalTransactionRequest(token, spender, options),\n };\n }\n\n private buildApprovalTransactionRequest(\n token: string,\n spender: string,\n options: SwapOptions\n ): SwapTxRequest {\n const tokenInterface = new ethers.Interface(ERC20_ABI);\n const data = tokenInterface.encodeFunctionData(\"approve\", [spender, ethers.MaxUint256]);\n return this.applyTxOverrides({ to: token, data, value: 0n }, options, false);\n }\n\n private async getAllowance(\n token: string,\n owner: string,\n spender: string\n ): Promise<bigint> {\n const tokenContract = new ethers.Contract(token, ERC20_ABI, this.provider);\n return tokenContract.allowance(owner, spender);\n }\n\n private applyTxOverrides(\n tx: SwapTxRequest,\n options: SwapOptions,\n includeGasLimit: boolean\n ): SwapTxRequest {\n const txWithOverrides: SwapTxRequest = { ...tx };\n if (options.gasPrice) {\n txWithOverrides.gasPrice = options.gasPrice;\n }\n if (includeGasLimit && options.gasLimit) {\n txWithOverrides.gasLimit = options.gasLimit;\n }\n return txWithOverrides;\n }\n\n private async sendTransactionWithTimeout(\n signer: ethers.Signer,\n tx: SwapTxRequest,\n options: Pick<ExecuteOptions, \"timeoutMs\" | \"transactionResponsePollingIntervalsMs\">\n ): Promise<ethers.TransactionResponse> {\n const effectiveTimeoutMs = options.timeoutMs ?? DEFAULT_EXECUTE_TIMEOUT_MS;\n if (effectiveTimeoutMs <= 0) {\n return signer.sendTransaction(tx);\n }\n\n const uncheckedSigner = signer as ethers.Signer & {\n sendUncheckedTransaction?: (tx: ethers.TransactionRequest) => Promise<string>;\n provider?: ethers.Provider | null;\n };\n\n if (\n typeof uncheckedSigner.sendUncheckedTransaction === \"function\" &&\n uncheckedSigner.provider\n ) {\n const hash = await withWalletSendTimeout(\n uncheckedSigner.sendUncheckedTransaction(tx),\n effectiveTimeoutMs\n );\n\n const pollResult = await this.waitForTransactionResponse(\n uncheckedSigner.provider,\n hash,\n effectiveTimeoutMs,\n options.transactionResponsePollingIntervalsMs\n );\n if (pollResult.response) {\n return pollResult.response;\n }\n\n const failureMode =\n pollResult.rpcErrors > 0\n ? `${pollResult.rpcErrors} transient provider error(s) and ${pollResult.nullResponses} null response(s)`\n : `${pollResult.nullResponses} null response(s)`;\n throw new ExecuteTimeoutError(\n `Transaction was broadcast but provider did not return TransactionResponse within ${effectiveTimeoutMs}ms (${failureMode}).`,\n \"provider_index\",\n hash\n );\n }\n\n return withWalletSendTimeout(signer.sendTransaction(tx), effectiveTimeoutMs);\n }\n\n private async waitForTransactionResponse(\n provider: ethers.Provider,\n hash: string,\n timeoutMs: number,\n pollingIntervalsMs: readonly number[] = DEFAULT_TRANSACTION_RESPONSE_POLL_INTERVALS_MS\n ): Promise<{\n response: ethers.TransactionResponse | null;\n nullResponses: number;\n rpcErrors: number;\n }> {\n const deadline = Date.now() + timeoutMs;\n let attempt = 0;\n let nullResponses = 0;\n let rpcErrors = 0;\n while (Date.now() < deadline) {\n try {\n const response = await provider.getTransaction(hash);\n if (response) {\n return { response, nullResponses, rpcErrors };\n }\n nullResponses++;\n } catch {\n // Some wallets/RPCs lag while indexing a just-broadcast tx; keep polling until timeout.\n rpcErrors++;\n }\n const nextDelay = this.getNextPollingDelay(pollingIntervalsMs, attempt);\n attempt++;\n await this.delay(Math.min(nextDelay, Math.max(25, deadline - Date.now())));\n }\n return { response: null, nullResponses, rpcErrors };\n }\n\n private async delay(ms: number): Promise<void> {\n if (ms <= 0) {\n return;\n }\n await new Promise<void>((resolve) => {\n setTimeout(resolve, ms);\n });\n }\n\n private getNextPollingDelay(\n pollingIntervalsMs: readonly number[],\n attempt: number\n ): number {\n if (pollingIntervalsMs.length === 0) {\n return DEFAULT_TRANSACTION_RESPONSE_POLL_INTERVALS_MS.at(-1) ?? 1200;\n }\n return pollingIntervalsMs[Math.min(attempt, pollingIntervalsMs.length - 1)] ?? 1200;\n }\n\n /**\n * Get token metadata and, optionally, the balance for a specific owner.\n */\n async getTokenInfo(tokenAddress: string, ownerAddress?: string): Promise<{\n symbol: string;\n decimals: number;\n balance?: bigint;\n }> {\n const token = new ethers.Contract(tokenAddress, ERC20_ABI, this.provider);\n const [symbol, decimals, balance] = await Promise.all([\n token.symbol(),\n token.decimals(),\n ownerAddress ? token.balanceOf(ownerAddress) : Promise.resolve(undefined),\n ]);\n\n return balance === undefined ? { symbol, decimals } : { symbol, decimals, balance };\n }\n\n /**\n * Get user token balance\n */\n async getBalance(tokenAddress: string, userAddress: string): Promise<bigint> {\n const token = new ethers.Contract(tokenAddress, ERC20_ABI, this.provider);\n return token.balanceOf(userAddress);\n }\n\n /**\n * Get quote via API\n * @throws Error if API client is not configured\n */\n async getQuote(params: {\n srcToken: string;\n dstToken: string;\n amountIn: bigint;\n options?: QuoteOptions;\n }): Promise<Quote> {\n const { srcToken, dstToken, amountIn, options = {} } = params;\n\n const {\n byAmountIn = true,\n depth = API_DEFAULTS.depth,\n splitCount = API_DEFAULTS.splitCount,\n providers = API_DEFAULTS.providers,\n deadlineSeconds = DEFAULT_DEADLINE_SECONDS,\n } = options;\n\n // Detect native token sentinel and convert to WBNB for API\n const srcNative = isNativeTokenAddress(srcToken);\n const dstNative = isNativeTokenAddress(dstToken);\n const apiSrcToken = srcNative ? this.config.weth : srcToken;\n const apiDstToken = dstNative ? this.config.weth : dstToken;\n\n const apiData = await this.apiClient.findRoutes({\n from: apiSrcToken,\n target: apiDstToken,\n amount: amountIn,\n byAmountIn,\n depth,\n splitCount,\n providers,\n });\n\n return this.buildQuoteFromApi(apiData, apiSrcToken, apiDstToken, deadlineSeconds, providers, srcNative, dstNative);\n }\n\n /**\n * Filter paths by allowed providers, removing paths with disallowed providers\n * and cascade-removing orphaned paths that depend on removed paths.\n */\n private filterPathsByProviders(\n paths: ApiRoutePath[],\n allowedProviders: Provider[],\n srcToken: string,\n dstToken: string\n ): ApiRoutePath[] {\n const allowedSet = new Set(allowedProviders.map((p) => p.toUpperCase()));\n\n // Step 1: Remove paths with disallowed providers\n let filtered = paths.filter((p) => allowedSet.has(p.provider.toUpperCase()));\n\n if (filtered.length === paths.length) {\n return filtered; // Nothing was removed\n }\n\n // Step 2: Cascade-remove orphaned paths via reachability analysis\n const srcLower = srcToken.toLowerCase();\n const dstLower = dstToken.toLowerCase();\n\n // Forward reachability: which tokens can be reached from srcToken?\n const reachableFromSrc = new Set<string>();\n reachableFromSrc.add(srcLower);\n let changed = true;\n while (changed) {\n changed = false;\n for (const p of filtered) {\n if (\n reachableFromSrc.has(p.token_in.toLowerCase()) &&\n !reachableFromSrc.has(p.token_out.toLowerCase())\n ) {\n reachableFromSrc.add(p.token_out.toLowerCase());\n changed = true;\n }\n }\n }\n\n // Backward reachability: which tokens can reach dstToken?\n const reachableToDst = new Set<string>();\n reachableToDst.add(dstLower);\n changed = true;\n while (changed) {\n changed = false;\n for (const p of filtered) {\n if (\n reachableToDst.has(p.token_out.toLowerCase()) &&\n !reachableToDst.has(p.token_in.toLowerCase())\n ) {\n reachableToDst.add(p.token_in.toLowerCase());\n changed = true;\n }\n }\n }\n\n // Keep only paths on valid src→dst routes\n const beforeOrphan = filtered.length;\n filtered = filtered.filter(\n (p) =>\n reachableFromSrc.has(p.token_in.toLowerCase()) &&\n reachableToDst.has(p.token_out.toLowerCase())\n );\n\n return filtered;\n }\n\n /**\n * Build Quote from route data (e.g. from JSON file or API response).\n * Use this to simulate a swap from a pre-computed route without calling the API.\n *\n * @param data - Route data with paths, amount_in, amount_out, contracts, gas\n * @param srcToken - Source token address (first path token_in)\n * @param dstToken - Destination token address (last path token_out)\n * @param deadlineSeconds - Optional deadline in seconds from now (default: 20 min)\n */\n buildQuoteFromRouteData(\n data: ApiFindRouteData,\n srcToken: string,\n dstToken: string,\n deadlineSeconds?: number,\n options?: { srcNative?: boolean; dstNative?: boolean }\n ): Quote {\n const quote = this.buildQuoteFromRouteDataInternal(\n data,\n srcToken,\n dstToken,\n deadlineSeconds ?? DEFAULT_DEADLINE_SECONDS,\n undefined\n );\n if (options?.srcNative) quote.srcNative = true;\n if (options?.dstNative) quote.dstNative = true;\n return quote;\n }\n\n /**\n * Build Quote from API response\n */\n private buildQuoteFromApi(\n data: ApiFindRouteData,\n srcToken: string,\n dstToken: string,\n deadlineSeconds: number,\n requestedProviders?: Provider[],\n srcNative?: boolean,\n dstNative?: boolean\n ): Quote {\n const quote = this.buildQuoteFromRouteDataInternal(\n data,\n srcToken,\n dstToken,\n deadlineSeconds,\n requestedProviders\n );\n if (srcNative) quote.srcNative = true;\n if (dstNative) quote.dstNative = true;\n return quote;\n }\n\n private buildQuoteFromRouteDataInternal(\n data: ApiFindRouteData,\n srcToken: string,\n dstToken: string,\n deadlineSeconds: number,\n requestedProviders?: Provider[]\n ): Quote {\n // Filter out zero-amount dead paths (amount_in=0 AND not an entry point)\n const srcTokenLower = srcToken.toLowerCase();\n const originalPathCount = data.paths.length;\n let validPaths = data.paths.filter((p) => {\n const isEntryPoint = p.token_in.toLowerCase() === srcTokenLower;\n if (!isEntryPoint && BigInt(p.amount_in) === 0n && BigInt(p.amount_out) === 0n) {\n return false;\n }\n return true;\n });\n\n if (validPaths.length === 0) {\n throw new ApiError(\"All route paths have zero amounts\", 4001);\n }\n\n // Filter by requested providers (defensive check against API returning unwanted providers)\n if (requestedProviders && requestedProviders.length > 0) {\n validPaths = this.filterPathsByProviders(validPaths, requestedProviders, srcToken, dstToken);\n\n if (validPaths.length === 0) {\n throw new ApiError(\"No valid route paths remaining after provider filtering\", 4001);\n }\n }\n\n // Recalculate amounts from remaining valid paths\n const dstTokenLower = dstToken.toLowerCase();\n let amountIn = 0n;\n let amountOut = 0n;\n for (const p of validPaths) {\n if (p.token_in.toLowerCase() === srcTokenLower) {\n amountIn += BigInt(p.amount_in);\n }\n if (p.token_out.toLowerCase() === dstTokenLower) {\n amountOut += BigInt(p.amount_out);\n }\n }\n const deadline = BigInt(\n Math.floor(Date.now() / 1000) + deadlineSeconds\n );\n\n // Count how many steps consume each tokenIn.\n // When multiple steps share the same tokenIn, all but the last must use an\n // explicit amountIn (from the API) so the router deducts a fixed amount from\n // transient storage instead of consumeAll-ing the entire balance on the first\n // step and leaving nothing for the rest. The last consumer keeps amountIn=0\n // so it takes whatever remainder is left (handles rounding).\n const tokenInCount = new Map<string, number>();\n for (const p of validPaths) {\n const key = p.token_in.toLowerCase();\n tokenInCount.set(key, (tokenInCount.get(key) ?? 0) + 1);\n }\n const tokenInSeen = new Map<string, number>();\n const steps: SwapStep[] = validPaths.map((p) => {\n const key = p.token_in.toLowerCase();\n const seen = (tokenInSeen.get(key) ?? 0) + 1;\n tokenInSeen.set(key, seen);\n const total = tokenInCount.get(key)!;\n // Use explicit amountIn when there are multiple consumers of this token\n // AND this is not the last consumer (last one uses consumeAll=0 for remainder).\n const useExplicit = total > 1 && seen < total;\n const isEntryPoint = key === srcTokenLower;\n return {\n adapter: p.adapter,\n pool: p.provider.toUpperCase() === \"PANCAKE_INFINITY_CL\"\n ? PANCAKE_INFINITY_CL_POOL_MANAGER\n : p.provider.toUpperCase() === \"UNISWAPV4\"\n ? ethers.ZeroAddress\n : p.pool,\n tokenIn: p.token_in,\n tokenOut: p.token_out,\n amountIn: isEntryPoint || useExplicit ? BigInt(p.amount_in) : 0n,\n extraData: p.extra_data || \"0x\",\n };\n });\n\n const intermediateTokenSet = new Set<string>();\n for (const p of validPaths) {\n const out = p.token_out.toLowerCase();\n if (out !== dstTokenLower) {\n intermediateTokenSet.add(p.token_out);\n }\n }\n const intermediateTokens = Array.from(intermediateTokenSet);\n\n const swapParams: SwapParams = {\n srcToken,\n dstToken,\n amountIn,\n amountOutMin: amountOut,\n steps,\n intermediateTokens,\n deadline,\n quoteId: data.request_id ? ethers.id(data.request_id).slice(0, 66) : ethers.ZeroHash,\n expectAmountOut: amountOut,\n };\n\n const route: SplitRoute = {\n routes: [\n {\n steps: validPaths.map((p) => ({\n pool: {\n address: p.pool,\n token0: p.token_in,\n token1: p.token_out,\n protocol: this.getProtocolForProvider(p.provider),\n fee: p.fee_rate\n ? Math.round(parseFloat(p.fee_rate) * 1_000_000)\n : undefined,\n },\n tokenIn: p.token_in,\n tokenOut: p.token_out,\n amountIn: BigInt(p.amount_in),\n amountOut: BigInt(p.amount_out),\n })),\n amountIn,\n amountOut,\n gasEstimate: BigInt(data.gas),\n },\n ],\n percentages: [10000],\n totalAmountIn: amountIn,\n totalAmountOut: amountOut,\n totalGasEstimate: BigInt(data.gas),\n };\n\n if (!data.contracts?.router) {\n throw new ApiError(\"API response missing contracts.router address\", 4002);\n }\n\n return {\n srcToken,\n dstToken,\n amountIn,\n amountOut,\n priceImpact: parseFloat(data.deviation_ratio || \"0\"),\n route,\n params: swapParams,\n gasEstimate: BigInt(data.gas),\n routerAddress: data.contracts?.router,\n };\n }\n\n /**\n * Get available providers from the API\n * @throws Error if API client is not configured\n */\n async getAvailableProviders(): Promise<string[]> {\n return this.apiClient.getAvailableProviders();\n }\n\n /**\n * Simulate swap via eth_call (no gas, no state change)\n * Useful for testing and verifying quote accuracy\n *\n * @param quote - Quote object from getQuote\n * @param slippageBps - Slippage tolerance in basis points (e.g. 50 = 0.5%). Required.\n * @param fromAddress - Optional caller address for simulation (default: zero address)\n * @param stateOverrides - Optional state overrides for ERC20 balance/allowance\n * @returns Simulated amountOut and method used\n */\n async simulate(\n quote: Quote,\n slippageBps: number,\n fromAddress?: string,\n stateOverrides?: Record<string, { stateDiff: Record<string, string> }>\n ): Promise<{ amountOut: bigint; method: 'swap' | 'swapETH' }> {\n const caller = fromAddress || ethers.ZeroAddress;\n const { to, data, value, method } = this.encodeSwapCalldata(quote, slippageBps);\n\n console.log('[PeachClient] simulate using router:', to);\n\n if (stateOverrides) {\n // Use JsonRpcProvider for state overrides. Some RPCs (Go hexutil.Big) reject hex with leading zeros.\n const jsonRpcProvider = this.getJsonRpcProviderForStateOverrides();\n const valueHex = value > 0n ? '0x' + value.toString(16) : undefined;\n const result = await jsonRpcProvider.send('eth_call', [\n { from: caller, to, data, value: valueHex },\n 'latest',\n stateOverrides,\n ]);\n const [amountOut] = this.routerContract.interface.decodeFunctionResult(method, result);\n return { amountOut, method };\n } else {\n const result = await this.provider.call({\n from: caller,\n to,\n data,\n value: value > 0n ? value : undefined,\n });\n const [amountOut] = this.routerContract.interface.decodeFunctionResult(method, result);\n return { amountOut, method };\n }\n }\n\n private getJsonRpcProviderForStateOverrides(): ethers.JsonRpcProvider {\n const provider = this.provider as Partial<ethers.JsonRpcProvider>;\n if (typeof provider.send !== \"function\") {\n throw new Error(\n \"stateOverrides require a JsonRpcProvider-compatible provider with send(method, params).\"\n );\n }\n return this.provider as ethers.JsonRpcProvider;\n }\n\n /**\n * Format simulate error with human-readable details\n */\n private formatSimulateError(err: unknown, quote: Quote, method: string, caller: string): Error {\n const original = err instanceof Error ? err : new Error(String(err));\n const errAny = err as Record<string, unknown>;\n\n // Extract revert reason from ethers CALL_EXCEPTION\n let reason = \"unknown\";\n if (errAny.reason && typeof errAny.reason === \"string\") {\n reason = errAny.reason;\n } else if (errAny.revert && typeof errAny.revert === \"object\") {\n const revert = errAny.revert as Record<string, unknown>;\n if (revert.args && Array.isArray(revert.args)) {\n reason = revert.args.join(\", \");\n }\n } else if (original.message) {\n reason = original.message;\n }\n\n // Build step summary\n const steps = quote.params.steps.map((s, i) =>\n ` Step ${i}: ${s.tokenIn.slice(0, 10)}→${s.tokenOut.slice(0, 10)} via adapter ${s.adapter.slice(0, 10)} pool ${s.pool.slice(0, 10)}`\n ).join(\"\\n\");\n\n const msg = [\n `Simulate ${method} failed: ${reason}`,\n ` Route: ${quote.srcToken} → ${quote.dstToken}`,\n ` AmountIn: ${quote.amountIn}`,\n ` AmountOutMin: ${quote.params.amountOutMin}`,\n ` Router: ${quote.routerAddress}`,\n ` Caller: ${caller}`,\n ` Steps (${quote.params.steps.length}):`,\n steps,\n ].join(\"\\n\");\n\n const e = new Error(msg) as Error & { cause?: unknown; reason?: string };\n e.cause = original;\n e.reason = reason;\n return e;\n }\n\n /**\n * Find which step in the route causes the same revert as the full route (e.g. MUL_ERROR).\n * Simulates with steps [0..1], [0..2], ... and returns the first step whose revert\n * matches fullRouteError. Ignores steps that revert with a different reason (e.g. unknown custom error).\n *\n * @param quote - Full quote from getQuote (the one that fails when simulated)\n * @param slippageBps - Same as for simulate\n * @param fromAddress - Same as for simulate\n * @param stateOverrides - Same as for simulate (use when simulating ERC20 sell with arbitrary address)\n * @param fullRouteError - The error from simulating the full route. Required so we match by revert reason (e.g. \"MUL_ERROR\"); only the step that produces the same reason is returned.\n * @returns The step index and step details whose revert matches fullRouteError, or null if none match or full route succeeds\n */\n async findFailingStep(\n quote: Quote,\n slippageBps: number,\n fromAddress?: string,\n stateOverrides?: Record<string, { stateDiff: Record<string, string> }>,\n fullRouteError?: unknown\n ): Promise<FindFailingStepResult | null> {\n const steps = quote.params.steps;\n if (!steps.length) return null;\n\n const targetReason = fullRouteError != null ? this.normalizeRevertReason(fullRouteError) : undefined;\n\n for (let n = 1; n <= steps.length; n++) {\n const truncated = this.quoteWithFirstNSteps(quote, n);\n try {\n await this.simulate(truncated, slippageBps, fromAddress, stateOverrides);\n } catch (err: unknown) {\n const stepReason = this.normalizeRevertReason(err);\n const msg =\n (err as { message?: string; reason?: string; shortMessage?: string })?.message ??\n (err as { reason?: string })?.reason ??\n (err as { shortMessage?: string })?.shortMessage;\n const match = targetReason != null ? stepReason === targetReason : true;\n if (match) {\n return {\n stepIndex: n - 1,\n step: steps[n - 1]!,\n error: err,\n revertMessage: typeof msg === 'string' ? msg : undefined,\n fullRouteRevertMessage: targetReason ?? undefined,\n };\n }\n // Revert reason differs (e.g. step 0 gave unknown custom error, we want MUL_ERROR): try next step\n }\n }\n return null;\n }\n\n /** Extract a comparable revert reason (e.g. \"MUL_ERROR\") from an error for findFailingStep matching. */\n private normalizeRevertReason(err: unknown): string | undefined {\n if (err == null) return undefined;\n const e = err as { reason?: string; message?: string; shortMessage?: string; data?: string };\n if (typeof e.reason === 'string' && e.reason.length > 0) return e.reason;\n const msg = e.shortMessage ?? e.message;\n if (typeof msg !== 'string') return undefined;\n const reasonMatch = msg.match(/reason=\"([^\"]+)\"/);\n if (reasonMatch) return reasonMatch[1];\n const revertedMatch = msg.match(/reverted:\\s*\"([^\"]+)\"/);\n if (revertedMatch) return revertedMatch[1];\n const execRevertedMatch = msg.match(/execution reverted:\\s*\"([^\"]+)\"/);\n if (execRevertedMatch) return execRevertedMatch[1];\n return undefined;\n }\n\n /** Build a quote that only includes the first stepCount steps (for findFailingStep). */\n private quoteWithFirstNSteps(quote: Quote, stepCount: number): Quote {\n const steps = quote.params.steps.slice(0, stepCount);\n const dstLower = quote.dstToken.toLowerCase();\n // Deduplicate intermediate tokens: split routes can have the same token as output\n // of multiple parallel steps (e.g. two USDT→tokenX hops both produce tokenX).\n // Passing duplicates to the router causes incorrect intermediate token accounting.\n const seen = new Set<string>();\n const intermediateTokens: string[] = [];\n for (const s of steps) {\n const lower = s.tokenOut.toLowerCase();\n if (lower !== dstLower && !seen.has(lower)) {\n seen.add(lower);\n intermediateTokens.push(s.tokenOut);\n }\n }\n return {\n ...quote,\n params: {\n ...quote.params,\n steps,\n intermediateTokens,\n },\n };\n }\n\n /**\n * Build state overrides for ERC20 token balance and allowance.\n * Useful for simulating swaps without actual on-chain token balance/approval.\n *\n * Automatically covers multiple storage slot layouts (slots 0-2 for balance,\n * slots 0-7 for allowance) to handle OZ ERC20 (slot 0/1), Ownable+ERC20 (slot 1/2),\n * and other common BSC token implementations.\n *\n * WBNB (native wrap) is skipped automatically — swapETH wraps msg.value\n * internally so no ERC20 approval from the sender is needed.\n *\n * @param tokenAddress - ERC20 token address (WBNB returns empty overrides)\n * @param owner - Address that needs the balance and allowance\n * @param routerAddress - Router address (used as fallback spender)\n * @param balance - Balance to inject (default: 1M tokens with 18 decimals)\n * @param spenderAddress - Spender to approve (default: routerAddress). Pass quote.routerAddress when simulating API quotes.\n */\n buildStateOverrides(\n tokenAddress: string,\n owner: string,\n routerAddress: string,\n balance?: bigint,\n spenderAddress?: string,\n options?: { isNative?: boolean }\n ): Record<string, { stateDiff: Record<string, string> }> {\n // swapETH wraps msg.value natively — no ERC20 override needed for native token\n if (options?.isNative || isNativeTokenAddress(tokenAddress)) {\n return {};\n }\n\n if (!routerAddress || routerAddress === ethers.ZeroAddress) {\n throw new Error(\"buildStateOverrides requires a non-zero routerAddress.\");\n }\n\n const abiCoder = ethers.AbiCoder.defaultAbiCoder();\n const tokenBalance = balance || ethers.parseUnits('1000000', 18);\n const spender = spenderAddress ?? routerAddress;\n const balanceHex = ethers.zeroPadValue(ethers.toBeHex(tokenBalance), 32);\n const maxUint256Hex = ethers.zeroPadValue(ethers.toBeHex(ethers.MaxUint256), 32);\n\n const stateDiff: Record<string, string> = {};\n\n // Target storage slots covering all common ERC20 layouts:\n // 0-2 : standard OZ ERC20 (balance=0, allowance=1) and Ownable+ERC20 (balance=1, allowance=2)\n // 9-13 : USDC/FiatToken style (balance≈9-11, allowance≈10-12)\n // 50-52 : OZ UpgradeableERC20 v4.x (ContextUpgradeable.__gap[50] pushes balance to slot 50, allowance to slot 51)\n // 100-102: OZ Upgradeable with OwnableUpgradeable (additional 50-slot gap before ERC20 state)\n const SLOTS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 50, 51, 52, 100, 101, 102];\n\n for (const slot of SLOTS) {\n // Balance\n const balKey = ethers.keccak256(abiCoder.encode(['address', 'uint256'], [owner, slot]));\n stateDiff[balKey] = balanceHex;\n\n // Allowance (nested mapping: allowances[owner][spender] at slot S)\n const inner = ethers.keccak256(abiCoder.encode(['address', 'uint256'], [owner, slot]));\n const allowKey = ethers.keccak256(abiCoder.encode(['address', 'bytes32'], [spender, inner]));\n stateDiff[allowKey] = maxUint256Hex;\n }\n\n return {\n [tokenAddress.toLowerCase()]: { stateDiff },\n };\n }\n}\n","/**\n * RouteDiscovery - Route discovery and optimization\n *\n * Features:\n * 1. Discover available pools\n * 2. Calculate optimal routes\n * 3. Split large trades\n */\n\nimport { ethers } from \"ethers\";\nimport {\n PeachConfig,\n PoolInfo,\n Route,\n RouteStep,\n SplitRoute,\n ProtocolType,\n} from \"../types\";\n\n// PancakeSwap V2 Pair ABI\nconst PAIR_ABI = [\n \"function token0() external view returns (address)\",\n \"function token1() external view returns (address)\",\n \"function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)\",\n];\n\n// PancakeSwap V3 Pool ABI\nconst V3_POOL_ABI = [\n \"function token0() external view returns (address)\",\n \"function token1() external view returns (address)\",\n \"function fee() external view returns (uint24)\",\n \"function liquidity() external view returns (uint128)\",\n \"function slot0() external view returns (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint32 feeProtocol, bool unlocked)\",\n];\n\n// PancakeSwap V2 Factory\nconst V2_FACTORY_ADDRESS = \"0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73\";\nconst V2_FACTORY_ABI = [\n \"function getPair(address tokenA, address tokenB) external view returns (address pair)\",\n];\n\n// PancakeSwap V3 Factory\nconst V3_FACTORY_ADDRESS = \"0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865\";\nconst V3_FACTORY_ABI = [\n \"function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool)\",\n];\n\n// V3 fee tiers\nconst V3_FEE_TIERS = [100, 500, 2500, 10000];\n\n// Gas estimation constants\nconst GAS_PER_V2_SWAP = 60000n;\nconst GAS_PER_V3_SWAP = 120000n;\nconst GAS_PER_DODO_SWAP = 100000n;\n\nexport class RouteDiscovery {\n private provider: ethers.Provider;\n private config: PeachConfig;\n private v2Factory: ethers.Contract;\n private v3Factory: ethers.Contract;\n\n // Pool cache\n private poolCache = new Map<string, PoolInfo>();\n\n constructor(provider: ethers.Provider, config: PeachConfig) {\n this.provider = provider;\n this.config = config;\n this.v2Factory = new ethers.Contract(\n V2_FACTORY_ADDRESS,\n V2_FACTORY_ABI,\n provider\n );\n this.v3Factory = new ethers.Contract(\n V3_FACTORY_ADDRESS,\n V3_FACTORY_ABI,\n provider\n );\n }\n\n /**\n * Discover optimal route\n */\n async findBestRoute(\n srcToken: string,\n dstToken: string,\n amountIn: bigint,\n \n ): Promise<SplitRoute> {\n // 1. Discover all available pools\n const pools = await this.discoverPools(srcToken, dstToken);\n\n if (pools.length === 0) {\n throw new Error(`No pools found for ${srcToken} -> ${dstToken}`);\n }\n\n // 2. Calculate quotes for each pool\n const routes = await this.calculateRoutes(\n pools,\n srcToken,\n dstToken,\n amountIn\n );\n\n // 3. Select optimal route (currently simple selection of max output single route)\n // TODO: Implement split route optimization\n const bestRoute = routes.reduce((best, current) =>\n current.amountOut > best.amountOut ? current : best\n );\n\n return {\n routes: [bestRoute],\n percentages: [10000], // 100%\n totalAmountIn: amountIn,\n totalAmountOut: bestRoute.amountOut,\n totalGasEstimate: bestRoute.gasEstimate,\n };\n }\n\n /**\n * Discover available pools (direct path)\n */\n private async discoverPools(\n tokenA: string,\n tokenB: string\n ): Promise<PoolInfo[]> {\n const pools: PoolInfo[] = [];\n\n // 1. Find V2 pool\n try {\n const v2Pool = await this.findV2Pool(tokenA, tokenB);\n if (v2Pool) {\n pools.push(v2Pool);\n }\n } catch (e) {\n // V2 pool doesn't exist\n }\n\n // 2. Find V3 pools (all fee tiers)\n for (const fee of V3_FEE_TIERS) {\n try {\n const v3Pool = await this.findV3Pool(tokenA, tokenB, fee);\n if (v3Pool) {\n pools.push(v3Pool);\n }\n } catch (e) {\n // V3 pool with this fee tier doesn't exist\n }\n }\n\n return pools;\n }\n\n /**\n * Find V2 pool\n */\n private async findV2Pool(\n tokenA: string,\n tokenB: string\n ): Promise<PoolInfo | null> {\n const cacheKey = `v2-${tokenA}-${tokenB}`;\n if (this.poolCache.has(cacheKey)) {\n return this.poolCache.get(cacheKey)!;\n }\n\n const pairAddress = await this.v2Factory.getPair(tokenA, tokenB);\n if (pairAddress === ethers.ZeroAddress) {\n return null;\n }\n\n const pair = new ethers.Contract(pairAddress, PAIR_ABI, this.provider);\n const [token0, token1, reserves] = await Promise.all([\n pair.token0(),\n pair.token1(),\n pair.getReserves(),\n ]);\n\n const pool: PoolInfo = {\n address: pairAddress,\n token0,\n token1,\n protocol: ProtocolType.PancakeV2,\n reserve0: reserves.reserve0,\n reserve1: reserves.reserve1,\n };\n\n this.poolCache.set(cacheKey, pool);\n return pool;\n }\n\n /**\n * Find V3 pool\n */\n private async findV3Pool(\n tokenA: string,\n tokenB: string,\n fee: number\n ): Promise<PoolInfo | null> {\n const cacheKey = `v3-${tokenA}-${tokenB}-${fee}`;\n if (this.poolCache.has(cacheKey)) {\n return this.poolCache.get(cacheKey)!;\n }\n\n const poolAddress = await this.v3Factory.getPool(tokenA, tokenB, fee);\n if (poolAddress === ethers.ZeroAddress) {\n return null;\n }\n\n const v3Pool = new ethers.Contract(poolAddress, V3_POOL_ABI, this.provider);\n const [token0, token1, liquidity] = await Promise.all([\n v3Pool.token0(),\n v3Pool.token1(),\n v3Pool.liquidity(),\n ]);\n\n // Skip pools with no liquidity\n if (liquidity === 0n) {\n return null;\n }\n\n const pool: PoolInfo = {\n address: poolAddress,\n token0,\n token1,\n protocol: ProtocolType.PancakeV3,\n fee,\n liquidity,\n };\n\n this.poolCache.set(cacheKey, pool);\n return pool;\n }\n\n /**\n * Calculate route quotes\n */\n private async calculateRoutes(\n pools: PoolInfo[],\n srcToken: string,\n dstToken: string,\n amountIn: bigint\n ): Promise<Route[]> {\n const routes: Route[] = [];\n\n for (const pool of pools) {\n try {\n const amountOut = await this.getAmountOut(\n pool,\n srcToken,\n dstToken,\n amountIn\n );\n\n if (amountOut > 0n) {\n routes.push({\n steps: [\n {\n pool,\n tokenIn: srcToken,\n tokenOut: dstToken,\n amountIn,\n amountOut,\n },\n ],\n amountIn,\n amountOut,\n gasEstimate:\n pool.protocol === ProtocolType.PancakeV2\n ? GAS_PER_V2_SWAP\n : pool.protocol === ProtocolType.Dodo\n ? GAS_PER_DODO_SWAP\n : GAS_PER_V3_SWAP,\n });\n }\n } catch (e) {\n // Quote failed, skip this pool\n }\n }\n\n return routes;\n }\n\n /**\n * Get output for a single pool\n */\n private async getAmountOut(\n pool: PoolInfo,\n tokenIn: string,\n tokenOut: string,\n amountIn: bigint\n ): Promise<bigint> {\n if (pool.protocol === ProtocolType.PancakeV2) {\n return this.getV2AmountOut(pool, tokenIn, amountIn);\n }\n if (pool.protocol === ProtocolType.Dodo) {\n // DODO pools are discovered via API; local discovery not implemented\n throw new Error(\"DODO amount out not supported in local route discovery\");\n }\n // V3 requires Quoter contract or simulation, simplified here\n return this.estimateV3AmountOut(pool, tokenIn, amountIn);\n }\n\n /**\n * V2 output calculation\n */\n private getV2AmountOut(\n pool: PoolInfo,\n tokenIn: string,\n amountIn: bigint\n ): bigint {\n const isToken0In = tokenIn.toLowerCase() === pool.token0.toLowerCase();\n const [reserveIn, reserveOut] = isToken0In\n ? [pool.reserve0!, pool.reserve1!]\n : [pool.reserve1!, pool.reserve0!];\n\n const amountInWithFee = amountIn * 9975n; // 0.25% fee\n const numerator = amountInWithFee * reserveOut;\n const denominator = reserveIn * 10000n + amountInWithFee;\n\n return numerator / denominator;\n }\n\n /**\n * V3 output estimation (simplified)\n */\n private estimateV3AmountOut(\n pool: PoolInfo,\n tokenIn: string,\n amountIn: bigint\n ): bigint {\n // Simplified estimation: assume linear price\n // Should use Quoter contract for accurate calculation in practice\n const fee = BigInt(pool.fee || 2500);\n const feeMultiplier = 1000000n - fee;\n return (amountIn * feeMultiplier) / 1000000n;\n }\n\n /**\n * Clear cache\n */\n clearCache(): void {\n this.poolCache.clear();\n }\n}\n","/**\n * SwapBuilder - Swap parameters builder\n *\n * Core features:\n * 1. Flatten split routes into step list\n * 2. Merge identical pools (pool merging optimization)\n * 3. Topological sort (ensure correct dependency order)\n * 4. Generate final SwapParams\n */\n\nimport { ethers } from \"ethers\";\nimport {\n SwapParams,\n SwapStep,\n SplitRoute,\n Route,\n RouteStep,\n PoolInfo,\n ProtocolType,\n BPS_DENOMINATOR,\n DEFAULT_DEADLINE_SECONDS,\n} from \"../types\";\n\nexport class SwapBuilder {\n private adapters: Map<ProtocolType, string>;\n\n constructor(adapters: Map<ProtocolType, string>) {\n this.adapters = adapters;\n }\n\n /**\n * Build SwapParams from split route\n */\n build(\n splitRoute: SplitRoute,\n srcToken: string,\n dstToken: string,\n amountOutMin: bigint,\n deadlineSeconds: number = DEFAULT_DEADLINE_SECONDS\n ): SwapParams {\n // 1. Flatten all routes into step list\n const flatSteps = this.flattenRoutes(splitRoute);\n\n // 2. Merge identical pools\n const mergedSteps = this.mergeIdenticalPools(flatSteps);\n\n // 3. Topological sort\n const sortedSteps = this.topologicalSort(mergedSteps, srcToken);\n\n // 4. Convert to SwapStep format\n const steps = this.convertToSwapSteps(sortedSteps);\n\n // 5. Extract intermediate tokens\n const intermediateTokens = this.extractIntermediates(\n sortedSteps,\n srcToken,\n dstToken\n );\n\n // 6. Calculate deadline\n const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineSeconds);\n\n return {\n srcToken,\n dstToken,\n amountIn: splitRoute.totalAmountIn,\n amountOutMin,\n steps,\n intermediateTokens,\n deadline,\n quoteId: ethers.ZeroHash,\n expectAmountOut: splitRoute.totalAmountOut,\n };\n }\n\n /**\n * Flatten split route\n */\n private flattenRoutes(splitRoute: SplitRoute): InternalStep[] {\n const steps: InternalStep[] = [];\n\n for (let i = 0; i < splitRoute.routes.length; i++) {\n const route = splitRoute.routes[i];\n const percentage = splitRoute.percentages[i];\n\n // Calculate allocated amount for this route\n const routeAmount =\n (splitRoute.totalAmountIn * BigInt(percentage)) / BPS_DENOMINATOR;\n\n for (let j = 0; j < route.steps.length; j++) {\n const routeStep = route.steps[j];\n\n steps.push({\n pool: routeStep.pool,\n tokenIn: routeStep.tokenIn,\n tokenOut: routeStep.tokenOut,\n // Only first step has fixed amount, subsequent steps depend on previous output\n amountIn: j === 0 ? routeAmount : 0n,\n routeIndex: i,\n stepIndex: j,\n });\n }\n }\n\n return steps;\n }\n\n /**\n * Merge identical pools\n * Key: pool + tokenIn + tokenOut\n */\n private mergeIdenticalPools(steps: InternalStep[]): InternalStep[] {\n const poolMap = new Map<string, InternalStep>();\n const result: InternalStep[] = [];\n\n for (const step of steps) {\n const key = `${step.pool.address}-${step.tokenIn}-${step.tokenOut}`;\n\n if (poolMap.has(key)) {\n // Identical pool already exists\n const existing = poolMap.get(key)!;\n\n // If current step has fixed input, accumulate to existing step\n if (step.amountIn > 0n) {\n if (existing.amountIn > 0n) {\n // Both have fixed input - this shouldn't happen after first hop\n // Keep consume all mode\n existing.amountIn = 0n;\n }\n }\n // If existing step is already consume all (0n), keep it unchanged\n } else {\n const newStep = { ...step };\n poolMap.set(key, newStep);\n result.push(newStep);\n }\n }\n\n // For merged pools with multiple input sources, set to consume all\n for (const step of result) {\n const key = `${step.pool.address}-${step.tokenIn}-${step.tokenOut}`;\n const allStepsForPool = steps.filter(\n (s) =>\n `${s.pool.address}-${s.tokenIn}-${s.tokenOut}` === key\n );\n\n if (allStepsForPool.length > 1) {\n // Multiple input sources, use consume all\n step.amountIn = 0n;\n }\n }\n\n return result;\n }\n\n /**\n * Topological sort (Kahn's Algorithm)\n */\n private topologicalSort(\n steps: InternalStep[],\n srcToken: string\n ): InternalStep[] {\n // Build adjacency list and in-degree\n const inDegree = new Map<string, number>();\n const stepMap = new Map<string, InternalStep>();\n const graph = new Map<string, string[]>(); // tokenOut -> [stepKeys that consume it]\n\n for (const step of steps) {\n const key = this.stepKey(step);\n stepMap.set(key, step);\n inDegree.set(key, 0);\n graph.set(key, []);\n }\n\n // Calculate in-degree\n for (const step of steps) {\n const key = this.stepKey(step);\n\n // If tokenIn is not source token, need to wait for producer\n if (step.tokenIn !== srcToken) {\n // Find steps that produce this token\n for (const producer of steps) {\n if (producer.tokenOut === step.tokenIn) {\n const producerKey = this.stepKey(producer);\n if (producerKey !== key) {\n // Add edge: producer -> step\n graph.get(producerKey)!.push(key);\n inDegree.set(key, (inDegree.get(key) || 0) + 1);\n }\n }\n }\n }\n }\n\n // BFS\n const queue: string[] = [];\n for (const [key, degree] of inDegree) {\n if (degree === 0) {\n queue.push(key);\n }\n }\n\n const result: InternalStep[] = [];\n while (queue.length > 0) {\n const key = queue.shift()!;\n const step = stepMap.get(key)!;\n result.push(step);\n\n // Update in-degree of steps dependent on this step\n for (const dependent of graph.get(key) || []) {\n const newDegree = (inDegree.get(dependent) || 0) - 1;\n inDegree.set(dependent, newDegree);\n if (newDegree === 0) {\n queue.push(dependent);\n }\n }\n }\n\n // Check for cycles\n if (result.length !== steps.length) {\n throw new Error(\"Circular dependency detected in route\");\n }\n\n return result;\n }\n\n /**\n * Convert to contract SwapStep format\n */\n private convertToSwapSteps(steps: InternalStep[]): SwapStep[] {\n return steps.map((step) => {\n const adapter = this.adapters.get(step.pool.protocol);\n if (!adapter) {\n throw new Error(`No adapter for protocol: ${step.pool.protocol}`);\n }\n\n return {\n adapter,\n pool: step.pool.address,\n tokenIn: step.tokenIn,\n tokenOut: step.tokenOut,\n amountIn: step.amountIn,\n extraData: this.encodeExtraData(step.pool),\n };\n });\n }\n\n /**\n * Encode protocol-specific parameters\n */\n private encodeExtraData(pool: PoolInfo): string {\n switch (pool.protocol) {\n case ProtocolType.PancakeV2:\n return \"0x\"; // V2 doesn't need extra parameters\n\n case ProtocolType.PancakeV3:\n case ProtocolType.UniswapV3:\n case ProtocolType.UniswapV4:\n // V3/V4 fee can be read from pool contract, no need to pass in extraData\n return \"0x\";\n\n case ProtocolType.Dodo:\n // DODO adapter typically uses pool-specific extraData from API or 0x\n return \"0x\";\n\n default:\n return \"0x\";\n }\n }\n\n /**\n * Extract intermediate tokens\n */\n private extractIntermediates(\n steps: InternalStep[],\n srcToken: string,\n dstToken: string\n ): string[] {\n const tokens = new Set<string>();\n\n for (const step of steps) {\n // tokenOut that is not destination token is intermediate token\n if (step.tokenOut !== dstToken) {\n tokens.add(step.tokenOut);\n }\n }\n\n // Exclude source and destination tokens\n tokens.delete(srcToken);\n tokens.delete(dstToken);\n\n return Array.from(tokens);\n }\n\n /**\n * Generate unique step key\n */\n private stepKey(step: InternalStep): string {\n return `${step.pool.address}-${step.tokenIn}-${step.tokenOut}`;\n }\n}\n\n/**\n * Internal step type\n */\ninterface InternalStep {\n pool: PoolInfo;\n tokenIn: string;\n tokenOut: string;\n amountIn: bigint;\n routeIndex: number;\n stepIndex: number;\n}\n"],"names":["DEFAULT_API_URL","DEFAULT_SLIPPAGE_BPS","BPS_DENOMINATOR","DEFAULT_DEADLINE_SECONDS","DEFAULT_EXECUTE_TIMEOUT_MS","DEFAULT_TRANSACTION_RESPONSE_POLL_INTERVALS_MS","NATIVE_TOKEN_ADDRESS","isNativeTokenAddress","address","ERR_ZERO_AMOUNT_PATHS","ProtocolType","BSC_MAINNET_CONFIG","BSC_TESTNET_CONFIG","ExecuteTimeoutError","message","stage","txHash","API_DEFAULTS","DEFAULT_TIMEOUT","ApiClient","config","params","from","target","amount","byAmountIn","depth","splitCount","providers","queryParams","url","controller","timeoutId","response","ApiError","json","error","code","withWalletSendTimeout","promise","timeoutMs","_","reject","PEACH_ROUTER_ABI","ERC20_ABI","PANCAKE_INFINITY_CL_POOL_MANAGER","PROVIDER_TO_PROTOCOL","PeachClient","provider","options","ethers","quote","addr","slippageBps","amountOutMin","ownerAddress","routerAddress","tx","method","approval","signer","signerAddress","prepared","err","msg","isEstimateGas","isMissingRevertData","friendly","e","step","swapParams","useSwapETH","encodedParams","data","to","value","token","owner","spender","allowance","includeGasLimit","txWithOverrides","effectiveTimeoutMs","uncheckedSigner","hash","pollResult","failureMode","pollingIntervalsMs","deadline","attempt","nullResponses","rpcErrors","nextDelay","ms","resolve","tokenAddress","symbol","decimals","balance","userAddress","srcToken","dstToken","amountIn","deadlineSeconds","srcNative","dstNative","apiSrcToken","apiDstToken","apiData","paths","allowedProviders","allowedSet","p","filtered","srcLower","dstLower","reachableFromSrc","changed","reachableToDst","requestedProviders","srcTokenLower","validPaths","dstTokenLower","amountOut","tokenInCount","key","tokenInSeen","steps","seen","total","useExplicit","isEntryPoint","intermediateTokenSet","intermediateTokens","route","fromAddress","stateOverrides","caller","jsonRpcProvider","valueHex","result","original","errAny","reason","revert","s","i","fullRouteError","targetReason","n","truncated","stepReason","reasonMatch","revertedMatch","execRevertedMatch","stepCount","lower","spenderAddress","abiCoder","tokenBalance","balanceHex","maxUint256Hex","stateDiff","SLOTS","slot","balKey","inner","allowKey","PAIR_ABI","V3_POOL_ABI","V2_FACTORY_ADDRESS","V2_FACTORY_ABI","V3_FACTORY_ADDRESS","V3_FACTORY_ABI","V3_FEE_TIERS","GAS_PER_V2_SWAP","GAS_PER_V3_SWAP","GAS_PER_DODO_SWAP","RouteDiscovery","pools","bestRoute","best","current","tokenA","tokenB","v2Pool","fee","v3Pool","cacheKey","pairAddress","pair","token0","token1","reserves","pool","poolAddress","liquidity","routes","tokenIn","tokenOut","isToken0In","reserveIn","reserveOut","amountInWithFee","numerator","denominator","feeMultiplier","SwapBuilder","adapters","splitRoute","flatSteps","mergedSteps","sortedSteps","percentage","routeAmount","j","routeStep","poolMap","existing","newStep","inDegree","stepMap","graph","producer","producerKey","queue","degree","dependent","newDegree","adapter","tokens"],"mappings":"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"}
|
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/types/index.ts","../src/clients/ApiClient.ts","../src/utils/wallet.ts","../src/clients/PeachClient.ts","../src/clients/RouteDiscovery.ts","../src/builders/SwapBuilder.ts"],"sourcesContent":["/**\n * Peach Aggregator SDK Types\n *\n * Simplified design: only supports linear execution + topological sort + pool merging\n */\n\n// ============ Constants ============\n\nexport const DEFAULT_API_URL = \"https://api.peach.ag\";\nexport const DEFAULT_SLIPPAGE_BPS = 50; // 0.5%\nexport const BPS_DENOMINATOR = 10000n;\nexport const DEFAULT_DEADLINE_SECONDS = 1200; // 20 minutes\nexport const DEFAULT_EXECUTE_TIMEOUT_MS = 60_000; // 60 seconds\nexport const DEFAULT_TRANSACTION_RESPONSE_POLL_INTERVALS_MS = [50, 100, 200, 400, 800, 1200] as const;\n\n/**\n * Sentinel address indicating native token (e.g. BNB on BSC).\n * Pass this as srcToken/dstToken to distinguish native BNB from WBNB ERC20.\n */\nexport const NATIVE_TOKEN_ADDRESS = \"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE\";\n\n/**\n * Check if an address is the native token sentinel address.\n */\nexport function isNativeTokenAddress(address: string): boolean {\n return address.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase();\n}\n\n// ============ Error Codes ============\n\n/** API returned route paths where all paths have zero amounts */\nexport const ERR_ZERO_AMOUNT_PATHS = 4001;\n\n// ============ Protocol Types ============\n\nexport enum ProtocolType {\n PancakeV2 = \"PancakeV2\",\n PancakeV3 = \"PancakeV3\",\n PancakeInfinityCl = \"Pancake_Infinity_Cl\",\n UniswapV3 = \"UniswapV3\",\n UniswapV4 = \"UniswapV4\",\n Dodo = \"Dodo\",\n Thena = \"Thena\",\n}\n\n// ============ Configuration ============\n\nexport interface AdapterConfig {\n protocol: ProtocolType;\n address: string;\n}\n\nexport interface PeachConfig {\n chainId: number;\n rpcUrl: string;\n /** Router address override. If not provided, uses the address from API response. */\n routerAddress?: string;\n weth: string;\n adapters: AdapterConfig[];\n}\n\n// ============ Pool Info ============\n\nexport interface PoolInfo {\n address: string;\n token0: string;\n token1: string;\n protocol: ProtocolType;\n // V2 specific\n reserve0?: bigint;\n reserve1?: bigint;\n // V3 specific\n fee?: number;\n liquidity?: bigint;\n sqrtPriceX96?: bigint;\n tick?: number;\n}\n\n// ============ Routes ============\n\nexport interface RouteStep {\n pool: PoolInfo;\n tokenIn: string;\n tokenOut: string;\n amountIn: bigint;\n amountOut: bigint;\n}\n\nexport interface Route {\n steps: RouteStep[];\n amountIn: bigint;\n amountOut: bigint;\n gasEstimate: bigint;\n}\n\nexport interface SplitRoute {\n routes: Route[];\n percentages: number[]; // Percentage for each route (BPS, sum = 10000)\n totalAmountIn: bigint;\n totalAmountOut: bigint;\n totalGasEstimate: bigint;\n}\n\n// ============ Swap Parameters (Contract Format) ============\n\nexport interface SwapStep {\n adapter: string;\n pool: string;\n tokenIn: string;\n tokenOut: string;\n amountIn: bigint; // 0 means consume all\n extraData: string;\n}\n\nexport interface SwapParams {\n srcToken: string;\n dstToken: string;\n amountIn: bigint;\n amountOutMin: bigint;\n steps: SwapStep[];\n intermediateTokens: string[];\n deadline: bigint;\n quoteId: string;\n expectAmountOut: bigint;\n}\n\n// ============ Quote ============\n\nexport interface Quote {\n srcToken: string;\n dstToken: string;\n amountIn: bigint;\n amountOut: bigint;\n priceImpact: number;\n route: SplitRoute;\n params: SwapParams;\n gasEstimate: bigint;\n /** Router contract address from quote API (contracts.router). When set, simulate/execute use this instead of config.routerAddress. */\n routerAddress?: string;\n /** True when the original srcToken was the native token sentinel address */\n srcNative?: boolean;\n /** True when the original dstToken was the native token sentinel address */\n dstNative?: boolean;\n}\n\n// ============ Swap Result ============\n\nexport interface SwapResult {\n txHash: string;\n amountIn: bigint;\n amountOut: bigint;\n gasUsed: bigint;\n}\n\n// ============ BSC Mainnet Preset Config ============\n\nexport const BSC_MAINNET_CONFIG: PeachConfig = {\n chainId: 56,\n rpcUrl: \"https://bsc-dataseed.binance.org\",\n weth: \"0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c\", // WBNB\n adapters: [],\n};\n\n// ============ BSC Testnet Preset Config ============\n\nexport const BSC_TESTNET_CONFIG: PeachConfig = {\n chainId: 97,\n rpcUrl: \"https://bsc-testnet-rpc.publicnode.com\",\n weth: \"0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd\", // WBNB Testnet\n adapters: [],\n};\n\n// ============ API Request Types ============\n\n/**\n * Known DEX providers supported by the SDK.\n */\nexport type KnownProvider =\n | \"PANCAKEV2\"\n | \"PANCAKEV3\"\n | \"PANCAKE_INFINITY_CL\"\n | \"UNISWAPV3\"\n | \"UNISWAPV4\"\n | \"DODO\"\n | \"THENA\";\n\n/**\n * Supported DEX providers.\n * Use getAvailableProviders() to get the list dynamically.\n */\nexport type Provider = KnownProvider | (string & {});\n\n/**\n * API request parameters for find_routes\n * Note: The following parameters are ignored by SDK:\n * - liquidity_change\n * - apikey\n * - gas\n * - with_sign\n * - cal_path_limit\n */\nexport interface ApiFindRouteRequest {\n /** Input token address (required) */\n from: string;\n /** Output token address (required) */\n target: string;\n /** Trade amount, cannot be 0 (required) */\n amount: string;\n /** true: calculate output from input; false: calculate input from output (default: true) */\n by_amount_in?: boolean;\n /** Route search depth / max hops (default: 3) */\n depth?: number;\n /** Trade split count for large trades optimization (default: 20) */\n split_count?: number;\n /** DEX providers, comma-separated (default: \"PANCAKEV2,PANCAKEV3\") */\n providers?: string;\n /** Client version (required >= 1001500 for V3) */\n v?: number;\n}\n\n/**\n * SDK-level quote request options\n */\nexport interface QuoteOptions {\n /** true: calculate output from input; false: calculate input from output (default: true) */\n byAmountIn?: boolean;\n /** Route search depth / max hops (default: 3) */\n depth?: number;\n /** Trade split count for large trades optimization (default: 20) */\n splitCount?: number;\n /** DEX providers to use (default: [\"PANCAKEV2\", \"PANCAKEV3\"]) */\n providers?: Provider[];\n /** Transaction deadline in seconds from now (default: 1200 = 20 min) */\n deadlineSeconds?: number;\n}\n\n// ============ Execute Options ============\n\nexport type ExecuteTimeoutStage = \"wallet_send\" | \"provider_index\";\n\nexport class ExecuteTimeoutError extends Error {\n readonly txHash?: string;\n readonly stage: ExecuteTimeoutStage;\n\n constructor(message: string, stage: ExecuteTimeoutStage, txHash?: string) {\n super(message);\n this.name = \"ExecuteTimeoutError\";\n this.stage = stage;\n this.txHash = txHash;\n }\n}\n\nexport interface SwapOptions {\n /** Slippage tolerance in basis points (e.g. 50 = 0.5%). Required. */\n slippageBps: number;\n /** Gas price in wei. Applied to the swap tx and, if present, the approval tx. */\n gasPrice?: bigint;\n /** Gas limit for the swap tx. */\n gasLimit?: bigint;\n}\n\nexport interface SwapTxRequest {\n to: string;\n data: string;\n value: bigint;\n gasPrice?: bigint;\n gasLimit?: bigint;\n}\n\nexport interface SwapApprovalRequest {\n token: string;\n owner: string;\n spender: string;\n currentAllowance: bigint;\n requiredAmount: bigint;\n approveAmount: bigint;\n tx: SwapTxRequest;\n}\n\nexport interface SwapRequest {\n routerAddress: string;\n method: \"swap\" | \"swapETH\";\n tx: SwapTxRequest;\n approval?: SwapApprovalRequest;\n}\n\nexport interface ExecuteOptions extends SwapOptions {\n /** Skip the preflight eth_call simulation before sending (default: false).\n * Set to true only if you've already called simulate() and confirmed the route is valid. */\n skipPreflight?: boolean;\n /** Timeout in milliseconds for the wallet to sign & broadcast the transaction.\n * If the wallet does not settle the Promise within this duration, execute() rejects\n * with a timeout error so the caller is not stuck on \"Pending Wallet Signature\" forever.\n * Default: 60_000 (60 seconds). Set to 0 to disable. */\n timeoutMs?: number;\n /** Polling intervals for getTransaction(hash) after the wallet returns a tx hash.\n * Defaults to [50, 100, 200, 400, 800, 1200]ms and then repeats the last value. */\n transactionResponsePollingIntervalsMs?: readonly number[];\n}\n\n/** Result of findFailingStep: which step index and step caused the revert */\nexport interface FindFailingStepResult {\n stepIndex: number;\n step: SwapStep;\n error: unknown;\n /** Revert reason when simulating only up to this step (may differ from full route) */\n revertMessage?: string;\n /** Revert reason from the full-route simulation, if fullRouteError was passed */\n fullRouteRevertMessage?: string;\n}\n\n/**\n * Default values for API parameters\n */\nexport const API_DEFAULTS = {\n /** Default route search depth */\n depth: 3,\n /** Default trade split count */\n splitCount: 20,\n /** Default DEX providers */\n providers: [\"PANCAKEV2\", \"PANCAKEV3\", \"PANCAKE_INFINITY_CL\", \"UNISWAPV3\", \"UNISWAPV4\", \"DODO\", \"THENA\"] as Provider[],\n /** Default client version for V3 API */\n clientVersion: 1001500,\n} as const;\n\n// ============ API Response Types ============\n\n/**\n * Aggregator API response wrapper\n */\nexport interface ApiResponse<T> {\n code: number;\n msg: string;\n data: T;\n}\n\n/**\n * Chainflow sync status for a provider\n */\nexport interface ChainflowStatus {\n /** Provider name (e.g., \"PANCAKEV3\") */\n provider: string;\n /** Current sync transaction cursor (tx hash) */\n tx_cursor: string | null;\n /** Sync version info */\n version: {\n /** Latest synced block number */\n latest_block_number: number;\n /** Latest synced transaction index in that block */\n latest_transaction_index: number;\n };\n /** Last update timestamp in milliseconds */\n update_at: number;\n}\n\n/**\n * Status API response data\n */\nexport interface ApiStatusData {\n /** Available liquidity providers */\n providers: string[];\n /** Chain sync status for each provider */\n chainflows: ChainflowStatus[];\n}\n\n/**\n * Full status response\n */\nexport type ApiStatusResponse = ApiResponse<ApiStatusData>;\n\n/**\n * Contract addresses for EVM\n */\nexport interface ApiContractAddresses {\n /** PeachAggregator router address */\n router: string;\n /** Adapter addresses by provider name */\n adapters: Record<string, string>;\n}\n\n/**\n * Route path from aggregator API (EVM format)\n */\nexport interface ApiRoutePath {\n /** Pool contract address */\n pool: string;\n /** Provider name (e.g., \"PANCAKEV3\") */\n provider: Provider;\n /** Adapter contract address */\n adapter: string;\n /** Input token address */\n token_in: string;\n /** Output token address */\n token_out: string;\n /** Swap direction (true = token0 -> token1) */\n direction: boolean;\n /** Fee rate (e.g., \"0.0005\" for 0.05%) */\n fee_rate: string;\n /** Input amount (string to support u128) */\n amount_in: string;\n /** Output amount (string to support u128) */\n amount_out: string;\n /** Extra data for adapter (hex encoded) */\n extra_data?: string;\n}\n\n/**\n * Find route response data\n */\nexport interface ApiFindRouteData {\n request_id: string;\n /** Total input amount (string to support u128) */\n amount_in: string;\n /** Total output amount (string to support u128) */\n amount_out: string;\n deviation_ratio: string;\n paths: ApiRoutePath[];\n /** Contract addresses for building transactions */\n contracts: ApiContractAddresses;\n /** Estimated gas */\n gas: number;\n}\n\n/**\n * Full find route response\n */\nexport type ApiFindRouteResponse = ApiResponse<ApiFindRouteData>;\n","/**\n * ApiClient - Peach Aggregator API client\n *\n * Route discovery and status access via the aggregator API.\n */\n\nimport {\n ApiFindRouteResponse,\n ApiFindRouteData,\n ApiStatusResponse,\n ApiStatusData,\n API_DEFAULTS,\n DEFAULT_API_URL,\n Provider,\n} from \"../types\";\n\nexport interface ApiClientConfig {\n /** API base URL (default: https://api.peach.ag) */\n baseUrl?: string;\n /** Request timeout in ms (default: 10000) */\n timeout?: number;\n}\n\nconst DEFAULT_TIMEOUT = 10000;\n\nexport class ApiClient {\n private baseUrl: string;\n private timeout: number;\n\n constructor(config: ApiClientConfig = {}) {\n this.baseUrl = config.baseUrl || DEFAULT_API_URL;\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n }\n\n /**\n * Find optimal routes via API\n */\n async findRoutes(params: {\n from: string;\n target: string;\n amount: bigint;\n byAmountIn?: boolean;\n depth?: number;\n splitCount?: number;\n providers?: Provider[];\n }): Promise<ApiFindRouteData> {\n const {\n from,\n target,\n amount,\n byAmountIn = true,\n depth = API_DEFAULTS.depth,\n splitCount = API_DEFAULTS.splitCount,\n providers = API_DEFAULTS.providers,\n } = params;\n\n const queryParams = new URLSearchParams({\n from,\n target,\n amount: amount.toString(),\n by_amount_in: byAmountIn.toString(),\n depth: depth.toString(),\n split_count: splitCount.toString(),\n providers: providers.join(\",\"),\n v: API_DEFAULTS.clientVersion.toString(),\n });\n\n const url = `${this.baseUrl}/router/find_routes?${queryParams}`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n },\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new ApiError(\n `API request failed: ${response.status} ${response.statusText}`,\n response.status\n );\n }\n\n const json = (await response.json()) as ApiFindRouteResponse;\n\n if (json.code !== 200) {\n throw new ApiError(json.msg || \"Route not found\", json.code);\n }\n\n if (!json.data || !json.data.paths || json.data.paths.length === 0) {\n throw new ApiError(\"No routes found\", 404);\n }\n\n return json.data;\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new ApiError(\"API request timeout\", 408);\n }\n throw new ApiError(`API request failed: ${error.message}`, 0);\n }\n\n throw new ApiError(\"Unknown API error\", 0);\n }\n }\n\n /**\n * Get service status including available providers\n */\n async getStatus(): Promise<ApiStatusData> {\n const url = `${this.baseUrl}/router/status`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n },\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new ApiError(\n `API request failed: ${response.status} ${response.statusText}`,\n response.status\n );\n }\n\n const json = (await response.json()) as ApiStatusResponse;\n\n if (json.code !== 200) {\n throw new ApiError(json.msg || \"Failed to get status\", json.code);\n }\n\n return json.data;\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new ApiError(\"API request timeout\", 408);\n }\n throw new ApiError(`API request failed: ${error.message}`, 0);\n }\n\n throw new ApiError(\"Unknown API error\", 0);\n }\n }\n\n /**\n * Get list of available providers\n */\n async getAvailableProviders(): Promise<string[]> {\n const status = await this.getStatus();\n return status.providers;\n }\n\n /**\n * Update API base URL\n */\n setBaseUrl(url: string): void {\n this.baseUrl = url;\n }\n\n /**\n * Get current API base URL\n */\n getBaseUrl(): string {\n return this.baseUrl;\n }\n}\n\n/**\n * API error class\n */\nexport class ApiError extends Error {\n public readonly code: number;\n\n constructor(message: string, code: number) {\n super(message);\n this.name = \"ApiError\";\n this.code = code;\n }\n}\n","import { DEFAULT_EXECUTE_TIMEOUT_MS, ExecuteTimeoutError } from \"../types\";\n\nexport async function withWalletSendTimeout<T>(\n promise: Promise<T>,\n timeoutMs = DEFAULT_EXECUTE_TIMEOUT_MS\n): Promise<T> {\n if (timeoutMs <= 0) {\n return promise;\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(\n new ExecuteTimeoutError(\n `Wallet did not settle sendTransaction within ${timeoutMs}ms.`,\n \"wallet_send\"\n )\n );\n }, timeoutMs);\n }),\n ]);\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n}\n","/**\n * PeachClient - Peach Aggregator SDK main entry point\n *\n * Uses the aggregator API for route discovery.\n */\n\nimport { ethers } from \"ethers\";\nimport {\n PeachConfig,\n Quote,\n SwapParams,\n SwapStep,\n SplitRoute,\n ProtocolType,\n BPS_DENOMINATOR,\n DEFAULT_DEADLINE_SECONDS,\n DEFAULT_EXECUTE_TIMEOUT_MS,\n DEFAULT_TRANSACTION_RESPONSE_POLL_INTERVALS_MS,\n QuoteOptions,\n SwapOptions,\n SwapApprovalRequest,\n SwapRequest,\n SwapTxRequest,\n ExecuteTimeoutError,\n ExecuteOptions,\n API_DEFAULTS,\n ApiFindRouteData,\n ApiRoutePath,\n KnownProvider,\n Provider,\n FindFailingStepResult,\n isNativeTokenAddress,\n} from \"../types\";\nimport { ApiClient, ApiClientConfig, ApiError } from \"./ApiClient\";\nimport { withWalletSendTimeout } from \"../utils/wallet\";\n\n// PeachRouter ABI (simplified)\nconst PEACH_ROUTER_ABI = [\n \"function swap((address srcToken, address dstToken, uint256 amountIn, uint256 amountOutMin, (address adapter, address pool, address tokenIn, address tokenOut, uint256 amountIn, bytes extraData)[] steps, address[] intermediateTokens, uint256 deadline, bytes32 quoteId, uint256 expectAmountOut) params) external returns (uint256 amountOut)\",\n \"function swapETH((address srcToken, address dstToken, uint256 amountIn, uint256 amountOutMin, (address adapter, address pool, address tokenIn, address tokenOut, uint256 amountIn, bytes extraData)[] steps, address[] intermediateTokens, uint256 deadline, bytes32 quoteId, uint256 expectAmountOut) params) external payable returns (uint256 amountOut)\",\n \"function isAdapterRegistered(address adapter) external view returns (bool)\",\n \"function WETH() external view returns (address)\",\n];\n\nconst ERC20_ABI = [\n \"function approve(address spender, uint256 amount) external returns (bool)\",\n \"function allowance(address owner, address spender) external view returns (uint256)\",\n \"function balanceOf(address account) external view returns (uint256)\",\n \"function decimals() external view returns (uint8)\",\n \"function symbol() external view returns (string)\",\n];\n\ntype EncodedSwapStep = {\n adapter: string;\n pool: string;\n tokenIn: string;\n tokenOut: string;\n amountIn: bigint;\n extraData: string;\n};\n\ntype EncodedSwapParams = {\n srcToken: string;\n dstToken: string;\n amountIn: bigint;\n amountOutMin: bigint;\n steps: EncodedSwapStep[];\n intermediateTokens: string[];\n deadline: bigint;\n quoteId: string;\n expectAmountOut: bigint;\n};\n\n/** BSC CLPoolManager contract address for Pancake Infinity (V4). */\nconst PANCAKE_INFINITY_CL_POOL_MANAGER = \"0xa0FfB9c1CE1Fe56963B0321B32E7A0302114058b\";\n\nconst PROVIDER_TO_PROTOCOL: Record<KnownProvider, ProtocolType> = {\n PANCAKEV3: ProtocolType.PancakeV3,\n PANCAKEV2: ProtocolType.PancakeV2,\n PANCAKE_INFINITY_CL: ProtocolType.PancakeInfinityCl,\n UNISWAPV3: ProtocolType.UniswapV3,\n UNISWAPV4: ProtocolType.UniswapV4,\n DODO: ProtocolType.Dodo,\n THENA: ProtocolType.Thena,\n};\n\nexport interface PeachClientOptions {\n /** API client configuration. Required for getQuote(). */\n api?: ApiClientConfig;\n}\n\nexport class PeachClient {\n private provider: ethers.Provider;\n private config: PeachConfig;\n private routerContract: ethers.Contract;\n private apiClient: ApiClient;\n\n constructor(\n config: PeachConfig,\n provider?: ethers.Provider,\n options?: PeachClientOptions\n ) {\n this.config = config;\n this.provider = provider || new ethers.JsonRpcProvider(config.rpcUrl);\n // routerContract is used for ABI encoding/decoding; address may be overridden per-quote\n this.routerContract = new ethers.Contract(\n config.routerAddress || ethers.ZeroAddress,\n PEACH_ROUTER_ABI,\n this.provider\n );\n\n // Create API client (uses default API URL if no baseUrl provided)\n this.apiClient = new ApiClient(options?.api);\n }\n\n /**\n * Get the effective router address for a quote.\n */\n private getRouterAddress(quote: Quote): string {\n const addr = quote.routerAddress || this.config.routerAddress;\n if (!addr || addr === ethers.ZeroAddress) {\n throw new Error(\"No router address available. Provide routerAddress in config or use API-based getQuote.\");\n }\n return addr;\n }\n\n /**\n * Apply slippage to swap params, returning a new SwapParams with adjusted amountOutMin\n */\n private applySlippage(params: SwapParams, slippageBps: number): SwapParams {\n if (slippageBps < 0 || slippageBps > 10000) {\n throw new Error(\"slippageBps must be between 0 and 10000\");\n }\n const amountOutMin =\n (params.amountOutMin * (BPS_DENOMINATOR - BigInt(slippageBps))) / BPS_DENOMINATOR;\n return { ...params, amountOutMin };\n }\n\n /**\n * Build transaction requests for an approval (if needed) and the swap itself.\n */\n async swap(\n quote: Quote,\n ownerAddress: string,\n options: SwapOptions\n ): Promise<SwapRequest> {\n const routerAddress = this.getRouterAddress(quote);\n const { tx, method } = this.buildSwapTransactionRequest(quote, options);\n\n let approval: SwapApprovalRequest | undefined;\n if (!quote.srcNative) {\n approval = await this.buildApprovalRequest(\n quote.srcToken,\n ownerAddress,\n quote.amountIn,\n routerAddress,\n options\n );\n }\n\n return {\n routerAddress,\n method,\n tx,\n approval,\n };\n }\n\n /**\n * Execute swap using the legacy signer-managed flow.\n *\n * @deprecated Prefer swap(), then send the returned tx request with your wallet/client.\n */\n async execute(\n quote: Quote,\n signer: ethers.Signer,\n options: ExecuteOptions\n ): Promise<ethers.TransactionResponse> {\n const signerAddress = await signer.getAddress();\n const prepared = await this.swap(quote, signerAddress, options);\n\n // Preflight: run eth_call before sending to surface clear revert reasons instead of\n // the opaque \"cannot estimate gas\" error that ethers throws when estimateGas fails.\n // if (!options.skipPreflight) {\n // await this.simulate(quote, options.slippageBps, signerAddress);\n // }\n\n try {\n if (prepared.approval) {\n const approvalTx = await this.sendTransactionWithTimeout(\n signer,\n prepared.approval.tx,\n options\n );\n await approvalTx.wait();\n }\n\n return await this.sendTransactionWithTimeout(signer, prepared.tx, options);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n const isEstimateGas = /estimateGas/i.test(msg);\n const isMissingRevertData =\n /missing revert data/i.test(msg) ||\n (msg.includes(\"reason=null\") && msg.includes(\"data=null\"));\n if (isEstimateGas && (isMissingRevertData || /reason=null|data=null/.test(msg))) {\n const friendly =\n \"Transaction reverted during gas estimation and the RPC did not return a revert reason. Try: 1) Get a fresh quote and confirm immediately 2) Switch network or RPC 3) Increase slippage.\";\n const e = new Error(`${friendly} (estimateGas/missing revert data)`) as Error & { cause?: unknown };\n e.cause = err;\n throw e;\n }\n throw err;\n }\n }\n\n /**\n * Encode parameters to contract format\n */\n private encodeParams(params: SwapParams): EncodedSwapParams {\n return {\n srcToken: params.srcToken,\n dstToken: params.dstToken,\n amountIn: params.amountIn,\n amountOutMin: params.amountOutMin,\n steps: params.steps.map((step) => ({\n adapter: step.adapter,\n pool: step.pool,\n tokenIn: step.tokenIn,\n tokenOut: step.tokenOut,\n amountIn: step.amountIn,\n extraData: step.extraData,\n })),\n intermediateTokens: params.intermediateTokens,\n deadline: params.deadline,\n quoteId: params.quoteId,\n expectAmountOut: params.expectAmountOut,\n };\n }\n\n private getProtocolForProvider(provider: Provider): ProtocolType {\n if (provider in PROVIDER_TO_PROTOCOL) {\n return PROVIDER_TO_PROTOCOL[provider as KnownProvider];\n }\n throw new Error(`Unsupported provider: ${provider}`);\n }\n\n /**\n * Encode swap calldata for the Peach Aggregator contract\n * Useful for simulation or building custom transactions\n *\n * @param quote - Quote object from getQuote\n * @param slippageBps - Slippage tolerance in basis points (e.g. 50 = 0.5%). Required.\n * @returns Encoded calldata and transaction info (to address, value)\n */\n encodeSwapCalldata(quote: Quote, slippageBps: number): {\n to: string;\n data: string;\n value: bigint;\n method: 'swap' | 'swapETH';\n } {\n const routerAddress = quote.routerAddress ?? this.config.routerAddress ?? this.routerContract.target as string;\n const swapParams = this.applySlippage(quote.params, slippageBps);\n const useSwapETH = quote.srcNative === true || quote.dstNative === true;\n const encodedParams = this.encodeParams(swapParams);\n\n if (useSwapETH) {\n const data = this.routerContract.interface.encodeFunctionData('swapETH', [encodedParams]);\n return {\n to: routerAddress,\n data,\n value: quote.srcNative ? quote.amountIn : 0n,\n method: 'swapETH',\n };\n } else {\n const data = this.routerContract.interface.encodeFunctionData('swap', [encodedParams]);\n return {\n to: routerAddress,\n data,\n value: 0n,\n method: 'swap',\n };\n }\n }\n\n private buildSwapTransactionRequest(\n quote: Quote,\n options: SwapOptions\n ): { tx: SwapTxRequest; method: \"swap\" | \"swapETH\" } {\n const { to, data, value, method } = this.encodeSwapCalldata(quote, options.slippageBps);\n return {\n method,\n tx: this.applyTxOverrides({ to, data, value }, options, true),\n };\n }\n\n private async buildApprovalRequest(\n token: string,\n owner: string,\n amount: bigint,\n spender: string,\n options: SwapOptions\n ): Promise<SwapApprovalRequest | undefined> {\n const allowance = await this.getAllowance(token, owner, spender);\n if (allowance >= amount) {\n return undefined;\n }\n\n return {\n token,\n owner,\n spender,\n currentAllowance: allowance,\n requiredAmount: amount,\n approveAmount: ethers.MaxUint256,\n tx: this.buildApprovalTransactionRequest(token, spender, options),\n };\n }\n\n private buildApprovalTransactionRequest(\n token: string,\n spender: string,\n options: SwapOptions\n ): SwapTxRequest {\n const tokenInterface = new ethers.Interface(ERC20_ABI);\n const data = tokenInterface.encodeFunctionData(\"approve\", [spender, ethers.MaxUint256]);\n return this.applyTxOverrides({ to: token, data, value: 0n }, options, false);\n }\n\n private async getAllowance(\n token: string,\n owner: string,\n spender: string\n ): Promise<bigint> {\n const tokenContract = new ethers.Contract(token, ERC20_ABI, this.provider);\n return tokenContract.allowance(owner, spender);\n }\n\n private applyTxOverrides(\n tx: SwapTxRequest,\n options: SwapOptions,\n includeGasLimit: boolean\n ): SwapTxRequest {\n const txWithOverrides: SwapTxRequest = { ...tx };\n if (options.gasPrice) {\n txWithOverrides.gasPrice = options.gasPrice;\n }\n if (includeGasLimit && options.gasLimit) {\n txWithOverrides.gasLimit = options.gasLimit;\n }\n return txWithOverrides;\n }\n\n private async sendTransactionWithTimeout(\n signer: ethers.Signer,\n tx: SwapTxRequest,\n options: Pick<ExecuteOptions, \"timeoutMs\" | \"transactionResponsePollingIntervalsMs\">\n ): Promise<ethers.TransactionResponse> {\n const effectiveTimeoutMs = options.timeoutMs ?? DEFAULT_EXECUTE_TIMEOUT_MS;\n if (effectiveTimeoutMs <= 0) {\n return signer.sendTransaction(tx);\n }\n\n const uncheckedSigner = signer as ethers.Signer & {\n sendUncheckedTransaction?: (tx: ethers.TransactionRequest) => Promise<string>;\n provider?: ethers.Provider | null;\n };\n\n if (\n typeof uncheckedSigner.sendUncheckedTransaction === \"function\" &&\n uncheckedSigner.provider\n ) {\n const hash = await withWalletSendTimeout(\n uncheckedSigner.sendUncheckedTransaction(tx),\n effectiveTimeoutMs\n );\n\n const pollResult = await this.waitForTransactionResponse(\n uncheckedSigner.provider,\n hash,\n effectiveTimeoutMs,\n options.transactionResponsePollingIntervalsMs\n );\n if (pollResult.response) {\n return pollResult.response;\n }\n\n const failureMode =\n pollResult.rpcErrors > 0\n ? `${pollResult.rpcErrors} transient provider error(s) and ${pollResult.nullResponses} null response(s)`\n : `${pollResult.nullResponses} null response(s)`;\n throw new ExecuteTimeoutError(\n `Transaction was broadcast but provider did not return TransactionResponse within ${effectiveTimeoutMs}ms (${failureMode}).`,\n \"provider_index\",\n hash\n );\n }\n\n return withWalletSendTimeout(signer.sendTransaction(tx), effectiveTimeoutMs);\n }\n\n private async waitForTransactionResponse(\n provider: ethers.Provider,\n hash: string,\n timeoutMs: number,\n pollingIntervalsMs: readonly number[] = DEFAULT_TRANSACTION_RESPONSE_POLL_INTERVALS_MS\n ): Promise<{\n response: ethers.TransactionResponse | null;\n nullResponses: number;\n rpcErrors: number;\n }> {\n const deadline = Date.now() + timeoutMs;\n let attempt = 0;\n let nullResponses = 0;\n let rpcErrors = 0;\n while (Date.now() < deadline) {\n try {\n const response = await provider.getTransaction(hash);\n if (response) {\n return { response, nullResponses, rpcErrors };\n }\n nullResponses++;\n } catch {\n // Some wallets/RPCs lag while indexing a just-broadcast tx; keep polling until timeout.\n rpcErrors++;\n }\n const nextDelay = this.getNextPollingDelay(pollingIntervalsMs, attempt);\n attempt++;\n await this.delay(Math.min(nextDelay, Math.max(25, deadline - Date.now())));\n }\n return { response: null, nullResponses, rpcErrors };\n }\n\n private async delay(ms: number): Promise<void> {\n if (ms <= 0) {\n return;\n }\n await new Promise<void>((resolve) => {\n setTimeout(resolve, ms);\n });\n }\n\n private getNextPollingDelay(\n pollingIntervalsMs: readonly number[],\n attempt: number\n ): number {\n if (pollingIntervalsMs.length === 0) {\n return DEFAULT_TRANSACTION_RESPONSE_POLL_INTERVALS_MS.at(-1) ?? 1200;\n }\n return pollingIntervalsMs[Math.min(attempt, pollingIntervalsMs.length - 1)] ?? 1200;\n }\n\n /**\n * Get token metadata and, optionally, the balance for a specific owner.\n */\n async getTokenInfo(tokenAddress: string, ownerAddress?: string): Promise<{\n symbol: string;\n decimals: number;\n balance?: bigint;\n }> {\n const token = new ethers.Contract(tokenAddress, ERC20_ABI, this.provider);\n const [symbol, decimals, balance] = await Promise.all([\n token.symbol(),\n token.decimals(),\n ownerAddress ? token.balanceOf(ownerAddress) : Promise.resolve(undefined),\n ]);\n\n return balance === undefined ? { symbol, decimals } : { symbol, decimals, balance };\n }\n\n /**\n * Get user token balance\n */\n async getBalance(tokenAddress: string, userAddress: string): Promise<bigint> {\n const token = new ethers.Contract(tokenAddress, ERC20_ABI, this.provider);\n return token.balanceOf(userAddress);\n }\n\n /**\n * Get quote via API\n * @throws Error if API client is not configured\n */\n async getQuote(params: {\n srcToken: string;\n dstToken: string;\n amountIn: bigint;\n options?: QuoteOptions;\n }): Promise<Quote> {\n const { srcToken, dstToken, amountIn, options = {} } = params;\n\n const {\n byAmountIn = true,\n depth = API_DEFAULTS.depth,\n splitCount = API_DEFAULTS.splitCount,\n providers = API_DEFAULTS.providers,\n deadlineSeconds = DEFAULT_DEADLINE_SECONDS,\n } = options;\n\n // Detect native token sentinel and convert to WBNB for API\n const srcNative = isNativeTokenAddress(srcToken);\n const dstNative = isNativeTokenAddress(dstToken);\n const apiSrcToken = srcNative ? this.config.weth : srcToken;\n const apiDstToken = dstNative ? this.config.weth : dstToken;\n\n const apiData = await this.apiClient.findRoutes({\n from: apiSrcToken,\n target: apiDstToken,\n amount: amountIn,\n byAmountIn,\n depth,\n splitCount,\n providers,\n });\n\n return this.buildQuoteFromApi(apiData, apiSrcToken, apiDstToken, deadlineSeconds, providers, srcNative, dstNative);\n }\n\n /**\n * Filter paths by allowed providers, removing paths with disallowed providers\n * and cascade-removing orphaned paths that depend on removed paths.\n */\n private filterPathsByProviders(\n paths: ApiRoutePath[],\n allowedProviders: Provider[],\n srcToken: string,\n dstToken: string\n ): ApiRoutePath[] {\n const allowedSet = new Set(allowedProviders.map((p) => p.toUpperCase()));\n\n // Step 1: Remove paths with disallowed providers\n let filtered = paths.filter((p) => allowedSet.has(p.provider.toUpperCase()));\n\n if (filtered.length === paths.length) {\n return filtered; // Nothing was removed\n }\n\n // Step 2: Cascade-remove orphaned paths via reachability analysis\n const srcLower = srcToken.toLowerCase();\n const dstLower = dstToken.toLowerCase();\n\n // Forward reachability: which tokens can be reached from srcToken?\n const reachableFromSrc = new Set<string>();\n reachableFromSrc.add(srcLower);\n let changed = true;\n while (changed) {\n changed = false;\n for (const p of filtered) {\n if (\n reachableFromSrc.has(p.token_in.toLowerCase()) &&\n !reachableFromSrc.has(p.token_out.toLowerCase())\n ) {\n reachableFromSrc.add(p.token_out.toLowerCase());\n changed = true;\n }\n }\n }\n\n // Backward reachability: which tokens can reach dstToken?\n const reachableToDst = new Set<string>();\n reachableToDst.add(dstLower);\n changed = true;\n while (changed) {\n changed = false;\n for (const p of filtered) {\n if (\n reachableToDst.has(p.token_out.toLowerCase()) &&\n !reachableToDst.has(p.token_in.toLowerCase())\n ) {\n reachableToDst.add(p.token_in.toLowerCase());\n changed = true;\n }\n }\n }\n\n // Keep only paths on valid src→dst routes\n const beforeOrphan = filtered.length;\n filtered = filtered.filter(\n (p) =>\n reachableFromSrc.has(p.token_in.toLowerCase()) &&\n reachableToDst.has(p.token_out.toLowerCase())\n );\n\n return filtered;\n }\n\n /**\n * Build Quote from route data (e.g. from JSON file or API response).\n * Use this to simulate a swap from a pre-computed route without calling the API.\n *\n * @param data - Route data with paths, amount_in, amount_out, contracts, gas\n * @param srcToken - Source token address (first path token_in)\n * @param dstToken - Destination token address (last path token_out)\n * @param deadlineSeconds - Optional deadline in seconds from now (default: 20 min)\n */\n buildQuoteFromRouteData(\n data: ApiFindRouteData,\n srcToken: string,\n dstToken: string,\n deadlineSeconds?: number,\n options?: { srcNative?: boolean; dstNative?: boolean }\n ): Quote {\n const quote = this.buildQuoteFromRouteDataInternal(\n data,\n srcToken,\n dstToken,\n deadlineSeconds ?? DEFAULT_DEADLINE_SECONDS,\n undefined\n );\n if (options?.srcNative) quote.srcNative = true;\n if (options?.dstNative) quote.dstNative = true;\n return quote;\n }\n\n /**\n * Build Quote from API response\n */\n private buildQuoteFromApi(\n data: ApiFindRouteData,\n srcToken: string,\n dstToken: string,\n deadlineSeconds: number,\n requestedProviders?: Provider[],\n srcNative?: boolean,\n dstNative?: boolean\n ): Quote {\n const quote = this.buildQuoteFromRouteDataInternal(\n data,\n srcToken,\n dstToken,\n deadlineSeconds,\n requestedProviders\n );\n if (srcNative) quote.srcNative = true;\n if (dstNative) quote.dstNative = true;\n return quote;\n }\n\n private buildQuoteFromRouteDataInternal(\n data: ApiFindRouteData,\n srcToken: string,\n dstToken: string,\n deadlineSeconds: number,\n requestedProviders?: Provider[]\n ): Quote {\n // Filter out zero-amount dead paths (amount_in=0 AND not an entry point)\n const srcTokenLower = srcToken.toLowerCase();\n const originalPathCount = data.paths.length;\n let validPaths = data.paths.filter((p) => {\n const isEntryPoint = p.token_in.toLowerCase() === srcTokenLower;\n if (!isEntryPoint && BigInt(p.amount_in) === 0n && BigInt(p.amount_out) === 0n) {\n return false;\n }\n return true;\n });\n\n if (validPaths.length === 0) {\n throw new ApiError(\"All route paths have zero amounts\", 4001);\n }\n\n // Filter by requested providers (defensive check against API returning unwanted providers)\n if (requestedProviders && requestedProviders.length > 0) {\n validPaths = this.filterPathsByProviders(validPaths, requestedProviders, srcToken, dstToken);\n\n if (validPaths.length === 0) {\n throw new ApiError(\"No valid route paths remaining after provider filtering\", 4001);\n }\n }\n\n // Recalculate amounts from remaining valid paths\n const dstTokenLower = dstToken.toLowerCase();\n let amountIn = 0n;\n let amountOut = 0n;\n for (const p of validPaths) {\n if (p.token_in.toLowerCase() === srcTokenLower) {\n amountIn += BigInt(p.amount_in);\n }\n if (p.token_out.toLowerCase() === dstTokenLower) {\n amountOut += BigInt(p.amount_out);\n }\n }\n const deadline = BigInt(\n Math.floor(Date.now() / 1000) + deadlineSeconds\n );\n\n // Count how many steps consume each tokenIn.\n // When multiple steps share the same tokenIn, all but the last must use an\n // explicit amountIn (from the API) so the router deducts a fixed amount from\n // transient storage instead of consumeAll-ing the entire balance on the first\n // step and leaving nothing for the rest. The last consumer keeps amountIn=0\n // so it takes whatever remainder is left (handles rounding).\n const tokenInCount = new Map<string, number>();\n for (const p of validPaths) {\n const key = p.token_in.toLowerCase();\n tokenInCount.set(key, (tokenInCount.get(key) ?? 0) + 1);\n }\n const tokenInSeen = new Map<string, number>();\n const steps: SwapStep[] = validPaths.map((p) => {\n const key = p.token_in.toLowerCase();\n const seen = (tokenInSeen.get(key) ?? 0) + 1;\n tokenInSeen.set(key, seen);\n const total = tokenInCount.get(key)!;\n // Use explicit amountIn when there are multiple consumers of this token\n // AND this is not the last consumer (last one uses consumeAll=0 for remainder).\n const useExplicit = total > 1 && seen < total;\n const isEntryPoint = key === srcTokenLower;\n return {\n adapter: p.adapter,\n pool: p.provider.toUpperCase() === \"PANCAKE_INFINITY_CL\"\n ? PANCAKE_INFINITY_CL_POOL_MANAGER\n : p.provider.toUpperCase() === \"UNISWAPV4\"\n ? ethers.ZeroAddress\n : p.pool,\n tokenIn: p.token_in,\n tokenOut: p.token_out,\n amountIn: isEntryPoint || useExplicit ? BigInt(p.amount_in) : 0n,\n extraData: p.extra_data || \"0x\",\n };\n });\n\n const intermediateTokenSet = new Set<string>();\n for (const p of validPaths) {\n const out = p.token_out.toLowerCase();\n if (out !== dstTokenLower) {\n intermediateTokenSet.add(p.token_out);\n }\n }\n const intermediateTokens = Array.from(intermediateTokenSet);\n\n const swapParams: SwapParams = {\n srcToken,\n dstToken,\n amountIn,\n amountOutMin: amountOut,\n steps,\n intermediateTokens,\n deadline,\n quoteId: data.request_id ? ethers.id(data.request_id).slice(0, 66) : ethers.ZeroHash,\n expectAmountOut: amountOut,\n };\n\n const route: SplitRoute = {\n routes: [\n {\n steps: validPaths.map((p) => ({\n pool: {\n address: p.pool,\n token0: p.token_in,\n token1: p.token_out,\n protocol: this.getProtocolForProvider(p.provider),\n fee: p.fee_rate\n ? Math.round(parseFloat(p.fee_rate) * 1_000_000)\n : undefined,\n },\n tokenIn: p.token_in,\n tokenOut: p.token_out,\n amountIn: BigInt(p.amount_in),\n amountOut: BigInt(p.amount_out),\n })),\n amountIn,\n amountOut,\n gasEstimate: BigInt(data.gas),\n },\n ],\n percentages: [10000],\n totalAmountIn: amountIn,\n totalAmountOut: amountOut,\n totalGasEstimate: BigInt(data.gas),\n };\n\n if (!data.contracts?.router) {\n throw new ApiError(\"API response missing contracts.router address\", 4002);\n }\n\n return {\n srcToken,\n dstToken,\n amountIn,\n amountOut,\n priceImpact: parseFloat(data.deviation_ratio || \"0\"),\n route,\n params: swapParams,\n gasEstimate: BigInt(data.gas),\n routerAddress: data.contracts?.router,\n };\n }\n\n /**\n * Get available providers from the API\n * @throws Error if API client is not configured\n */\n async getAvailableProviders(): Promise<string[]> {\n return this.apiClient.getAvailableProviders();\n }\n\n /**\n * Simulate swap via eth_call (no gas, no state change)\n * Useful for testing and verifying quote accuracy\n *\n * @param quote - Quote object from getQuote\n * @param slippageBps - Slippage tolerance in basis points (e.g. 50 = 0.5%). Required.\n * @param fromAddress - Optional caller address for simulation (default: zero address)\n * @param stateOverrides - Optional state overrides for ERC20 balance/allowance\n * @returns Simulated amountOut and method used\n */\n async simulate(\n quote: Quote,\n slippageBps: number,\n fromAddress?: string,\n stateOverrides?: Record<string, { stateDiff: Record<string, string> }>\n ): Promise<{ amountOut: bigint; method: 'swap' | 'swapETH' }> {\n const caller = fromAddress || ethers.ZeroAddress;\n const { to, data, value, method } = this.encodeSwapCalldata(quote, slippageBps);\n\n console.log('[PeachClient] simulate using router:', to);\n\n if (stateOverrides) {\n // Use JsonRpcProvider for state overrides. Some RPCs (Go hexutil.Big) reject hex with leading zeros.\n const jsonRpcProvider = this.getJsonRpcProviderForStateOverrides();\n const valueHex = value > 0n ? '0x' + value.toString(16) : undefined;\n const result = await jsonRpcProvider.send('eth_call', [\n { from: caller, to, data, value: valueHex },\n 'latest',\n stateOverrides,\n ]);\n const [amountOut] = this.routerContract.interface.decodeFunctionResult(method, result);\n return { amountOut, method };\n } else {\n const result = await this.provider.call({\n from: caller,\n to,\n data,\n value: value > 0n ? value : undefined,\n });\n const [amountOut] = this.routerContract.interface.decodeFunctionResult(method, result);\n return { amountOut, method };\n }\n }\n\n private getJsonRpcProviderForStateOverrides(): ethers.JsonRpcProvider {\n const provider = this.provider as Partial<ethers.JsonRpcProvider>;\n if (typeof provider.send !== \"function\") {\n throw new Error(\n \"stateOverrides require a JsonRpcProvider-compatible provider with send(method, params).\"\n );\n }\n return this.provider as ethers.JsonRpcProvider;\n }\n\n /**\n * Format simulate error with human-readable details\n */\n private formatSimulateError(err: unknown, quote: Quote, method: string, caller: string): Error {\n const original = err instanceof Error ? err : new Error(String(err));\n const errAny = err as Record<string, unknown>;\n\n // Extract revert reason from ethers CALL_EXCEPTION\n let reason = \"unknown\";\n if (errAny.reason && typeof errAny.reason === \"string\") {\n reason = errAny.reason;\n } else if (errAny.revert && typeof errAny.revert === \"object\") {\n const revert = errAny.revert as Record<string, unknown>;\n if (revert.args && Array.isArray(revert.args)) {\n reason = revert.args.join(\", \");\n }\n } else if (original.message) {\n reason = original.message;\n }\n\n // Build step summary\n const steps = quote.params.steps.map((s, i) =>\n ` Step ${i}: ${s.tokenIn.slice(0, 10)}→${s.tokenOut.slice(0, 10)} via adapter ${s.adapter.slice(0, 10)} pool ${s.pool.slice(0, 10)}`\n ).join(\"\\n\");\n\n const msg = [\n `Simulate ${method} failed: ${reason}`,\n ` Route: ${quote.srcToken} → ${quote.dstToken}`,\n ` AmountIn: ${quote.amountIn}`,\n ` AmountOutMin: ${quote.params.amountOutMin}`,\n ` Router: ${quote.routerAddress}`,\n ` Caller: ${caller}`,\n ` Steps (${quote.params.steps.length}):`,\n steps,\n ].join(\"\\n\");\n\n const e = new Error(msg) as Error & { cause?: unknown; reason?: string };\n e.cause = original;\n e.reason = reason;\n return e;\n }\n\n /**\n * Find which step in the route causes the same revert as the full route (e.g. MUL_ERROR).\n * Simulates with steps [0..1], [0..2], ... and returns the first step whose revert\n * matches fullRouteError. Ignores steps that revert with a different reason (e.g. unknown custom error).\n *\n * @param quote - Full quote from getQuote (the one that fails when simulated)\n * @param slippageBps - Same as for simulate\n * @param fromAddress - Same as for simulate\n * @param stateOverrides - Same as for simulate (use when simulating ERC20 sell with arbitrary address)\n * @param fullRouteError - The error from simulating the full route. Required so we match by revert reason (e.g. \"MUL_ERROR\"); only the step that produces the same reason is returned.\n * @returns The step index and step details whose revert matches fullRouteError, or null if none match or full route succeeds\n */\n async findFailingStep(\n quote: Quote,\n slippageBps: number,\n fromAddress?: string,\n stateOverrides?: Record<string, { stateDiff: Record<string, string> }>,\n fullRouteError?: unknown\n ): Promise<FindFailingStepResult | null> {\n const steps = quote.params.steps;\n if (!steps.length) return null;\n\n const targetReason = fullRouteError != null ? this.normalizeRevertReason(fullRouteError) : undefined;\n\n for (let n = 1; n <= steps.length; n++) {\n const truncated = this.quoteWithFirstNSteps(quote, n);\n try {\n await this.simulate(truncated, slippageBps, fromAddress, stateOverrides);\n } catch (err: unknown) {\n const stepReason = this.normalizeRevertReason(err);\n const msg =\n (err as { message?: string; reason?: string; shortMessage?: string })?.message ??\n (err as { reason?: string })?.reason ??\n (err as { shortMessage?: string })?.shortMessage;\n const match = targetReason != null ? stepReason === targetReason : true;\n if (match) {\n return {\n stepIndex: n - 1,\n step: steps[n - 1]!,\n error: err,\n revertMessage: typeof msg === 'string' ? msg : undefined,\n fullRouteRevertMessage: targetReason ?? undefined,\n };\n }\n // Revert reason differs (e.g. step 0 gave unknown custom error, we want MUL_ERROR): try next step\n }\n }\n return null;\n }\n\n /** Extract a comparable revert reason (e.g. \"MUL_ERROR\") from an error for findFailingStep matching. */\n private normalizeRevertReason(err: unknown): string | undefined {\n if (err == null) return undefined;\n const e = err as { reason?: string; message?: string; shortMessage?: string; data?: string };\n if (typeof e.reason === 'string' && e.reason.length > 0) return e.reason;\n const msg = e.shortMessage ?? e.message;\n if (typeof msg !== 'string') return undefined;\n const reasonMatch = msg.match(/reason=\"([^\"]+)\"/);\n if (reasonMatch) return reasonMatch[1];\n const revertedMatch = msg.match(/reverted:\\s*\"([^\"]+)\"/);\n if (revertedMatch) return revertedMatch[1];\n const execRevertedMatch = msg.match(/execution reverted:\\s*\"([^\"]+)\"/);\n if (execRevertedMatch) return execRevertedMatch[1];\n return undefined;\n }\n\n /** Build a quote that only includes the first stepCount steps (for findFailingStep). */\n private quoteWithFirstNSteps(quote: Quote, stepCount: number): Quote {\n const steps = quote.params.steps.slice(0, stepCount);\n const dstLower = quote.dstToken.toLowerCase();\n // Deduplicate intermediate tokens: split routes can have the same token as output\n // of multiple parallel steps (e.g. two USDT→tokenX hops both produce tokenX).\n // Passing duplicates to the router causes incorrect intermediate token accounting.\n const seen = new Set<string>();\n const intermediateTokens: string[] = [];\n for (const s of steps) {\n const lower = s.tokenOut.toLowerCase();\n if (lower !== dstLower && !seen.has(lower)) {\n seen.add(lower);\n intermediateTokens.push(s.tokenOut);\n }\n }\n return {\n ...quote,\n params: {\n ...quote.params,\n steps,\n intermediateTokens,\n },\n };\n }\n\n /**\n * Build state overrides for ERC20 token balance and allowance.\n * Useful for simulating swaps without actual on-chain token balance/approval.\n *\n * Automatically covers multiple storage slot layouts (slots 0-2 for balance,\n * slots 0-7 for allowance) to handle OZ ERC20 (slot 0/1), Ownable+ERC20 (slot 1/2),\n * and other common BSC token implementations.\n *\n * WBNB (native wrap) is skipped automatically — swapETH wraps msg.value\n * internally so no ERC20 approval from the sender is needed.\n *\n * @param tokenAddress - ERC20 token address (WBNB returns empty overrides)\n * @param owner - Address that needs the balance and allowance\n * @param routerAddress - Router address (used as fallback spender)\n * @param balance - Balance to inject (default: 1M tokens with 18 decimals)\n * @param spenderAddress - Spender to approve (default: routerAddress). Pass quote.routerAddress when simulating API quotes.\n */\n buildStateOverrides(\n tokenAddress: string,\n owner: string,\n routerAddress: string,\n balance?: bigint,\n spenderAddress?: string,\n options?: { isNative?: boolean }\n ): Record<string, { stateDiff: Record<string, string> }> {\n // swapETH wraps msg.value natively — no ERC20 override needed for native token\n if (options?.isNative || isNativeTokenAddress(tokenAddress)) {\n return {};\n }\n\n if (!routerAddress || routerAddress === ethers.ZeroAddress) {\n throw new Error(\"buildStateOverrides requires a non-zero routerAddress.\");\n }\n\n const abiCoder = ethers.AbiCoder.defaultAbiCoder();\n const tokenBalance = balance || ethers.parseUnits('1000000', 18);\n const spender = spenderAddress ?? routerAddress;\n const balanceHex = ethers.zeroPadValue(ethers.toBeHex(tokenBalance), 32);\n const maxUint256Hex = ethers.zeroPadValue(ethers.toBeHex(ethers.MaxUint256), 32);\n\n const stateDiff: Record<string, string> = {};\n\n // Target storage slots covering all common ERC20 layouts:\n // 0-2 : standard OZ ERC20 (balance=0, allowance=1) and Ownable+ERC20 (balance=1, allowance=2)\n // 9-13 : USDC/FiatToken style (balance≈9-11, allowance≈10-12)\n // 50-52 : OZ UpgradeableERC20 v4.x (ContextUpgradeable.__gap[50] pushes balance to slot 50, allowance to slot 51)\n // 100-102: OZ Upgradeable with OwnableUpgradeable (additional 50-slot gap before ERC20 state)\n const SLOTS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 50, 51, 52, 100, 101, 102];\n\n for (const slot of SLOTS) {\n // Balance\n const balKey = ethers.keccak256(abiCoder.encode(['address', 'uint256'], [owner, slot]));\n stateDiff[balKey] = balanceHex;\n\n // Allowance (nested mapping: allowances[owner][spender] at slot S)\n const inner = ethers.keccak256(abiCoder.encode(['address', 'uint256'], [owner, slot]));\n const allowKey = ethers.keccak256(abiCoder.encode(['address', 'bytes32'], [spender, inner]));\n stateDiff[allowKey] = maxUint256Hex;\n }\n\n return {\n [tokenAddress.toLowerCase()]: { stateDiff },\n };\n }\n}\n","/**\n * RouteDiscovery - Route discovery and optimization\n *\n * Features:\n * 1. Discover available pools\n * 2. Calculate optimal routes\n * 3. Split large trades\n */\n\nimport { ethers } from \"ethers\";\nimport {\n PeachConfig,\n PoolInfo,\n Route,\n RouteStep,\n SplitRoute,\n ProtocolType,\n} from \"../types\";\n\n// PancakeSwap V2 Pair ABI\nconst PAIR_ABI = [\n \"function token0() external view returns (address)\",\n \"function token1() external view returns (address)\",\n \"function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)\",\n];\n\n// PancakeSwap V3 Pool ABI\nconst V3_POOL_ABI = [\n \"function token0() external view returns (address)\",\n \"function token1() external view returns (address)\",\n \"function fee() external view returns (uint24)\",\n \"function liquidity() external view returns (uint128)\",\n \"function slot0() external view returns (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint32 feeProtocol, bool unlocked)\",\n];\n\n// PancakeSwap V2 Factory\nconst V2_FACTORY_ADDRESS = \"0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73\";\nconst V2_FACTORY_ABI = [\n \"function getPair(address tokenA, address tokenB) external view returns (address pair)\",\n];\n\n// PancakeSwap V3 Factory\nconst V3_FACTORY_ADDRESS = \"0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865\";\nconst V3_FACTORY_ABI = [\n \"function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool)\",\n];\n\n// V3 fee tiers\nconst V3_FEE_TIERS = [100, 500, 2500, 10000];\n\n// Gas estimation constants\nconst GAS_PER_V2_SWAP = 60000n;\nconst GAS_PER_V3_SWAP = 120000n;\nconst GAS_PER_DODO_SWAP = 100000n;\n\nexport class RouteDiscovery {\n private provider: ethers.Provider;\n private config: PeachConfig;\n private v2Factory: ethers.Contract;\n private v3Factory: ethers.Contract;\n\n // Pool cache\n private poolCache = new Map<string, PoolInfo>();\n\n constructor(provider: ethers.Provider, config: PeachConfig) {\n this.provider = provider;\n this.config = config;\n this.v2Factory = new ethers.Contract(\n V2_FACTORY_ADDRESS,\n V2_FACTORY_ABI,\n provider\n );\n this.v3Factory = new ethers.Contract(\n V3_FACTORY_ADDRESS,\n V3_FACTORY_ABI,\n provider\n );\n }\n\n /**\n * Discover optimal route\n */\n async findBestRoute(\n srcToken: string,\n dstToken: string,\n amountIn: bigint,\n \n ): Promise<SplitRoute> {\n // 1. Discover all available pools\n const pools = await this.discoverPools(srcToken, dstToken);\n\n if (pools.length === 0) {\n throw new Error(`No pools found for ${srcToken} -> ${dstToken}`);\n }\n\n // 2. Calculate quotes for each pool\n const routes = await this.calculateRoutes(\n pools,\n srcToken,\n dstToken,\n amountIn\n );\n\n // 3. Select optimal route (currently simple selection of max output single route)\n // TODO: Implement split route optimization\n const bestRoute = routes.reduce((best, current) =>\n current.amountOut > best.amountOut ? current : best\n );\n\n return {\n routes: [bestRoute],\n percentages: [10000], // 100%\n totalAmountIn: amountIn,\n totalAmountOut: bestRoute.amountOut,\n totalGasEstimate: bestRoute.gasEstimate,\n };\n }\n\n /**\n * Discover available pools (direct path)\n */\n private async discoverPools(\n tokenA: string,\n tokenB: string\n ): Promise<PoolInfo[]> {\n const pools: PoolInfo[] = [];\n\n // 1. Find V2 pool\n try {\n const v2Pool = await this.findV2Pool(tokenA, tokenB);\n if (v2Pool) {\n pools.push(v2Pool);\n }\n } catch (e) {\n // V2 pool doesn't exist\n }\n\n // 2. Find V3 pools (all fee tiers)\n for (const fee of V3_FEE_TIERS) {\n try {\n const v3Pool = await this.findV3Pool(tokenA, tokenB, fee);\n if (v3Pool) {\n pools.push(v3Pool);\n }\n } catch (e) {\n // V3 pool with this fee tier doesn't exist\n }\n }\n\n return pools;\n }\n\n /**\n * Find V2 pool\n */\n private async findV2Pool(\n tokenA: string,\n tokenB: string\n ): Promise<PoolInfo | null> {\n const cacheKey = `v2-${tokenA}-${tokenB}`;\n if (this.poolCache.has(cacheKey)) {\n return this.poolCache.get(cacheKey)!;\n }\n\n const pairAddress = await this.v2Factory.getPair(tokenA, tokenB);\n if (pairAddress === ethers.ZeroAddress) {\n return null;\n }\n\n const pair = new ethers.Contract(pairAddress, PAIR_ABI, this.provider);\n const [token0, token1, reserves] = await Promise.all([\n pair.token0(),\n pair.token1(),\n pair.getReserves(),\n ]);\n\n const pool: PoolInfo = {\n address: pairAddress,\n token0,\n token1,\n protocol: ProtocolType.PancakeV2,\n reserve0: reserves.reserve0,\n reserve1: reserves.reserve1,\n };\n\n this.poolCache.set(cacheKey, pool);\n return pool;\n }\n\n /**\n * Find V3 pool\n */\n private async findV3Pool(\n tokenA: string,\n tokenB: string,\n fee: number\n ): Promise<PoolInfo | null> {\n const cacheKey = `v3-${tokenA}-${tokenB}-${fee}`;\n if (this.poolCache.has(cacheKey)) {\n return this.poolCache.get(cacheKey)!;\n }\n\n const poolAddress = await this.v3Factory.getPool(tokenA, tokenB, fee);\n if (poolAddress === ethers.ZeroAddress) {\n return null;\n }\n\n const v3Pool = new ethers.Contract(poolAddress, V3_POOL_ABI, this.provider);\n const [token0, token1, liquidity] = await Promise.all([\n v3Pool.token0(),\n v3Pool.token1(),\n v3Pool.liquidity(),\n ]);\n\n // Skip pools with no liquidity\n if (liquidity === 0n) {\n return null;\n }\n\n const pool: PoolInfo = {\n address: poolAddress,\n token0,\n token1,\n protocol: ProtocolType.PancakeV3,\n fee,\n liquidity,\n };\n\n this.poolCache.set(cacheKey, pool);\n return pool;\n }\n\n /**\n * Calculate route quotes\n */\n private async calculateRoutes(\n pools: PoolInfo[],\n srcToken: string,\n dstToken: string,\n amountIn: bigint\n ): Promise<Route[]> {\n const routes: Route[] = [];\n\n for (const pool of pools) {\n try {\n const amountOut = await this.getAmountOut(\n pool,\n srcToken,\n dstToken,\n amountIn\n );\n\n if (amountOut > 0n) {\n routes.push({\n steps: [\n {\n pool,\n tokenIn: srcToken,\n tokenOut: dstToken,\n amountIn,\n amountOut,\n },\n ],\n amountIn,\n amountOut,\n gasEstimate:\n pool.protocol === ProtocolType.PancakeV2\n ? GAS_PER_V2_SWAP\n : pool.protocol === ProtocolType.Dodo\n ? GAS_PER_DODO_SWAP\n : GAS_PER_V3_SWAP,\n });\n }\n } catch (e) {\n // Quote failed, skip this pool\n }\n }\n\n return routes;\n }\n\n /**\n * Get output for a single pool\n */\n private async getAmountOut(\n pool: PoolInfo,\n tokenIn: string,\n tokenOut: string,\n amountIn: bigint\n ): Promise<bigint> {\n if (pool.protocol === ProtocolType.PancakeV2) {\n return this.getV2AmountOut(pool, tokenIn, amountIn);\n }\n if (pool.protocol === ProtocolType.Dodo) {\n // DODO pools are discovered via API; local discovery not implemented\n throw new Error(\"DODO amount out not supported in local route discovery\");\n }\n // V3 requires Quoter contract or simulation, simplified here\n return this.estimateV3AmountOut(pool, tokenIn, amountIn);\n }\n\n /**\n * V2 output calculation\n */\n private getV2AmountOut(\n pool: PoolInfo,\n tokenIn: string,\n amountIn: bigint\n ): bigint {\n const isToken0In = tokenIn.toLowerCase() === pool.token0.toLowerCase();\n const [reserveIn, reserveOut] = isToken0In\n ? [pool.reserve0!, pool.reserve1!]\n : [pool.reserve1!, pool.reserve0!];\n\n const amountInWithFee = amountIn * 9975n; // 0.25% fee\n const numerator = amountInWithFee * reserveOut;\n const denominator = reserveIn * 10000n + amountInWithFee;\n\n return numerator / denominator;\n }\n\n /**\n * V3 output estimation (simplified)\n */\n private estimateV3AmountOut(\n pool: PoolInfo,\n tokenIn: string,\n amountIn: bigint\n ): bigint {\n // Simplified estimation: assume linear price\n // Should use Quoter contract for accurate calculation in practice\n const fee = BigInt(pool.fee || 2500);\n const feeMultiplier = 1000000n - fee;\n return (amountIn * feeMultiplier) / 1000000n;\n }\n\n /**\n * Clear cache\n */\n clearCache(): void {\n this.poolCache.clear();\n }\n}\n","/**\n * SwapBuilder - Swap parameters builder\n *\n * Core features:\n * 1. Flatten split routes into step list\n * 2. Merge identical pools (pool merging optimization)\n * 3. Topological sort (ensure correct dependency order)\n * 4. Generate final SwapParams\n */\n\nimport { ethers } from \"ethers\";\nimport {\n SwapParams,\n SwapStep,\n SplitRoute,\n Route,\n RouteStep,\n PoolInfo,\n ProtocolType,\n BPS_DENOMINATOR,\n DEFAULT_DEADLINE_SECONDS,\n} from \"../types\";\n\nexport class SwapBuilder {\n private adapters: Map<ProtocolType, string>;\n\n constructor(adapters: Map<ProtocolType, string>) {\n this.adapters = adapters;\n }\n\n /**\n * Build SwapParams from split route\n */\n build(\n splitRoute: SplitRoute,\n srcToken: string,\n dstToken: string,\n amountOutMin: bigint,\n deadlineSeconds: number = DEFAULT_DEADLINE_SECONDS\n ): SwapParams {\n // 1. Flatten all routes into step list\n const flatSteps = this.flattenRoutes(splitRoute);\n\n // 2. Merge identical pools\n const mergedSteps = this.mergeIdenticalPools(flatSteps);\n\n // 3. Topological sort\n const sortedSteps = this.topologicalSort(mergedSteps, srcToken);\n\n // 4. Convert to SwapStep format\n const steps = this.convertToSwapSteps(sortedSteps);\n\n // 5. Extract intermediate tokens\n const intermediateTokens = this.extractIntermediates(\n sortedSteps,\n srcToken,\n dstToken\n );\n\n // 6. Calculate deadline\n const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineSeconds);\n\n return {\n srcToken,\n dstToken,\n amountIn: splitRoute.totalAmountIn,\n amountOutMin,\n steps,\n intermediateTokens,\n deadline,\n quoteId: ethers.ZeroHash,\n expectAmountOut: splitRoute.totalAmountOut,\n };\n }\n\n /**\n * Flatten split route\n */\n private flattenRoutes(splitRoute: SplitRoute): InternalStep[] {\n const steps: InternalStep[] = [];\n\n for (let i = 0; i < splitRoute.routes.length; i++) {\n const route = splitRoute.routes[i];\n const percentage = splitRoute.percentages[i];\n\n // Calculate allocated amount for this route\n const routeAmount =\n (splitRoute.totalAmountIn * BigInt(percentage)) / BPS_DENOMINATOR;\n\n for (let j = 0; j < route.steps.length; j++) {\n const routeStep = route.steps[j];\n\n steps.push({\n pool: routeStep.pool,\n tokenIn: routeStep.tokenIn,\n tokenOut: routeStep.tokenOut,\n // Only first step has fixed amount, subsequent steps depend on previous output\n amountIn: j === 0 ? routeAmount : 0n,\n routeIndex: i,\n stepIndex: j,\n });\n }\n }\n\n return steps;\n }\n\n /**\n * Merge identical pools\n * Key: pool + tokenIn + tokenOut\n */\n private mergeIdenticalPools(steps: InternalStep[]): InternalStep[] {\n const poolMap = new Map<string, InternalStep>();\n const result: InternalStep[] = [];\n\n for (const step of steps) {\n const key = `${step.pool.address}-${step.tokenIn}-${step.tokenOut}`;\n\n if (poolMap.has(key)) {\n // Identical pool already exists\n const existing = poolMap.get(key)!;\n\n // If current step has fixed input, accumulate to existing step\n if (step.amountIn > 0n) {\n if (existing.amountIn > 0n) {\n // Both have fixed input - this shouldn't happen after first hop\n // Keep consume all mode\n existing.amountIn = 0n;\n }\n }\n // If existing step is already consume all (0n), keep it unchanged\n } else {\n const newStep = { ...step };\n poolMap.set(key, newStep);\n result.push(newStep);\n }\n }\n\n // For merged pools with multiple input sources, set to consume all\n for (const step of result) {\n const key = `${step.pool.address}-${step.tokenIn}-${step.tokenOut}`;\n const allStepsForPool = steps.filter(\n (s) =>\n `${s.pool.address}-${s.tokenIn}-${s.tokenOut}` === key\n );\n\n if (allStepsForPool.length > 1) {\n // Multiple input sources, use consume all\n step.amountIn = 0n;\n }\n }\n\n return result;\n }\n\n /**\n * Topological sort (Kahn's Algorithm)\n */\n private topologicalSort(\n steps: InternalStep[],\n srcToken: string\n ): InternalStep[] {\n // Build adjacency list and in-degree\n const inDegree = new Map<string, number>();\n const stepMap = new Map<string, InternalStep>();\n const graph = new Map<string, string[]>(); // tokenOut -> [stepKeys that consume it]\n\n for (const step of steps) {\n const key = this.stepKey(step);\n stepMap.set(key, step);\n inDegree.set(key, 0);\n graph.set(key, []);\n }\n\n // Calculate in-degree\n for (const step of steps) {\n const key = this.stepKey(step);\n\n // If tokenIn is not source token, need to wait for producer\n if (step.tokenIn !== srcToken) {\n // Find steps that produce this token\n for (const producer of steps) {\n if (producer.tokenOut === step.tokenIn) {\n const producerKey = this.stepKey(producer);\n if (producerKey !== key) {\n // Add edge: producer -> step\n graph.get(producerKey)!.push(key);\n inDegree.set(key, (inDegree.get(key) || 0) + 1);\n }\n }\n }\n }\n }\n\n // BFS\n const queue: string[] = [];\n for (const [key, degree] of inDegree) {\n if (degree === 0) {\n queue.push(key);\n }\n }\n\n const result: InternalStep[] = [];\n while (queue.length > 0) {\n const key = queue.shift()!;\n const step = stepMap.get(key)!;\n result.push(step);\n\n // Update in-degree of steps dependent on this step\n for (const dependent of graph.get(key) || []) {\n const newDegree = (inDegree.get(dependent) || 0) - 1;\n inDegree.set(dependent, newDegree);\n if (newDegree === 0) {\n queue.push(dependent);\n }\n }\n }\n\n // Check for cycles\n if (result.length !== steps.length) {\n throw new Error(\"Circular dependency detected in route\");\n }\n\n return result;\n }\n\n /**\n * Convert to contract SwapStep format\n */\n private convertToSwapSteps(steps: InternalStep[]): SwapStep[] {\n return steps.map((step) => {\n const adapter = this.adapters.get(step.pool.protocol);\n if (!adapter) {\n throw new Error(`No adapter for protocol: ${step.pool.protocol}`);\n }\n\n return {\n adapter,\n pool: step.pool.address,\n tokenIn: step.tokenIn,\n tokenOut: step.tokenOut,\n amountIn: step.amountIn,\n extraData: this.encodeExtraData(step.pool),\n };\n });\n }\n\n /**\n * Encode protocol-specific parameters\n */\n private encodeExtraData(pool: PoolInfo): string {\n switch (pool.protocol) {\n case ProtocolType.PancakeV2:\n return \"0x\"; // V2 doesn't need extra parameters\n\n case ProtocolType.PancakeV3:\n case ProtocolType.UniswapV3:\n case ProtocolType.UniswapV4:\n // V3/V4 fee can be read from pool contract, no need to pass in extraData\n return \"0x\";\n\n case ProtocolType.Dodo:\n // DODO adapter typically uses pool-specific extraData from API or 0x\n return \"0x\";\n\n default:\n return \"0x\";\n }\n }\n\n /**\n * Extract intermediate tokens\n */\n private extractIntermediates(\n steps: InternalStep[],\n srcToken: string,\n dstToken: string\n ): string[] {\n const tokens = new Set<string>();\n\n for (const step of steps) {\n // tokenOut that is not destination token is intermediate token\n if (step.tokenOut !== dstToken) {\n tokens.add(step.tokenOut);\n }\n }\n\n // Exclude source and destination tokens\n tokens.delete(srcToken);\n tokens.delete(dstToken);\n\n return Array.from(tokens);\n }\n\n /**\n * Generate unique step key\n */\n private stepKey(step: InternalStep): string {\n return `${step.pool.address}-${step.tokenIn}-${step.tokenOut}`;\n }\n}\n\n/**\n * Internal step type\n */\ninterface InternalStep {\n pool: PoolInfo;\n tokenIn: string;\n tokenOut: string;\n amountIn: bigint;\n routeIndex: number;\n stepIndex: number;\n}\n"],"names":["DEFAULT_API_URL","DEFAULT_SLIPPAGE_BPS","BPS_DENOMINATOR","DEFAULT_DEADLINE_SECONDS","DEFAULT_EXECUTE_TIMEOUT_MS","DEFAULT_TRANSACTION_RESPONSE_POLL_INTERVALS_MS","NATIVE_TOKEN_ADDRESS","isNativeTokenAddress","address","ERR_ZERO_AMOUNT_PATHS","ProtocolType","BSC_MAINNET_CONFIG","BSC_TESTNET_CONFIG","ExecuteTimeoutError","message","stage","txHash","API_DEFAULTS","DEFAULT_TIMEOUT","ApiClient","config","params","from","target","amount","byAmountIn","depth","splitCount","providers","queryParams","url","controller","timeoutId","response","ApiError","json","error","code","withWalletSendTimeout","promise","timeoutMs","_","reject","PEACH_ROUTER_ABI","ERC20_ABI","PANCAKE_INFINITY_CL_POOL_MANAGER","PROVIDER_TO_PROTOCOL","PeachClient","provider","options","ethers","quote","addr","slippageBps","amountOutMin","ownerAddress","routerAddress","tx","method","approval","signer","signerAddress","prepared","err","msg","isEstimateGas","isMissingRevertData","friendly","e","step","swapParams","useSwapETH","encodedParams","data","to","value","token","owner","spender","allowance","includeGasLimit","txWithOverrides","effectiveTimeoutMs","uncheckedSigner","hash","pollResult","failureMode","pollingIntervalsMs","deadline","attempt","nullResponses","rpcErrors","nextDelay","ms","resolve","tokenAddress","symbol","decimals","balance","userAddress","srcToken","dstToken","amountIn","deadlineSeconds","srcNative","dstNative","apiSrcToken","apiDstToken","apiData","paths","allowedProviders","allowedSet","p","filtered","srcLower","dstLower","reachableFromSrc","changed","reachableToDst","requestedProviders","srcTokenLower","validPaths","dstTokenLower","amountOut","tokenInCount","key","tokenInSeen","steps","seen","total","useExplicit","isEntryPoint","intermediateTokenSet","intermediateTokens","route","fromAddress","stateOverrides","caller","jsonRpcProvider","valueHex","result","original","errAny","reason","revert","s","i","fullRouteError","targetReason","n","truncated","stepReason","reasonMatch","revertedMatch","execRevertedMatch","stepCount","lower","spenderAddress","abiCoder","tokenBalance","balanceHex","maxUint256Hex","stateDiff","SLOTS","slot","balKey","inner","allowKey","PAIR_ABI","V3_POOL_ABI","V2_FACTORY_ADDRESS","V2_FACTORY_ABI","V3_FACTORY_ADDRESS","V3_FACTORY_ABI","V3_FEE_TIERS","GAS_PER_V2_SWAP","GAS_PER_V3_SWAP","GAS_PER_DODO_SWAP","RouteDiscovery","pools","bestRoute","best","current","tokenA","tokenB","v2Pool","fee","v3Pool","cacheKey","pairAddress","pair","token0","token1","reserves","pool","poolAddress","liquidity","routes","tokenIn","tokenOut","isToken0In","reserveIn","reserveOut","amountInWithFee","numerator","denominator","feeMultiplier","SwapBuilder","adapters","splitRoute","flatSteps","mergedSteps","sortedSteps","percentage","routeAmount","j","routeStep","poolMap","existing","newStep","inDegree","stepMap","graph","producer","producerKey","queue","degree","dependent","newDegree","adapter","tokens"],"mappings":"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"}
|