@pagg/aggregator-sdk 0.0.0-experimental-20260203092523

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 ADDED
@@ -0,0 +1,220 @@
1
+ # Peach Aggregator SDK
2
+
3
+ TypeScript SDK for the DEX Aggregator on BSC. Fetches optimal swap routes from the aggregator API and builds on-chain transactions.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @pagg/aggregator-sdk ethers
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { PeachClient, BSC_MAINNET_CONFIG } from '@peach/aggregator-sdk';
15
+ import { ethers } from 'ethers';
16
+
17
+ const provider = new ethers.JsonRpcProvider('https://bsc-dataseed.binance.org');
18
+
19
+ const client = new PeachClient(BSC_MAINNET_CONFIG, provider, {
20
+ api: { baseUrl: 'https://api.cipheron.org' },
21
+ enableFallback: true, // Use local calculation if API fails
22
+ });
23
+
24
+ // Get quote
25
+ const quote = await client.getQuote({
26
+ srcToken: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c', // WBNB
27
+ dstToken: '0x55d398326f99059fF775485246999027B3197955', // USDT
28
+ amountIn: ethers.parseEther('1'),
29
+ options: {
30
+ slippageBps: 50, // 0.5% slippage
31
+ splitCount: 5, // Split into up to 5 routes
32
+ providers: ['PANCAKEV3'],
33
+ },
34
+ });
35
+
36
+ console.log('Amount out:', ethers.formatUnits(quote.amountOut, 18));
37
+
38
+ // Execute swap
39
+ const signer = new ethers.Wallet(PRIVATE_KEY, provider);
40
+ const tx = await client.execute(quote, signer);
41
+ await tx.wait();
42
+ ```
43
+
44
+ ## API
45
+
46
+ ### PeachClient
47
+
48
+ Main SDK client with API-first routing and local fallback.
49
+
50
+ ```typescript
51
+ const client = new PeachClient(config, provider?, options?);
52
+ ```
53
+
54
+ **Parameters:**
55
+ - `config`: `PeachConfig` - Chain configuration (use `BSC_MAINNET_CONFIG` or `BSC_TESTNET_CONFIG`)
56
+ - `provider?`: `ethers.Provider` - Optional provider (defaults to config.rpcUrl)
57
+ - `options?`: `PeachClientOptions`
58
+ - `api?`: `{ baseUrl: string, timeout?: number }` - API configuration
59
+ - `enableFallback?`: `boolean` - Use local calculation if API fails (default: true)
60
+
61
+ **Methods:**
62
+
63
+ ```typescript
64
+ // Get quote with options
65
+ await client.getQuote({
66
+ srcToken: string,
67
+ dstToken: string,
68
+ amountIn: bigint,
69
+ options?: {
70
+ byAmountIn?: boolean, // true: input→output, false: output→input (default: true)
71
+ depth?: number, // Route search depth (default: 3)
72
+ splitCount?: number, // Trade split count (default: 20)
73
+ providers?: string[], // DEX providers (default: ['PANCAKEV3'])
74
+ slippageBps?: number, // Slippage in bps (default: 50 = 0.5%)
75
+ }
76
+ }): Promise<Quote>
77
+
78
+ // Execute swap
79
+ await client.execute(quote: Quote, signer: ethers.Signer): Promise<TransactionResponse>
80
+
81
+ // Get available providers
82
+ await client.getAvailableProviders(): Promise<string[]>
83
+
84
+ // Get token info
85
+ await client.getTokenInfo(tokenAddress: string): Promise<{ symbol, decimals, balance }>
86
+
87
+ // Get token balance
88
+ await client.getBalance(tokenAddress: string, userAddress: string): Promise<bigint>
89
+ ```
90
+
91
+ ### ApiClient
92
+
93
+ Direct API access for custom integrations.
94
+
95
+ ```typescript
96
+ import { ApiClient } from '@peach/aggregator-sdk';
97
+
98
+ const api = new ApiClient({ baseUrl: 'https://api.cipheron.org' });
99
+
100
+ // Find routes
101
+ const data = await api.findRoutes({
102
+ from: '0x...srcToken',
103
+ target: '0x...dstToken',
104
+ amount: 1000000000000000000n,
105
+ byAmountIn: true,
106
+ splitCount: 5,
107
+ providers: ['PANCAKEV3'],
108
+ });
109
+
110
+ // Get service status
111
+ const status = await api.getStatus();
112
+ console.log('Available providers:', status.providers);
113
+ ```
114
+
115
+ ## Quote Object
116
+
117
+ The `Quote` object returned by `getQuote()`:
118
+
119
+ ```typescript
120
+ interface Quote {
121
+ srcToken: string;
122
+ dstToken: string;
123
+ amountIn: bigint;
124
+ amountOut: bigint;
125
+ amountOutMin: bigint; // After slippage
126
+ priceImpact: number;
127
+ gasEstimate: bigint;
128
+ route: SplitRoute; // Route details for display
129
+ params: SwapParams; // Contract call parameters
130
+ }
131
+ ```
132
+
133
+ ## API Response Format
134
+
135
+ The aggregator API returns routes with contract addresses:
136
+
137
+ ```json
138
+ {
139
+ "code": 200,
140
+ "msg": "success",
141
+ "data": {
142
+ "request_id": "...",
143
+ "amount_in": "1000000000000000000",
144
+ "amount_out": "580000000000000000000",
145
+ "gas": "150000",
146
+ "contracts": {
147
+ "router": "0x371ba011c77493038318A9662E8E760448e0D87F",
148
+ "adapters": {
149
+ "PANCAKEV3": "0x30bc59E2d16282cA4953b073Fc397766c2c2dF24"
150
+ }
151
+ },
152
+ "paths": [
153
+ {
154
+ "pool": "0x...",
155
+ "provider": "PANCAKEV3",
156
+ "adapter": "0x30bc59E2d16282cA4953b073Fc397766c2c2dF24",
157
+ "token_in": "0x...",
158
+ "token_out": "0x...",
159
+ "fee_rate": "0.0005",
160
+ "amount_in": "1000000000000000000",
161
+ "amount_out": "580000000000000000000",
162
+ "extra_data": "0x..."
163
+ }
164
+ ]
165
+ }
166
+ }
167
+ ```
168
+
169
+ ## Exports
170
+
171
+ ```typescript
172
+ // Clients
173
+ export { PeachClient, PeachClientOptions } from './clients/PeachClient';
174
+ export { ApiClient, ApiError, ApiClientConfig } from './clients/ApiClient';
175
+ export { RouteDiscovery } from './clients/RouteDiscovery';
176
+
177
+ // Configs
178
+ export { BSC_MAINNET_CONFIG, BSC_TESTNET_CONFIG } from './types';
179
+
180
+ // Types
181
+ export {
182
+ Quote,
183
+ SwapParams,
184
+ SwapStep,
185
+ SplitRoute,
186
+ Route,
187
+ RouteStep,
188
+ PoolInfo,
189
+ PeachConfig,
190
+ QuoteOptions,
191
+ Provider,
192
+ ProtocolType,
193
+ } from './types';
194
+
195
+ // Constants
196
+ export {
197
+ DEFAULT_SLIPPAGE_BPS, // 50 (0.5%)
198
+ BPS_DENOMINATOR, // 10000
199
+ DEFAULT_DEADLINE_SECONDS, // 1200 (20 min)
200
+ API_DEFAULTS,
201
+ } from './types';
202
+ ```
203
+
204
+ ## Testing
205
+
206
+ ```bash
207
+ npm test # All tests
208
+ npm run test:api # API integration tests
209
+ npm run test:integration # Mainnet integration
210
+ ```
211
+
212
+ ## Build
213
+
214
+ ```bash
215
+ npm run build
216
+ ```
217
+
218
+ ## License
219
+
220
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("ethers"),v=50,I=10000n,P=1800;var m=(w=>(w.PancakeV2="PancakeV2",w.PancakeV3="PancakeV3",w))(m||{});const _={chainId:56,rpcUrl:"https://bsc-dataseed.binance.org",routerAddress:"0x371ba011c77493038318A9662E8E760448e0D87F",weth:"0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",adapters:[{protocol:"PancakeV2",address:"0x7B513523c1B0D934Ca70660E041f9348785e86d0"},{protocol:"PancakeV3",address:"0x30bc59E2d16282cA4953b073Fc397766c2c2dF24"}]},b={chainId:97,rpcUrl:"https://bsc-testnet-rpc.publicnode.com",routerAddress:"",weth:"0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd",adapters:[{protocol:"PancakeV3",address:""}]},A={depth:3,splitCount:20,providers:["PANCAKEV2","PANCAKEV3"],clientVersion:1001500};class E{constructor(t){this.adapters=t}build(t,e,n,o,s=P){const r=this.flattenRoutes(t),a=this.mergeIdenticalPools(r),i=this.topologicalSort(a,e),u=this.convertToSwapSteps(i),l=this.extractIntermediates(i,e,n),d=BigInt(Math.floor(Date.now()/1e3)+s);return{srcToken:e,dstToken:n,amountIn:t.totalAmountIn,amountOutMin:o,steps:u,intermediateTokens:l,deadline:d}}flattenRoutes(t){const e=[];for(let n=0;n<t.routes.length;n++){const o=t.routes[n],s=t.percentages[n],r=t.totalAmountIn*BigInt(s)/I;for(let a=0;a<o.steps.length;a++){const i=o.steps[a];e.push({pool:i.pool,tokenIn:i.tokenIn,tokenOut:i.tokenOut,amountIn:a===0?r:0n,routeIndex:n,stepIndex:a})}}return e}mergeIdenticalPools(t){const e=new Map,n=[];for(const o of t){const s=`${o.pool.address}-${o.tokenIn}-${o.tokenOut}`;if(e.has(s)){const r=e.get(s);o.amountIn>0n&&r.amountIn>0n&&(r.amountIn=0n)}else{const r={...o};e.set(s,r),n.push(r)}}for(const o of n){const s=`${o.pool.address}-${o.tokenIn}-${o.tokenOut}`;t.filter(a=>`${a.pool.address}-${a.tokenIn}-${a.tokenOut}`===s).length>1&&(o.amountIn=0n)}return n}topologicalSort(t,e){const n=new Map,o=new Map,s=new Map;for(const i of t){const u=this.stepKey(i);o.set(u,i),n.set(u,0),s.set(u,[])}for(const i of t){const u=this.stepKey(i);if(i.tokenIn!==e){for(const l of t)if(l.tokenOut===i.tokenIn){const d=this.stepKey(l);d!==u&&(s.get(d).push(u),n.set(u,(n.get(u)||0)+1))}}}const r=[];for(const[i,u]of n)u===0&&r.push(i);const a=[];for(;r.length>0;){const i=r.shift(),u=o.get(i);a.push(u);for(const l of s.get(i)||[]){const d=(n.get(l)||0)-1;n.set(l,d),d===0&&r.push(l)}}if(a.length!==t.length)throw new Error("Circular dependency detected in route");return a}convertToSwapSteps(t){return t.map(e=>{const n=this.adapters.get(e.pool.protocol);if(!n)throw new Error(`No adapter for protocol: ${e.pool.protocol}`);return{adapter:n,pool:e.pool.address,tokenIn:e.tokenIn,tokenOut:e.tokenOut,amountIn:e.amountIn,extraData:this.encodeExtraData(e.pool)}})}encodeExtraData(t){switch(t.protocol){case m.PancakeV2:return"0x";case m.PancakeV3:return"0x";default:return"0x"}}extractIntermediates(t,e,n){const o=new Set;for(const s of t)s.tokenOut!==n&&o.add(s.tokenOut);return o.delete(e),o.delete(n),Array.from(o)}stepKey(t){return`${t.pool.address}-${t.tokenIn}-${t.tokenOut}`}}const y=["function token0() external view returns (address)","function token1() external view returns (address)","function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)"],S=["function token0() external view returns (address)","function token1() external view returns (address)","function fee() external view returns (uint24)","function liquidity() external view returns (uint128)","function slot0() external view returns (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint32 feeProtocol, bool unlocked)"],x="0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73",B=["function getPair(address tokenA, address tokenB) external view returns (address pair)"],T="0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865",D=["function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool)"],F=[100,500,2500,1e4],V=60000n,R=120000n;class O{constructor(t,e){this.poolCache=new Map,this.provider=t,this.config=e,this.v2Factory=new f.ethers.Contract(x,B,t),this.v3Factory=new f.ethers.Contract(T,D,t)}async findBestRoute(t,e,n){const o=await this.discoverPools(t,e);if(o.length===0)throw new Error(`No pools found for ${t} -> ${e}`);const r=(await this.calculateRoutes(o,t,e,n)).reduce((a,i)=>i.amountOut>a.amountOut?i:a);return{routes:[r],percentages:[1e4],totalAmountIn:n,totalAmountOut:r.amountOut,totalGasEstimate:r.gasEstimate}}async discoverPools(t,e){const n=[];try{const o=await this.findV2Pool(t,e);o&&n.push(o)}catch{}for(const o of F)try{const s=await this.findV3Pool(t,e,o);s&&n.push(s)}catch{}return n}async findV2Pool(t,e){const n=`v2-${t}-${e}`;if(this.poolCache.has(n))return this.poolCache.get(n);const o=await this.v2Factory.getPair(t,e);if(o===f.ethers.ZeroAddress)return null;const s=new f.ethers.Contract(o,y,this.provider),[r,a,i]=await Promise.all([s.token0(),s.token1(),s.getReserves()]),u={address:o,token0:r,token1:a,protocol:m.PancakeV2,reserve0:i.reserve0,reserve1:i.reserve1};return this.poolCache.set(n,u),u}async findV3Pool(t,e,n){const o=`v3-${t}-${e}-${n}`;if(this.poolCache.has(o))return this.poolCache.get(o);const s=await this.v3Factory.getPool(t,e,n);if(s===f.ethers.ZeroAddress)return null;const r=new f.ethers.Contract(s,S,this.provider),[a,i,u]=await Promise.all([r.token0(),r.token1(),r.liquidity()]);if(u===0n)return null;const l={address:s,token0:a,token1:i,protocol:m.PancakeV3,fee:n,liquidity:u};return this.poolCache.set(o,l),l}async calculateRoutes(t,e,n,o){const s=[];for(const r of t)try{const a=await this.getAmountOut(r,e,n,o);a>0n&&s.push({steps:[{pool:r,tokenIn:e,tokenOut:n,amountIn:o,amountOut:a}],amountIn:o,amountOut:a,gasEstimate:r.protocol===m.PancakeV2?V:R})}catch{}return s}async getAmountOut(t,e,n,o){return t.protocol===m.PancakeV2?this.getV2AmountOut(t,e,o):this.estimateV3AmountOut(t,e,o)}getV2AmountOut(t,e,n){const o=e.toLowerCase()===t.token0.toLowerCase(),[s,r]=o?[t.reserve0,t.reserve1]:[t.reserve1,t.reserve0],a=n*9975n,i=a*r,u=s*10000n+a;return i/u}estimateV3AmountOut(t,e,n){const s=1000000n-BigInt(t.fee||2500);return n*s/1000000n}clearCache(){this.poolCache.clear()}}const U=1e4;class C{constructor(t){if(!t.baseUrl)throw new Error("ApiClient requires a baseUrl");this.baseUrl=t.baseUrl,this.timeout=t.timeout||U}async findRoutes(t){const{from:e,target:n,amount:o,byAmountIn:s=!0,depth:r=A.depth,splitCount:a=A.splitCount,providers:i=A.providers}=t,u=new URLSearchParams({from:e,target:n,amount:o.toString(),by_amount_in:s.toString(),depth:r.toString(),split_count:a.toString(),providers:i.join(","),v:A.clientVersion.toString()}),l=`${this.baseUrl}/router/find_routes?${u}`,d=new AbortController,g=setTimeout(()=>d.abort(),this.timeout);try{const c=await fetch(l,{method:"GET",headers:{Accept:"application/json"},signal:d.signal});if(clearTimeout(g),!c.ok)throw new p(`API request failed: ${c.status} ${c.statusText}`,c.status);const h=await c.json();if(h.code!==200)throw new p(h.msg||"Route not found",h.code);if(!h.data||!h.data.paths||h.data.paths.length===0)throw new p("No routes found",404);return h.data}catch(c){throw clearTimeout(g),c instanceof p?c:c instanceof Error?c.name==="AbortError"?new p("API request timeout",408):new p(`API request failed: ${c.message}`,0):new p("Unknown API error",0)}}async getStatus(){const t=`${this.baseUrl}/router/status`,e=new AbortController,n=setTimeout(()=>e.abort(),this.timeout);try{const o=await fetch(t,{method:"GET",headers:{Accept:"application/json"},signal:e.signal});if(clearTimeout(n),!o.ok)throw new p(`API request failed: ${o.status} ${o.statusText}`,o.status);const s=await o.json();if(s.code!==200)throw new p(s.msg||"Failed to get status",s.code);return s.data}catch(o){throw clearTimeout(n),o instanceof p?o:o instanceof Error?o.name==="AbortError"?new p("API request timeout",408):new p(`API request failed: ${o.message}`,0):new p("Unknown API error",0)}}async getAvailableProviders(){return(await this.getStatus()).providers}setBaseUrl(t){this.baseUrl=t}getBaseUrl(){return this.baseUrl}}class p extends Error{constructor(t,e){super(t),this.name="ApiError",this.code=e}}const $=["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) params) external returns (uint256 amountOut)","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) params) external payable returns (uint256 amountOut)","function isAdapterRegistered(address adapter) external view returns (bool)","function WETH() external view returns (address)"],k=["function approve(address spender, uint256 amount) external returns (bool)","function allowance(address owner, address spender) external view returns (uint256)","function balanceOf(address account) external view returns (uint256)","function decimals() external view returns (uint8)","function symbol() external view returns (string)"];class L{constructor(t,e,n){this.config=t,this.provider=e||new f.ethers.JsonRpcProvider(t.rpcUrl),this.enableFallback=n?.enableFallback??!0;const o=new Map;for(const s of t.adapters)o.set(s.protocol,s.address);this.routeDiscovery=new O(this.provider,t),this.swapBuilder=new E(o),this.routerContract=new f.ethers.Contract(t.routerAddress,$,this.provider),this.apiClient=n?.api?new C(n.api):null}async getQuote(t){const{srcToken:e,dstToken:n,amountIn:o,options:s={}}=t,{byAmountIn:r=!0,depth:a=A.depth,splitCount:i=A.splitCount,providers:u=A.providers,slippageBps:l=v}=s;if(!this.apiClient)return this.getQuoteLocal(e,n,o,l);try{const d=await this.apiClient.findRoutes({from:e,target:n,amount:o,byAmountIn:r,depth:a,splitCount:i,providers:u});return this.buildQuoteFromApi(d,e,n,l)}catch(d){if(this.enableFallback)return console.warn("[PeachClient] API failed, falling back to local calculation:",d instanceof Error?d.message:d),this.getQuoteLocal(e,n,o,l);throw d}}async getQuoteLocal(t,e,n,o){const s=await this.routeDiscovery.findBestRoute(t,e,n),r=s.totalAmountOut*(I-BigInt(o))/I,a=this.swapBuilder.build(s,t,e,r),i=this.calculatePriceImpact(s);return{srcToken:t,dstToken:e,amountIn:n,amountOut:s.totalAmountOut,amountOutMin:r,priceImpact:i,route:s,params:a,gasEstimate:s.totalGasEstimate}}buildQuoteFromApi(t,e,n,o){const s=BigInt(t.amount_in),r=BigInt(t.amount_out),a=r*(I-BigInt(o))/I,i=BigInt(Math.floor(Date.now()/1e3)+P),u=t.paths.map(c=>{const h=c.token_in.toLowerCase()===e.toLowerCase();return{adapter:c.adapter,pool:c.pool,tokenIn:c.token_in,tokenOut:c.token_out,amountIn:h?BigInt(c.amount_in):0n,extraData:c.extra_data||"0x"}}),l=[];for(let c=0;c<t.paths.length-1;c++){const h=t.paths[c].token_out;h.toLowerCase()!==n.toLowerCase()&&l.push(h)}const d={srcToken:e,dstToken:n,amountIn:s,amountOutMin:a,steps:u,intermediateTokens:l,deadline:i},g={routes:[{steps:t.paths.map(c=>({pool:{address:c.pool,token0:c.token_in,token1:c.token_out,protocol:c.provider==="PANCAKEV3"?m.PancakeV3:m.PancakeV2,fee:c.fee_rate?Math.round(parseFloat(c.fee_rate)*1e6):void 0},tokenIn:c.token_in,tokenOut:c.token_out,amountIn:BigInt(c.amount_in),amountOut:BigInt(c.amount_out)})),amountIn:s,amountOut:r,gasEstimate:BigInt(t.gas)}],percentages:[1e4],totalAmountIn:s,totalAmountOut:r,totalGasEstimate:BigInt(t.gas)};return{srcToken:e,dstToken:n,amountIn:s,amountOut:r,amountOutMin:a,priceImpact:parseFloat(t.deviation_ratio||"0"),route:g,params:d,gasEstimate:BigInt(t.gas)}}async execute(t,e){const n=await e.getAddress(),o=this.routerContract.connect(e);return await this.ensureAllowance(t.srcToken,n,t.amountIn,e),t.srcToken.toLowerCase()===this.config.weth.toLowerCase()?o.swapETH(this.encodeParams(t.params),{value:t.amountIn}):o.swap(this.encodeParams(t.params))}encodeParams(t){return{srcToken:t.srcToken,dstToken:t.dstToken,amountIn:t.amountIn,amountOutMin:t.amountOutMin,steps:t.steps.map(e=>({adapter:e.adapter,pool:e.pool,tokenIn:e.tokenIn,tokenOut:e.tokenOut,amountIn:e.amountIn,extraData:e.extraData})),intermediateTokens:t.intermediateTokens,deadline:t.deadline}}async ensureAllowance(t,e,n,o){if(t.toLowerCase()===this.config.weth.toLowerCase())return;const s=new f.ethers.Contract(t,k,this.provider);await s.allowance(e,this.config.routerAddress)<n&&await(await s.connect(o).approve(this.config.routerAddress,f.ethers.MaxUint256)).wait()}calculatePriceImpact(t){return 0}async getTokenInfo(t){const e=new f.ethers.Contract(t,k,this.provider),[n,o]=await Promise.all([e.symbol(),e.decimals()]);return{symbol:n,decimals:o,balance:0n}}async getBalance(t,e){return new f.ethers.Contract(t,k,this.provider).balanceOf(e)}getApiClient(){return this.apiClient}setApiBaseUrl(t){this.apiClient?this.apiClient.setBaseUrl(t):this.apiClient=new C({baseUrl:t})}async getAvailableProviders(){if(!this.apiClient)throw new Error("API client is not configured");return this.apiClient.getAvailableProviders()}}exports.API_DEFAULTS=A;exports.ApiClient=C;exports.ApiError=p;exports.BPS_DENOMINATOR=I;exports.BSC_MAINNET_CONFIG=_;exports.BSC_TESTNET_CONFIG=b;exports.DEFAULT_DEADLINE_SECONDS=P;exports.DEFAULT_SLIPPAGE_BPS=v;exports.PeachClient=L;exports.ProtocolType=m;exports.RouteDiscovery=O;exports.SwapBuilder=E;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/types/index.ts","../src/builders/SwapBuilder.ts","../src/clients/RouteDiscovery.ts","../src/clients/ApiClient.ts","../src/clients/PeachClient.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_SLIPPAGE_BPS = 50; // 0.5%\nexport const BPS_DENOMINATOR = 10000n;\nexport const DEFAULT_DEADLINE_SECONDS = 1800; // 30 minutes\n\n// ============ Protocol Types ============\n\nexport enum ProtocolType {\n PancakeV2 = \"PancakeV2\",\n PancakeV3 = \"PancakeV3\",\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 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}\n\n// ============ Quote ============\n\nexport interface Quote {\n srcToken: string;\n dstToken: string;\n amountIn: bigint;\n amountOut: bigint;\n amountOutMin: bigint;\n priceImpact: number;\n route: SplitRoute;\n params: SwapParams;\n gasEstimate: bigint;\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 routerAddress: \"0x371ba011c77493038318A9662E8E760448e0D87F\", // PeachAggregatorUpgradeable Proxy\n weth: \"0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c\", // WBNB\n adapters: [\n {\n protocol: ProtocolType.PancakeV2,\n address: \"0x7B513523c1B0D934Ca70660E041f9348785e86d0\",\n },\n {\n protocol: ProtocolType.PancakeV3,\n address: \"0x30bc59E2d16282cA4953b073Fc397766c2c2dF24\",\n },\n ],\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 routerAddress: \"\", // Fill after deployment\n weth: \"0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd\", // WBNB Testnet\n adapters: [\n {\n protocol: ProtocolType.PancakeV3,\n address: \"\", // Fill after deployment\n },\n ],\n};\n\n// ============ API Request Types ============\n\n/**\n * Supported DEX providers\n * Note: Currently only PANCAKEV3 is supported. Use getAvailableProviders() to get the list dynamically.\n */\nexport type Provider = \"PANCAKEV2\" | \"PANCAKEV3\" | 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 /** Slippage tolerance in basis points (default: 50 = 0.5%) */\n slippageBps?: number;\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\"] 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: string;\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 * 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 {\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 };\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 // V3 fee can be read from pool contract, no need to pass in extraData\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","/**\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;\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 : 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 } else {\n // V3 requires Quoter contract or simulation, simplified here\n // Should call PancakeSwap Quoter in practice\n return this.estimateV3AmountOut(pool, tokenIn, amountIn);\n }\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 * ApiClient - Peach Aggregator API client\n *\n * Primary route discovery via API, with local calculation as fallback\n */\n\nimport {\n ApiFindRouteResponse,\n ApiFindRouteData,\n ApiStatusResponse,\n ApiStatusData,\n API_DEFAULTS,\n Provider,\n} from \"../types\";\n\nexport interface ApiClientConfig {\n /** API base URL (required) */\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 if (!config.baseUrl) {\n throw new Error(\"ApiClient requires a baseUrl\");\n }\n this.baseUrl = config.baseUrl;\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","/**\n * PeachClient - Peach Aggregator SDK main entry point\n *\n * Uses API for route discovery with local calculation as fallback\n */\n\nimport { ethers } from \"ethers\";\nimport {\n PeachConfig,\n Quote,\n SwapParams,\n SwapStep,\n SplitRoute,\n ProtocolType,\n DEFAULT_SLIPPAGE_BPS,\n BPS_DENOMINATOR,\n DEFAULT_DEADLINE_SECONDS,\n QuoteOptions,\n API_DEFAULTS,\n ApiFindRouteData,\n} from \"../types\";\nimport { SwapBuilder } from \"../builders/SwapBuilder\";\nimport { RouteDiscovery } from \"./RouteDiscovery\";\nimport { ApiClient, ApiClientConfig } from \"./ApiClient\";\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) 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) 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\nexport interface PeachClientOptions {\n /** API client configuration. Required for API-based routing. If not provided, only local fallback is used. */\n api?: ApiClientConfig;\n /** Whether to use local fallback when API fails or is not configured (default: true) */\n enableFallback?: boolean;\n}\n\nexport class PeachClient {\n private provider: ethers.Provider;\n private config: PeachConfig;\n private routeDiscovery: RouteDiscovery;\n private swapBuilder: SwapBuilder;\n private routerContract: ethers.Contract;\n private apiClient: ApiClient | null;\n private enableFallback: boolean;\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 this.enableFallback = options?.enableFallback ?? true;\n\n // Build adapter mapping\n const adapters = new Map<ProtocolType, string>();\n for (const adapter of config.adapters) {\n adapters.set(adapter.protocol, adapter.address);\n }\n\n this.routeDiscovery = new RouteDiscovery(this.provider, config);\n this.swapBuilder = new SwapBuilder(adapters);\n this.routerContract = new ethers.Contract(\n config.routerAddress,\n PEACH_ROUTER_ABI,\n this.provider\n );\n\n // Only create API client if config is provided\n this.apiClient = options?.api ? new ApiClient(options.api) : null;\n }\n\n /**\n * Get quote via API with optional local fallback\n * @param params.srcToken - Source token address\n * @param params.dstToken - Destination token address\n * @param params.amountIn - Input amount (or output amount if byAmountIn is false)\n * @param params.options - Optional quote options\n * @param params.options.byAmountIn - Calculate output from input (true) or input from output (false). Default: true\n * @param params.options.depth - Route search depth / max hops. Default: 3\n * @param params.options.splitCount - Trade split count for large trades. Default: 20\n * @param params.options.providers - DEX providers to use. Default: [\"PANCAKEV3\"]\n * @param params.options.slippageBps - Slippage tolerance in basis points. Default: 50 (0.5%)\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 slippageBps = DEFAULT_SLIPPAGE_BPS,\n } = options;\n\n // If no API client configured, use local calculation directly\n if (!this.apiClient) {\n return this.getQuoteLocal(srcToken, dstToken, amountIn, slippageBps);\n }\n\n // Try API first\n try {\n const apiData = await this.apiClient.findRoutes({\n from: srcToken,\n target: dstToken,\n amount: amountIn,\n byAmountIn,\n depth,\n splitCount,\n providers,\n });\n\n return this.buildQuoteFromApi(apiData, srcToken, dstToken, slippageBps);\n } catch (error) {\n // If API fails and fallback is enabled, try local calculation\n if (this.enableFallback) {\n console.warn(\n \"[PeachClient] API failed, falling back to local calculation:\",\n error instanceof Error ? error.message : error\n );\n return this.getQuoteLocal(srcToken, dstToken, amountIn, slippageBps);\n }\n\n throw error;\n }\n }\n\n /**\n * Get quote using local route discovery (fallback)\n */\n private async getQuoteLocal(\n srcToken: string,\n dstToken: string,\n amountIn: bigint,\n slippageBps: number\n ): Promise<Quote> {\n const splitRoute = await this.routeDiscovery.findBestRoute(\n srcToken,\n dstToken,\n amountIn\n );\n\n const amountOutMin =\n (splitRoute.totalAmountOut * (BPS_DENOMINATOR - BigInt(slippageBps))) /\n BPS_DENOMINATOR;\n\n const swapParams = this.swapBuilder.build(\n splitRoute,\n srcToken,\n dstToken,\n amountOutMin\n );\n\n const priceImpact = this.calculatePriceImpact(splitRoute);\n\n return {\n srcToken,\n dstToken,\n amountIn,\n amountOut: splitRoute.totalAmountOut,\n amountOutMin,\n priceImpact,\n route: splitRoute,\n params: swapParams,\n gasEstimate: splitRoute.totalGasEstimate,\n };\n }\n\n /**\n * Build Quote from API response\n */\n private buildQuoteFromApi(\n data: ApiFindRouteData,\n srcToken: string,\n dstToken: string,\n slippageBps: number\n ): Quote {\n const amountIn = BigInt(data.amount_in);\n const amountOut = BigInt(data.amount_out);\n const amountOutMin =\n (amountOut * (BPS_DENOMINATOR - BigInt(slippageBps))) / BPS_DENOMINATOR;\n const deadline = BigInt(\n Math.floor(Date.now() / 1000) + DEFAULT_DEADLINE_SECONDS\n );\n\n // Build swap steps from API paths\n // Entry point steps (tokenIn === srcToken) get their specific amountIn\n // Non-entry steps get 0 (consume all from previous step)\n const steps: SwapStep[] = data.paths.map((p) => {\n const isEntryPoint =\n p.token_in.toLowerCase() === srcToken.toLowerCase();\n return {\n adapter: p.adapter,\n pool: p.pool,\n tokenIn: p.token_in,\n tokenOut: p.token_out,\n amountIn: isEntryPoint ? BigInt(p.amount_in) : 0n,\n extraData: p.extra_data || \"0x\",\n };\n });\n\n // Intermediate tokens: outputs of non-final steps that are not the destination\n const intermediateTokens: string[] = [];\n for (let i = 0; i < data.paths.length - 1; i++) {\n const out = data.paths[i].token_out;\n if (out.toLowerCase() !== dstToken.toLowerCase()) {\n intermediateTokens.push(out);\n }\n }\n\n const swapParams: SwapParams = {\n srcToken,\n dstToken,\n amountIn,\n amountOutMin,\n steps,\n intermediateTokens,\n deadline,\n };\n\n // Build route info for display\n const route: SplitRoute = {\n routes: [\n {\n steps: data.paths.map((p) => ({\n pool: {\n address: p.pool,\n token0: p.token_in,\n token1: p.token_out,\n protocol:\n p.provider === \"PANCAKEV3\"\n ? ProtocolType.PancakeV3\n : ProtocolType.PancakeV2,\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 return {\n srcToken,\n dstToken,\n amountIn,\n amountOut,\n amountOutMin,\n priceImpact: parseFloat(data.deviation_ratio || \"0\"),\n route,\n params: swapParams,\n gasEstimate: BigInt(data.gas),\n };\n }\n\n /**\n * Execute swap\n */\n async execute(\n quote: Quote,\n signer: ethers.Signer\n ): Promise<ethers.TransactionResponse> {\n const signerAddress = await signer.getAddress();\n const router = this.routerContract.connect(signer) as ethers.Contract;\n\n // Check and execute approval\n await this.ensureAllowance(\n quote.srcToken,\n signerAddress,\n quote.amountIn,\n signer\n );\n\n // Check if native token\n const isNativeToken =\n quote.srcToken.toLowerCase() === this.config.weth.toLowerCase();\n\n if (isNativeToken) {\n return router.swapETH(this.encodeParams(quote.params), {\n value: quote.amountIn,\n });\n } else {\n return router.swap(this.encodeParams(quote.params));\n }\n }\n\n /**\n * Encode parameters to contract format\n */\n private encodeParams(params: SwapParams): any {\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 };\n }\n\n /**\n * Ensure sufficient allowance\n */\n private async ensureAllowance(\n token: string,\n owner: string,\n amount: bigint,\n signer: ethers.Signer\n ): Promise<void> {\n // Skip native token\n if (token.toLowerCase() === this.config.weth.toLowerCase()) {\n return;\n }\n\n const tokenContract = new ethers.Contract(token, ERC20_ABI, this.provider);\n\n const allowance = await tokenContract.allowance(\n owner,\n this.config.routerAddress\n );\n\n if (allowance < amount) {\n const tokenWithSigner = tokenContract.connect(signer) as ethers.Contract;\n const tx = await tokenWithSigner.approve(\n this.config.routerAddress,\n ethers.MaxUint256\n );\n await tx.wait();\n }\n }\n\n /**\n * Calculate price impact\n */\n private calculatePriceImpact(splitRoute: SplitRoute): number {\n // Simplified calculation: estimate based on input/output ratio\n // Should compare with no-slippage price in practice\n void splitRoute;\n return 0; // TODO: Implement accurate calculation\n }\n\n /**\n * Get token info\n */\n async getTokenInfo(tokenAddress: 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] = await Promise.all([\n token.symbol(),\n token.decimals(),\n ]);\n\n return { symbol, decimals, balance: 0n };\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 API client for direct access (returns null if not configured)\n */\n getApiClient(): ApiClient | null {\n return this.apiClient;\n }\n\n /**\n * Set API base URL. Creates a new API client if not already configured.\n */\n setApiBaseUrl(url: string): void {\n if (this.apiClient) {\n this.apiClient.setBaseUrl(url);\n } else {\n this.apiClient = new ApiClient({ baseUrl: url });\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 if (!this.apiClient) {\n throw new Error(\"API client is not configured\");\n }\n return this.apiClient.getAvailableProviders();\n }\n}\n"],"names":["DEFAULT_SLIPPAGE_BPS","BPS_DENOMINATOR","DEFAULT_DEADLINE_SECONDS","ProtocolType","BSC_MAINNET_CONFIG","BSC_TESTNET_CONFIG","API_DEFAULTS","SwapBuilder","adapters","splitRoute","srcToken","dstToken","amountOutMin","deadlineSeconds","flatSteps","mergedSteps","sortedSteps","steps","intermediateTokens","deadline","i","route","percentage","routeAmount","j","routeStep","poolMap","result","step","key","existing","newStep","s","inDegree","stepMap","graph","producer","producerKey","queue","degree","dependent","newDegree","adapter","pool","tokens","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","RouteDiscovery","provider","config","ethers","amountIn","pools","bestRoute","best","current","tokenA","tokenB","v2Pool","fee","v3Pool","cacheKey","pairAddress","pair","token0","token1","reserves","poolAddress","liquidity","routes","amountOut","tokenIn","tokenOut","isToken0In","reserveIn","reserveOut","amountInWithFee","numerator","denominator","feeMultiplier","DEFAULT_TIMEOUT","ApiClient","params","from","target","amount","byAmountIn","depth","splitCount","providers","queryParams","url","controller","timeoutId","response","ApiError","json","error","message","code","PEACH_ROUTER_ABI","ERC20_ABI","PeachClient","options","slippageBps","apiData","swapParams","priceImpact","data","p","isEntryPoint","out","quote","signer","signerAddress","router","token","owner","tokenContract","tokenAddress","symbol","decimals","userAddress"],"mappings":"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"}