@peachprojects/aggregator-sdk 0.1.3 → 0.1.4
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/dist/index.cjs +2 -2
- package/dist/index.d.ts +140 -1
- package/dist/index.mjs +635 -485
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("ethers"),Y="https://api.peach.ag",J=50,P=10000n,N=500,ee=5e3,R=1200,V=6e4,F=[50,100,200,400,800,1200],Q="0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";function T(f){return f.toLowerCase()===Q.toLowerCase()}const te=4001;var d=(f=>(f.PancakeV2="PancakeV2",f.PancakeV3="PancakeV3",f.PancakeInfinityCl="Pancake_Infinity_Cl",f.PancakeInfinityLb="Pancake_Infinity_Lb",f.UniswapV3="UniswapV3",f.UniswapV4="UniswapV4",f.Dodo="Dodo",f.ThenaV3="ThenaV3",f.ThenaFusion="Thena_Fusion",f.NomiswapStable="Nomiswap_Stable",f.Biswap="Biswap",f.Apeswap="Apeswap",f.BabyDogeSwap="BabyDogeSwap",f.BabySwap="BabySwap",f.PancakeStable="Pancake_Stable",f.ListaStable="Lista_Stable",f.SquadSwapV3="SquadSwap_V3",f.SquadSwapV2="SquadSwap_V2",f.Wombat="Wombat",f.SushiSwapV2="SushiSwap_V2",f.SushiSwapV3="SushiSwap_V3",f))(d||{});const ne={chainId:56,rpcUrl:"https://bsc-dataseed.binance.org",weth:"0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",adapters:[]},re={chainId:97,rpcUrl:"https://bsc-testnet-rpc.publicnode.com",weth:"0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd",adapters:[]};class x extends Error{constructor(e){super(e),this.name="CustomFeeError"}}class U extends Error{constructor(e,t,n){super(e),this.name="ExecuteTimeoutError",this.stage=t,this.txHash=n}}const _={depth:3,splitCount:20,providers:["PANCAKEV2","PANCAKEV3","PANCAKE_INFINITY_CL","PANCAKE_INFINITY_LB","UNISWAPV3","UNISWAPV4","DODO","THENAV3","NOMISWAP_STABLE","BISWAP","APESWAP","BABYDOGESWAP","BABYSWAP","PANCAKE_STABLE","LISTA_STABLE","SQUADSWAP_V3","SQUADSWAP_V2","WOMBAT","THENA_FUSION","SUSHISWAP_V2","SUSHISWAP_V3"],clientVersion:1001500},oe=1e4;function se(f){const e={};return f.forEach((t,n)=>{e[n]=t}),e}class Z{constructor(e={}){this.baseUrl=e.baseUrl||Y,this.timeout=e.timeout||oe,this.extraHeaders=e.headers?{...e.headers}:{}}async findRoutes(e){const{from:t,target:n,amount:r,byAmountIn:o=!0,depth:s=_.depth,splitCount:a=_.splitCount,providers:i=_.providers,includeResponseHeaders:c=!1}=e,u=new URLSearchParams({from:t,target:n,amount:r.toString(),by_amount_in:o.toString(),depth:s.toString(),split_count:a.toString(),providers:i.join(","),v:_.clientVersion.toString()}),l=`${this.baseUrl}/router/find_routes?${u}`,p=new AbortController,A=setTimeout(()=>p.abort(),this.timeout);try{const m=await fetch(l,{method:"GET",headers:{Accept:"application/json",...this.extraHeaders},signal:p.signal});if(clearTimeout(A),!m.ok)throw new g(`API request failed: ${m.status} ${m.statusText}`,m.status);const S=await m.json();if(S.code!==200)throw new g(S.msg||"Route not found",S.code);if(!S.data||!S.data.paths||S.data.paths.length===0)throw new g("No routes found",404);return c?{data:S.data,responseHeaders:se(m.headers)}:S.data}catch(m){throw clearTimeout(A),m instanceof g?m:m instanceof Error?m.name==="AbortError"?new g("API request timeout",408):new g(`API request failed: ${m.message}`,0):new g("Unknown API error",0)}}async getStatus(){const e=`${this.baseUrl}/router/status`,t=new AbortController,n=setTimeout(()=>t.abort(),this.timeout);try{const r=await fetch(e,{method:"GET",headers:{Accept:"application/json",...this.extraHeaders},signal:t.signal});if(clearTimeout(n),!r.ok)throw new g(`API request failed: ${r.status} ${r.statusText}`,r.status);const o=await r.json();if(o.code!==200)throw new g(o.msg||"Failed to get status",o.code);return o.data}catch(r){throw clearTimeout(n),r instanceof g?r:r instanceof Error?r.name==="AbortError"?new g("API request timeout",408):new g(`API request failed: ${r.message}`,0):new g("Unknown API error",0)}}async getAvailableProviders(){return(await this.getStatus()).providers}setBaseUrl(e){this.baseUrl=e}getBaseUrl(){return this.baseUrl}}class g extends Error{constructor(e,t){super(e),this.name="ApiError",this.code=t}}async function L(f,e=V){if(e<=0)return f;let t;try{return await Promise.race([f,new Promise((n,r)=>{t=setTimeout(()=>{r(new U(`Wallet did not settle sendTransaction within ${e}ms.`,"wallet_send"))},e)})])}finally{t&&clearTimeout(t)}}const y=["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, address feeReceiver, uint16 feeBps) 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, bytes32 quoteId, uint256 expectAmountOut, address feeReceiver, uint16 feeBps) params) external payable returns (uint256 amountOut)","function isAdapterRegistered(address adapter) external view returns (bool)","function getAdapterProtocolId(address adapter) external view returns (bytes32)","function WETH() external view returns (address)","function owner() external view returns (address)","function pendingOwner() external view returns (address)","function paused() external view returns (bool)","function maxCustomFeeBps() external view returns (uint16)","function maxProtocolCutBps() external view returns (uint16)","function protocolFeeReceiver() external view returns (address)","function protocolCutBps() external view returns (uint16)"],b=["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)"],ae="0xa0FfB9c1CE1Fe56963B0321B32E7A0302114058b",ie="0xC697d2898e0D09264376196696c51D7aBbbAA4a9",ce="0x28e2ea090877bf75740558f6bfb36a5ffee9e9df",K={PANCAKEV3:d.PancakeV3,PANCAKEV2:d.PancakeV2,PANCAKE_INFINITY_CL:d.PancakeInfinityCl,PANCAKE_INFINITY_LB:d.PancakeInfinityLb,UNISWAPV3:d.UniswapV3,UNISWAPV4:d.UniswapV4,DODO:d.Dodo,THENAV3:d.ThenaV3,NOMISWAP_STABLE:d.NomiswapStable,BISWAP:d.Biswap,APESWAP:d.Apeswap,BABYDOGESWAP:d.BabyDogeSwap,BABYSWAP:d.BabySwap,PANCAKE_STABLE:d.PancakeStable,LISTA_STABLE:d.ListaStable,SQUADSWAP_V3:d.SquadSwapV3,SQUADSWAP_V2:d.SquadSwapV2,WOMBAT:d.Wombat,THENA_FUSION:d.ThenaFusion,SUSHISWAP_V2:d.SushiSwapV2,SUSHISWAP_V3:d.SushiSwapV3},O=class O{constructor(e,t,n){this.config=e,this.provider=t||new h.ethers.JsonRpcProvider(e.rpcUrl),this.routerContract=new h.ethers.Contract(e.routerAddress||h.ethers.ZeroAddress,y,this.provider),this.apiClient=new Z(n?.api)}getRouterAddress(e){const t=e.routerAddress||this.config.routerAddress;if(!t||t===h.ethers.ZeroAddress)throw new Error("No router address available. Provide routerAddress in config or use API-based getQuote.");return t}applySlippage(e,t){if(t<0||t>1e4)throw new Error("slippageBps must be between 0 and 10000");const n=e.amountOutMin*(P-BigInt(t))/P;return{...e,amountOutMin:n}}async swap(e,t,n){const r=this.getRouterAddress(e),{tx:o,method:s}=this.buildSwapTransactionRequest(e,n);let a;return e.srcNative||(a=await this.buildApprovalRequest(e.srcToken,t,e.amountIn,r,n)),{routerAddress:r,method:s,tx:o,approval:a}}async execute(e,t,n){const r=await t.getAddress(),o=await this.swap(e,r,n);try{return o.approval&&await(await this.sendTransactionWithTimeout(t,o.approval.tx,n)).wait(),await this.sendTransactionWithTimeout(t,o.tx,n)}catch(s){const a=s instanceof Error?s.message:String(s),i=/estimateGas/i.test(a),c=/missing revert data/i.test(a)||a.includes("reason=null")&&a.includes("data=null");if(i&&(c||/reason=null|data=null/.test(a))){const u="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.",l=new Error(`${u} (estimateGas/missing revert data)`);throw l.cause=s,l}throw s}}encodeParams(e){return{srcToken:e.srcToken,dstToken:e.dstToken,amountIn:e.amountIn,amountOutMin:e.amountOutMin,steps:e.steps.map(t=>({adapter:t.adapter,pool:t.pool,tokenIn:t.tokenIn,tokenOut:t.tokenOut,amountIn:t.amountIn,extraData:t.extraData})),intermediateTokens:e.intermediateTokens,deadline:e.deadline,quoteId:e.quoteId,expectAmountOut:e.expectAmountOut,feeReceiver:e.feeReceiver,feeBps:e.feeBps}}getProtocolForProvider(e){if(e in K)return K[e];throw new Error(`Unsupported provider: ${e}`)}encodeSwapCalldata(e,t){const n=e.routerAddress??this.config.routerAddress??this.routerContract.target,r=this.applySlippage(e.params,t),o=e.srcNative===!0||e.dstNative===!0,s=this.encodeParams(r);if(o){const a=this.routerContract.interface.encodeFunctionData("swapETH",[s]);return{to:n,data:a,value:e.srcNative?e.amountIn:0n,method:"swapETH"}}else{const a=this.routerContract.interface.encodeFunctionData("swap",[s]);return{to:n,data:a,value:0n,method:"swap"}}}buildSwapTransactionRequest(e,t){const{to:n,data:r,value:o,method:s}=this.encodeSwapCalldata(e,t.slippageBps);return{method:s,tx:this.applyTxOverrides({to:n,data:r,value:o},t,!0)}}async buildApprovalRequest(e,t,n,r,o){const s=await this.getAllowance(e,t,r);if(!(s>=n))return{token:e,owner:t,spender:r,currentAllowance:s,requiredAmount:n,approveAmount:h.ethers.MaxUint256,tx:this.buildApprovalTransactionRequest(e,r,o)}}buildApprovalTransactionRequest(e,t,n){const o=new h.ethers.Interface(b).encodeFunctionData("approve",[t,h.ethers.MaxUint256]);return this.applyTxOverrides({to:e,data:o,value:0n},n,!1)}async getAllowance(e,t,n){return new h.ethers.Contract(e,b,this.provider).allowance(t,n)}applyTxOverrides(e,t,n){const r={...e};return t.gasPrice&&(r.gasPrice=t.gasPrice),n&&t.gasLimit&&(r.gasLimit=t.gasLimit),r}async sendTransactionWithTimeout(e,t,n){const r=n.timeoutMs??V;if(r<=0)return e.sendTransaction(t);const o=e;if(typeof o.sendUncheckedTransaction=="function"&&o.provider){const s=await L(o.sendUncheckedTransaction(t),r),a=await this.waitForTransactionResponse(o.provider,s,r,n.transactionResponsePollingIntervalsMs);if(a.response)return a.response;const i=a.rpcErrors>0?`${a.rpcErrors} transient provider error(s) and ${a.nullResponses} null response(s)`:`${a.nullResponses} null response(s)`;throw new U(`Transaction was broadcast but provider did not return TransactionResponse within ${r}ms (${i}).`,"provider_index",s)}return L(e.sendTransaction(t),r)}async waitForTransactionResponse(e,t,n,r=F){const o=Date.now()+n;let s=0,a=0,i=0;for(;Date.now()<o;){try{const u=await e.getTransaction(t);if(u)return{response:u,nullResponses:a,rpcErrors:i};a++}catch{i++}const c=this.getNextPollingDelay(r,s);s++,await this.delay(Math.min(c,Math.max(25,o-Date.now())))}return{response:null,nullResponses:a,rpcErrors:i}}async delay(e){e<=0||await new Promise(t=>{setTimeout(t,e)})}getNextPollingDelay(e,t){return e.length===0?F.at(-1)??1200:e[Math.min(t,e.length-1)]??1200}async getTokenInfo(e,t){const n=new h.ethers.Contract(e,b,this.provider),[r,o,s]=await Promise.all([n.symbol(),n.decimals(),t?n.balanceOf(t):Promise.resolve(void 0)]);return s===void 0?{symbol:r,decimals:o}:{symbol:r,decimals:o,balance:s}}async getBalance(e,t){return new h.ethers.Contract(e,b,this.provider).balanceOf(t)}async getQuote(e){const{srcToken:t,dstToken:n,amountIn:r,options:o={}}=e,{byAmountIn:s=!0,depth:a=_.depth,splitCount:i=_.splitCount,providers:c=_.providers,deadlineSeconds:u=R,includeResponseHeaders:l=!1,customFee:p}=o;O.validateCustomFee(p);const A=T(t),m=T(n),S=A?this.config.weth:t,v=m?this.config.weth:n;if(l){const{data:C,responseHeaders:k}=await this.apiClient.findRoutes({from:S,target:v,amount:r,byAmountIn:s,depth:a,splitCount:i,providers:c,includeResponseHeaders:!0});return this.buildQuoteFromApi(C,S,v,u,c,A,m,k,p)}const I=await this.apiClient.findRoutes({from:S,target:v,amount:r,byAmountIn:s,depth:a,splitCount:i,providers:c});return this.buildQuoteFromApi(I,S,v,u,c,A,m,void 0,p)}static validateCustomFee(e){if(!e)return;const{feeAccount:t,feeBps:n}=e;if(!Number.isInteger(n)||n<1||n>N)throw new x(`customFee.feeBps must be an integer in [1, ${N}] (5%), got ${n}`);if(!t||!h.ethers.isAddress(t))throw new x(`customFee.feeAccount must be a valid address, got ${t}`);if(t===h.ethers.ZeroAddress)throw new x("customFee.feeAccount must not be the zero address")}resolveRouterAddress(e){const t=e??this.config.routerAddress;if(!t||t===h.ethers.ZeroAddress)throw new Error("No router address available. Pass one explicitly or set config.routerAddress.");return t}async getProtocolFeeConfig(e){const t=this.resolveRouterAddress(e),n=new h.ethers.Contract(t,y,this.provider),[r,o]=await Promise.all([n.protocolFeeReceiver(),n.protocolCutBps()]);return{protocolFeeReceiver:r,protocolCutBps:Number(o)}}async getPlatformConfig(e){return this.getProtocolFeeConfig(e)}async getRouterConfig(e){const t=this.resolveRouterAddress(e),n=new h.ethers.Contract(t,y,this.provider),[r,o,s,a,i,c,u,l]=await Promise.all([n.WETH(),n.owner(),n.pendingOwner(),n.paused(),n.maxCustomFeeBps(),n.maxProtocolCutBps(),n.protocolFeeReceiver(),n.protocolCutBps()]);return{address:t,weth:r,owner:o,pendingOwner:s,paused:!!a,maxCustomFeeBps:Number(i),maxProtocolCutBps:Number(c),protocolFee:{protocolFeeReceiver:u,protocolCutBps:Number(l)}}}filterPathsByProviders(e,t,n,r){const o=new Set(t.map(p=>p.toUpperCase()));let s=e.filter(p=>o.has(p.provider.toUpperCase()));if(s.length===e.length)return s;const a=n.toLowerCase(),i=r.toLowerCase(),c=new Set;c.add(a);let u=!0;for(;u;){u=!1;for(const p of s)c.has(p.token_in.toLowerCase())&&!c.has(p.token_out.toLowerCase())&&(c.add(p.token_out.toLowerCase()),u=!0)}const l=new Set;for(l.add(i),u=!0;u;){u=!1;for(const p of s)l.has(p.token_out.toLowerCase())&&!l.has(p.token_in.toLowerCase())&&(l.add(p.token_in.toLowerCase()),u=!0)}return s.length,s=s.filter(p=>c.has(p.token_in.toLowerCase())&&l.has(p.token_out.toLowerCase())),s}buildQuoteFromRouteData(e,t,n,r,o){const s=this.buildQuoteFromRouteDataInternal(e,t,n,r??R,void 0);return o?.srcNative&&(s.srcNative=!0),o?.dstNative&&(s.dstNative=!0),s}buildQuoteFromApi(e,t,n,r,o,s,a,i,c){const u=this.buildQuoteFromRouteDataInternal(e,t,n,r,o,c);return s&&(u.srcNative=!0),a&&(u.dstNative=!0),i&&(u.responseHeaders=i),u}buildQuoteFromRouteDataInternal(e,t,n,r,o,s){const a=t.toLowerCase();e.paths.length;let i=e.paths.filter(w=>!(!(w.token_in.toLowerCase()===a)&&BigInt(w.amount_in)===0n&&BigInt(w.amount_out)===0n));if(i.length===0)throw new g("All route paths have zero amounts",4001);if(o&&o.length>0&&(i=this.filterPathsByProviders(i,o,t,n),i.length===0))throw new g("No valid route paths remaining after provider filtering",4001);const c=n.toLowerCase();let u=0n,l=0n;for(const w of i)w.token_in.toLowerCase()===a&&(u+=BigInt(w.amount_in)),w.token_out.toLowerCase()===c&&(l+=BigInt(w.amount_out));const p=BigInt(Math.floor(Date.now()/1e3)+r),A=new Map;for(const w of i){const E=w.token_in.toLowerCase();A.set(E,(A.get(E)??0)+1)}const m=new Map,S=i.map(w=>{const E=w.token_in.toLowerCase(),W=(m.get(E)??0)+1;m.set(E,W);const H=A.get(E),z=H>1&&W<H,X=E===a;return{adapter:w.adapter,pool:w.provider.toUpperCase()==="PANCAKE_INFINITY_CL"?ae:w.provider.toUpperCase()==="PANCAKE_INFINITY_LB"?ie:w.provider.toUpperCase()==="UNISWAPV4"?ce:w.pool,tokenIn:w.token_in,tokenOut:w.token_out,amountIn:X||z?BigInt(w.amount_in):0n,extraData:w.extra_data||"0x"}}),v=new Set;for(const w of i)w.token_out.toLowerCase()!==c&&v.add(w.token_out);const I=Array.from(v),C=s?.feeBps??0,k=s?.feeAccount??h.ethers.ZeroAddress,M=l,$=C>0?M*BigInt(C)/P:0n,B=M-$,j={srcToken:t,dstToken:n,amountIn:u,amountOutMin:B,steps:S,intermediateTokens:I,deadline:p,quoteId:e.request_id?h.ethers.id(e.request_id).slice(0,66):h.ethers.ZeroHash,expectAmountOut:B,feeReceiver:k,feeBps:C},q={routes:[{steps:i.map(w=>({pool:{address:w.pool,token0:w.token_in,token1:w.token_out,protocol:this.getProtocolForProvider(w.provider),fee:w.fee_rate?Math.round(parseFloat(w.fee_rate)*1e6):void 0},tokenIn:w.token_in,tokenOut:w.token_out,amountIn:BigInt(w.amount_in),amountOut:BigInt(w.amount_out)})),amountIn:u,amountOut:l,gasEstimate:BigInt(e.gas)}],percentages:[1e4],totalAmountIn:u,totalAmountOut:l,totalGasEstimate:BigInt(e.gas)};if(!e.contracts?.router)throw new g("API response missing contracts.router address",4002);return{srcToken:t,dstToken:n,amountIn:u,amountOut:B,priceImpact:parseFloat(e.deviation_ratio||"0"),route:q,params:j,gasEstimate:BigInt(e.gas),routerAddress:e.contracts?.router,customFee:C>0?{feeAccount:k,feeBps:C,feeAmount:$}:void 0}}async getAvailableProviders(){return this.apiClient.getAvailableProviders()}async simulate(e,t,n,r){const o=n||h.ethers.ZeroAddress,{to:s,data:a,value:i,method:c}=this.encodeSwapCalldata(e,t);if(r){const u=this.getJsonRpcProviderForStateOverrides(),l=i>0n?"0x"+i.toString(16):void 0,p=await u.send("eth_call",[{from:o,to:s,data:a,value:l},"latest",r]),[A]=this.routerContract.interface.decodeFunctionResult(c,p);return{amountOut:A,method:c}}else{const u=await this.provider.call({from:o,to:s,data:a,value:i>0n?i:void 0}),[l]=this.routerContract.interface.decodeFunctionResult(c,u);return{amountOut:l,method:c}}}getJsonRpcProviderForStateOverrides(){if(typeof this.provider.send!="function")throw new Error("stateOverrides require a JsonRpcProvider-compatible provider with send(method, params).");return this.provider}formatSimulateError(e,t,n,r){const o=e instanceof Error?e:new Error(String(e)),s=e;let a="unknown";if(s.reason&&typeof s.reason=="string")a=s.reason;else if(s.revert&&typeof s.revert=="object"){const l=s.revert;l.args&&Array.isArray(l.args)&&(a=l.args.join(", "))}else o.message&&(a=o.message);const i=t.params.steps.map((l,p)=>` Step ${p}: ${l.tokenIn.slice(0,10)}→${l.tokenOut.slice(0,10)} via adapter ${l.adapter.slice(0,10)} pool ${l.pool.slice(0,10)}`).join(`
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("ethers"),Z="https://api.peach.ag",te=50,k=10000n,D=500,ne=5e3,R=1200,M=6e4,F=[50,100,200,400,800,1200],j="0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";function x(m){return m.toLowerCase()===j.toLowerCase()}const re=4001;var d=(m=>(m.PancakeV1="PancakeV1",m.PancakeV2="PancakeV2",m.PancakeV3="PancakeV3",m.PancakeInfinityCl="Pancake_Infinity_Cl",m.PancakeInfinityLb="Pancake_Infinity_Lb",m.UniswapV2="UniswapV2",m.UniswapV3="UniswapV3",m.UniswapV4="UniswapV4",m.Dodo="Dodo",m.ThenaV3="ThenaV3",m.ThenaFusion="Thena_Fusion",m.NomiswapStable="Nomiswap_Stable",m.Biswap="Biswap",m.Apeswap="Apeswap",m.BabyDogeSwap="BabyDogeSwap",m.BabySwap="BabySwap",m.BakerySwap="BakerySwap",m.PancakeStable="Pancake_Stable",m.ListaStable="Lista_Stable",m.SquadSwapV3="SquadSwap_V3",m.SquadSwapV2="SquadSwap_V2",m.Wombat="Wombat",m.SushiSwapV2="SushiSwap_V2",m.SushiSwapV3="SushiSwap_V3",m))(d||{});const se={chainId:56,rpcUrl:"https://bsc-dataseed.binance.org",weth:"0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",adapters:[]},oe={chainId:97,rpcUrl:"https://bsc-testnet-rpc.publicnode.com",weth:"0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd",adapters:[]};class y extends Error{constructor(e){super(e),this.name="CustomFeeError"}}class $ extends Error{constructor(e,t,n){super(e),this.name="ExecuteTimeoutError",this.stage=t,this.txHash=n}}const O="0x000000000022D473030F116dDEE9F6B43aC78BA3",L=(1n<<160n)-1n,P={depth:3,splitCount:20,providers:["PANCAKEV1","PANCAKEV2","PANCAKEV3","PANCAKE_INFINITY_CL","PANCAKE_INFINITY_LB","UNISWAPV2","UNISWAPV3","UNISWAPV4","DODO","THENAV3","NOMISWAP_STABLE","BISWAP","APESWAP","BABYDOGESWAP","BABYSWAP","BAKERYSWAP","PANCAKE_STABLE","LISTA_STABLE","SQUADSWAP_V3","SQUADSWAP_V2","WOMBAT","THENA_FUSION","SUSHISWAP_V2","SUSHISWAP_V3"],clientVersion:1001500},ae=1e4;function ie(m){const e={};return m.forEach((t,n)=>{e[n]=t}),e}class X{constructor(e={}){this.baseUrl=e.baseUrl||Z,this.timeout=e.timeout||ae,this.extraHeaders=e.headers?{...e.headers}:{}}async findRoutes(e){const{from:t,target:n,amount:r,byAmountIn:o=!0,depth:s=P.depth,splitCount:a=P.splitCount,providers:i=P.providers,includeResponseHeaders:c=!1}=e,u=new URLSearchParams({from:t,target:n,amount:r.toString(),by_amount_in:o.toString(),depth:s.toString(),split_count:a.toString(),providers:i.join(","),v:P.clientVersion.toString()}),l=`${this.baseUrl}/router/find_routes?${u}`,p=new AbortController,A=setTimeout(()=>p.abort(),this.timeout);try{const w=await fetch(l,{method:"GET",headers:{Accept:"application/json",...this.extraHeaders},signal:p.signal});if(clearTimeout(A),!w.ok)throw new g(`API request failed: ${w.status} ${w.statusText}`,w.status);const S=await w.json();if(S.code!==200)throw new g(S.msg||"Route not found",S.code);if(!S.data||!S.data.paths||S.data.paths.length===0)throw new g("No routes found",404);return c?{data:S.data,responseHeaders:ie(w.headers)}:S.data}catch(w){throw clearTimeout(A),w instanceof g?w:w instanceof Error?w.name==="AbortError"?new g("API request timeout",408):new g(`API request failed: ${w.message}`,0):new g("Unknown API error",0)}}async getStatus(){const e=`${this.baseUrl}/router/status`,t=new AbortController,n=setTimeout(()=>t.abort(),this.timeout);try{const r=await fetch(e,{method:"GET",headers:{Accept:"application/json",...this.extraHeaders},signal:t.signal});if(clearTimeout(n),!r.ok)throw new g(`API request failed: ${r.status} ${r.statusText}`,r.status);const o=await r.json();if(o.code!==200)throw new g(o.msg||"Failed to get status",o.code);return o.data}catch(r){throw clearTimeout(n),r instanceof g?r:r instanceof Error?r.name==="AbortError"?new g("API request timeout",408):new g(`API request failed: ${r.message}`,0):new g("Unknown API error",0)}}async getAvailableProviders(){return(await this.getStatus()).providers}setBaseUrl(e){this.baseUrl=e}getBaseUrl(){return this.baseUrl}}class g extends Error{constructor(e,t){super(e),this.name="ApiError",this.code=t}}async function V(m,e=M){if(e<=0)return m;let t;try{return await Promise.race([m,new Promise((n,r)=>{t=setTimeout(()=>{r(new $(`Wallet did not settle sendTransaction within ${e}ms.`,"wallet_send"))},e)})])}finally{t&&clearTimeout(t)}}const 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, address feeReceiver, uint16 feeBps) 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, bytes32 quoteId, uint256 expectAmountOut, address feeReceiver, uint16 feeBps) params) external payable returns (uint256 amountOut)","function swapWithPermit2((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, address feeReceiver, uint16 feeBps) params, (((address token, uint160 amount, uint48 expiration, uint48 nonce) details, address spender, uint256 sigDeadline) permitSingle, bytes signature) p2) external returns (uint256 amountOut)","function swapETHWithPermit2((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, address feeReceiver, uint16 feeBps) params, (((address token, uint160 amount, uint48 expiration, uint48 nonce) details, address spender, uint256 sigDeadline) permitSingle, bytes signature) p2) external returns (uint256 amountOut)","function isAdapterRegistered(address adapter) external view returns (bool)","function getAdapterProtocolId(address adapter) external view returns (bytes32)","function WETH() external view returns (address)","function owner() external view returns (address)","function pendingOwner() external view returns (address)","function paused() external view returns (bool)","function maxCustomFeeBps() external view returns (uint16)","function maxProtocolCutBps() external view returns (uint16)","function protocolFeeReceiver() external view returns (address)","function protocolCutBps() external view returns (uint16)"],T=["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)"],ce=["function allowance(address user, address token, address spender) external view returns (uint160 amount, uint48 expiration, uint48 nonce)"],ue={PermitDetails:[{name:"token",type:"address"},{name:"amount",type:"uint160"},{name:"expiration",type:"uint48"},{name:"nonce",type:"uint48"}],PermitSingle:[{name:"details",type:"PermitDetails"},{name:"spender",type:"address"},{name:"sigDeadline",type:"uint256"}]},de="0xa0FfB9c1CE1Fe56963B0321B32E7A0302114058b",le="0xC697d2898e0D09264376196696c51D7aBbbAA4a9",pe="0x28e2ea090877bf75740558f6bfb36a5ffee9e9df",G={PANCAKEV3:d.PancakeV3,PANCAKEV2:d.PancakeV2,PANCAKEV1:d.PancakeV1,PANCAKE_INFINITY_CL:d.PancakeInfinityCl,PANCAKE_INFINITY_LB:d.PancakeInfinityLb,UNISWAPV2:d.UniswapV2,UNISWAPV3:d.UniswapV3,UNISWAPV4:d.UniswapV4,DODO:d.Dodo,THENAV3:d.ThenaV3,NOMISWAP_STABLE:d.NomiswapStable,BISWAP:d.Biswap,APESWAP:d.Apeswap,BABYDOGESWAP:d.BabyDogeSwap,BABYSWAP:d.BabySwap,BAKERYSWAP:d.BakerySwap,PANCAKE_STABLE:d.PancakeStable,LISTA_STABLE:d.ListaStable,SQUADSWAP_V3:d.SquadSwapV3,SQUADSWAP_V2:d.SquadSwapV2,WOMBAT:d.Wombat,THENA_FUSION:d.ThenaFusion,SUSHISWAP_V2:d.SushiSwapV2,SUSHISWAP_V3:d.SushiSwapV3},C=class C{constructor(e,t,n){this.config=e,this.provider=t||new h.ethers.JsonRpcProvider(e.rpcUrl),this.routerContract=new h.ethers.Contract(e.routerAddress||h.ethers.ZeroAddress,N,this.provider),this.apiClient=new X(n?.api)}getRouterAddress(e){const t=e.routerAddress||this.config.routerAddress;if(!t||t===h.ethers.ZeroAddress)throw new Error("No router address available. Provide routerAddress in config or use API-based getQuote.");return t}applySlippage(e,t){if(t<0||t>1e4)throw new Error("slippageBps must be between 0 and 10000");const n=e.amountOutMin*(k-BigInt(t))/k;return{...e,amountOutMin:n}}async swap(e,t,n){const r=this.getRouterAddress(e),{tx:o,method:s}=this.buildSwapTransactionRequest(e,n);let a;return e.srcNative||(a=await this.buildApprovalRequest(e.srcToken,t,e.amountIn,r,n)),{routerAddress:r,method:s,tx:o,approval:a}}async execute(e,t,n){const r=await t.getAddress(),o=await this.swap(e,r,n);try{return o.approval&&await(await this.sendTransactionWithTimeout(t,o.approval.tx,n)).wait(),await this.sendTransactionWithTimeout(t,o.tx,n)}catch(s){const a=s instanceof Error?s.message:String(s),i=/estimateGas/i.test(a),c=/missing revert data/i.test(a)||a.includes("reason=null")&&a.includes("data=null");if(i&&(c||/reason=null|data=null/.test(a))){const u="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.",l=new Error(`${u} (estimateGas/missing revert data)`);throw l.cause=s,l}throw s}}encodeParams(e){return{srcToken:e.srcToken,dstToken:e.dstToken,amountIn:e.amountIn,amountOutMin:e.amountOutMin,steps:e.steps.map(t=>({adapter:t.adapter,pool:t.pool,tokenIn:t.tokenIn,tokenOut:t.tokenOut,amountIn:t.amountIn,extraData:t.extraData})),intermediateTokens:e.intermediateTokens,deadline:e.deadline,quoteId:e.quoteId,expectAmountOut:e.expectAmountOut,feeReceiver:e.feeReceiver,feeBps:e.feeBps}}getProtocolForProvider(e){if(e in G)return G[e];throw new Error(`Unsupported provider: ${e}`)}encodeSwapCalldata(e,t){const n=e.routerAddress??this.config.routerAddress??this.routerContract.target,r=this.applySlippage(e.params,t),o=e.srcNative===!0||e.dstNative===!0,s=this.encodeParams(r);if(o){const a=this.routerContract.interface.encodeFunctionData("swapETH",[s]);return{to:n,data:a,value:e.srcNative?e.amountIn:0n,method:"swapETH"}}else{const a=this.routerContract.interface.encodeFunctionData("swap",[s]);return{to:n,data:a,value:0n,method:"swap"}}}buildSwapTransactionRequest(e,t){const{to:n,data:r,value:o,method:s}=this.encodeSwapCalldata(e,t.slippageBps);return{method:s,tx:this.applyTxOverrides({to:n,data:r,value:o},t,!0)}}async buildApprovalRequest(e,t,n,r,o){const s=await this.getAllowance(e,t,r);if(!(s>=n))return{token:e,owner:t,spender:r,currentAllowance:s,requiredAmount:n,approveAmount:h.ethers.MaxUint256,tx:this.buildApprovalTransactionRequest(e,r,o)}}buildApprovalTransactionRequest(e,t,n){const o=new h.ethers.Interface(T).encodeFunctionData("approve",[t,h.ethers.MaxUint256]);return this.applyTxOverrides({to:e,data:o,value:0n},n,!1)}async getAllowance(e,t,n){return new h.ethers.Contract(e,T,this.provider).allowance(t,n)}async getPermit2Allowance(e,t,n){const r=new h.ethers.Contract(O,ce,this.provider),o=n??this.resolveRouterAddress(),[s,a,i]=await r.allowance(t,e,o);return{amount:BigInt(s),expiration:Number(a),nonce:Number(i)}}async buildPermit2TokenApproval(e,t,n,r={}){if(await this.getAllowance(e,t,O)>=n)return;const a=new h.ethers.Interface(T).encodeFunctionData("approve",[O,h.ethers.MaxUint256]),i={to:e,data:a,value:0n};return r.gasPrice&&(i.gasPrice=r.gasPrice),i}async buildPermit2TypedData(e,t,n={}){if(e.srcNative)throw new Error("Permit2 cannot be used for native-token input; use swapETH instead.");const r=this.getRouterAddress(e);this.assertUint160(e.amountIn);const{nonce:o}=await this.getPermit2Allowance(e.srcToken,t,r),s=Math.floor(Date.now()/1e3),a=n.permitAmount??L;this.assertUint160(a);const i={details:{token:e.srcToken,amount:a,expiration:n.expiration??s+720*60*60,nonce:o},spender:r,sigDeadline:n.sigDeadline??BigInt(s+1800)};return{domain:{name:"Permit2",chainId:this.config.chainId,verifyingContract:O},types:ue,values:i}}encodeSwapWithPermit2Calldata(e,t,n,r){if(e.srcNative)throw new Error("Permit2 cannot be used for native-token input; use swapETH instead.");const o=this.getRouterAddress(e),s=this.applySlippage(e.params,t),a=this.encodeParams(s),i={permitSingle:n,signature:r},c=e.dstNative?"swapETHWithPermit2":"swapWithPermit2",u=this.routerContract.interface.encodeFunctionData(c,[a,i]);return{to:o,data:u,value:0n,method:c}}async swapWithPermit2(e,t,n){if(e.srcNative)throw new Error("Permit2 cannot be used for native-token input; use swapETH instead.");const r=this.getRouterAddress(e);this.assertUint160(e.amountIn);const o=await this.buildPermit2TokenApproval(e.srcToken,t,e.amountIn,n),s=await this.getPermit2Allowance(e.srcToken,t,r),a=Math.floor(Date.now()/1e3),i=!o&&s.amount>=e.amountIn&&s.expiration>a;let c,u;i?u={details:{token:e.srcToken,amount:s.amount,expiration:s.expiration,nonce:s.nonce},spender:r,sigDeadline:0n}:(c=await this.buildPermit2TypedData(e,t,n),u=c.values);const l=e.dstNative?"swapETHWithPermit2":"swapWithPermit2";return{routerAddress:r,method:l,permit2Approval:o,typedData:c,permitSingle:u,buildSwapTx:p=>{const{to:A,data:w,value:S}=this.encodeSwapWithPermit2Calldata(e,n.slippageBps,u,p);return this.applyTxOverrides({to:A,data:w,value:S},n,!0)}}}assertUint160(e){if(e>L)throw new Error(`amount ${e} exceeds Permit2 uint160 max`)}applyTxOverrides(e,t,n){const r={...e};return t.gasPrice&&(r.gasPrice=t.gasPrice),n&&t.gasLimit&&(r.gasLimit=t.gasLimit),r}async sendTransactionWithTimeout(e,t,n){const r=n.timeoutMs??M;if(r<=0)return e.sendTransaction(t);const o=e;if(typeof o.sendUncheckedTransaction=="function"&&o.provider){const s=await V(o.sendUncheckedTransaction(t),r),a=await this.waitForTransactionResponse(o.provider,s,r,n.transactionResponsePollingIntervalsMs);if(a.response)return a.response;const i=a.rpcErrors>0?`${a.rpcErrors} transient provider error(s) and ${a.nullResponses} null response(s)`:`${a.nullResponses} null response(s)`;throw new $(`Transaction was broadcast but provider did not return TransactionResponse within ${r}ms (${i}).`,"provider_index",s)}return V(e.sendTransaction(t),r)}async waitForTransactionResponse(e,t,n,r=F){const o=Date.now()+n;let s=0,a=0,i=0;for(;Date.now()<o;){try{const u=await e.getTransaction(t);if(u)return{response:u,nullResponses:a,rpcErrors:i};a++}catch{i++}const c=this.getNextPollingDelay(r,s);s++,await this.delay(Math.min(c,Math.max(25,o-Date.now())))}return{response:null,nullResponses:a,rpcErrors:i}}async delay(e){e<=0||await new Promise(t=>{setTimeout(t,e)})}getNextPollingDelay(e,t){return e.length===0?F.at(-1)??1200:e[Math.min(t,e.length-1)]??1200}async getTokenInfo(e,t){const n=new h.ethers.Contract(e,T,this.provider),[r,o,s]=await Promise.all([n.symbol(),n.decimals(),t?n.balanceOf(t):Promise.resolve(void 0)]);return s===void 0?{symbol:r,decimals:o}:{symbol:r,decimals:o,balance:s}}async getBalance(e,t){return new h.ethers.Contract(e,T,this.provider).balanceOf(t)}async getQuote(e){const{srcToken:t,dstToken:n,amountIn:r,options:o={}}=e,{byAmountIn:s=!0,depth:a=P.depth,splitCount:i=P.splitCount,providers:c=P.providers,deadlineSeconds:u=R,includeResponseHeaders:l=!1,customFee:p}=o;C.validateCustomFee(p);const A=x(t),w=x(n),S=A?this.config.weth:t,v=w?this.config.weth:n;if(l){const{data:_,responseHeaders:b}=await this.apiClient.findRoutes({from:S,target:v,amount:r,byAmountIn:s,depth:a,splitCount:i,providers:c,includeResponseHeaders:!0});return this.buildQuoteFromApi(_,S,v,u,c,A,w,b,p)}const E=await this.apiClient.findRoutes({from:S,target:v,amount:r,byAmountIn:s,depth:a,splitCount:i,providers:c});return this.buildQuoteFromApi(E,S,v,u,c,A,w,void 0,p)}static validateCustomFee(e){if(!e)return;const{feeAccount:t,feeBps:n}=e;if(!Number.isInteger(n)||n<1||n>D)throw new y(`customFee.feeBps must be an integer in [1, ${D}] (5%), got ${n}`);if(!t||!h.ethers.isAddress(t))throw new y(`customFee.feeAccount must be a valid address, got ${t}`);if(t===h.ethers.ZeroAddress)throw new y("customFee.feeAccount must not be the zero address")}resolveRouterAddress(e){const t=e??this.config.routerAddress;if(!t||t===h.ethers.ZeroAddress)throw new Error("No router address available. Pass one explicitly or set config.routerAddress.");return t}async getProtocolFeeConfig(e){const t=this.resolveRouterAddress(e),n=new h.ethers.Contract(t,N,this.provider),[r,o]=await Promise.all([n.protocolFeeReceiver(),n.protocolCutBps()]);return{protocolFeeReceiver:r,protocolCutBps:Number(o)}}async getPlatformConfig(e){return this.getProtocolFeeConfig(e)}async getRouterConfig(e){const t=this.resolveRouterAddress(e),n=new h.ethers.Contract(t,N,this.provider),[r,o,s,a,i,c,u,l]=await Promise.all([n.WETH(),n.owner(),n.pendingOwner(),n.paused(),n.maxCustomFeeBps(),n.maxProtocolCutBps(),n.protocolFeeReceiver(),n.protocolCutBps()]);return{address:t,weth:r,owner:o,pendingOwner:s,paused:!!a,maxCustomFeeBps:Number(i),maxProtocolCutBps:Number(c),protocolFee:{protocolFeeReceiver:u,protocolCutBps:Number(l)}}}filterPathsByProviders(e,t,n,r){const o=new Set(t.map(p=>p.toUpperCase()));let s=e.filter(p=>o.has(p.provider.toUpperCase()));if(s.length===e.length)return s;const a=n.toLowerCase(),i=r.toLowerCase(),c=new Set;c.add(a);let u=!0;for(;u;){u=!1;for(const p of s)c.has(p.token_in.toLowerCase())&&!c.has(p.token_out.toLowerCase())&&(c.add(p.token_out.toLowerCase()),u=!0)}const l=new Set;for(l.add(i),u=!0;u;){u=!1;for(const p of s)l.has(p.token_out.toLowerCase())&&!l.has(p.token_in.toLowerCase())&&(l.add(p.token_in.toLowerCase()),u=!0)}return s.length,s=s.filter(p=>c.has(p.token_in.toLowerCase())&&l.has(p.token_out.toLowerCase())),s}buildQuoteFromRouteData(e,t,n,r,o){const s=this.buildQuoteFromRouteDataInternal(e,t,n,r??R,void 0);return o?.srcNative&&(s.srcNative=!0),o?.dstNative&&(s.dstNative=!0),s}buildQuoteFromApi(e,t,n,r,o,s,a,i,c){const u=this.buildQuoteFromRouteDataInternal(e,t,n,r,o,c);return s&&(u.srcNative=!0),a&&(u.dstNative=!0),i&&(u.responseHeaders=i),u}buildQuoteFromRouteDataInternal(e,t,n,r,o,s){const a=t.toLowerCase();e.paths.length;let i=e.paths.filter(f=>!(!(f.token_in.toLowerCase()===a)&&BigInt(f.amount_in)===0n&&BigInt(f.amount_out)===0n));if(i.length===0)throw new g("All route paths have zero amounts",4001);if(o&&o.length>0&&(i=this.filterPathsByProviders(i,o,t,n),i.length===0))throw new g("No valid route paths remaining after provider filtering",4001);const c=n.toLowerCase();let u=0n,l=0n;for(const f of i)f.token_in.toLowerCase()===a&&(u+=BigInt(f.amount_in)),f.token_out.toLowerCase()===c&&(l+=BigInt(f.amount_out));const p=BigInt(Math.floor(Date.now()/1e3)+r),A=new Map;for(const f of i){const I=f.token_in.toLowerCase();A.set(I,(A.get(I)??0)+1)}const w=new Map,S=i.map(f=>{const I=f.token_in.toLowerCase(),K=(w.get(I)??0)+1;w.set(I,K);const Y=A.get(I),q=Y>1&&K<Y,ee=I===a;return{adapter:f.adapter,pool:f.provider.toUpperCase()==="PANCAKE_INFINITY_CL"?de:f.provider.toUpperCase()==="PANCAKE_INFINITY_LB"?le:f.provider.toUpperCase()==="UNISWAPV4"?pe:f.pool,tokenIn:f.token_in,tokenOut:f.token_out,amountIn:ee||q?BigInt(f.amount_in):0n,extraData:f.extra_data||"0x"}}),v=new Set;for(const f of i)f.token_out.toLowerCase()!==c&&v.add(f.token_out);const E=Array.from(v),_=s?.feeBps??0,b=s?.feeAccount??h.ethers.ZeroAddress,W=l,H=_>0?W*BigInt(_)/k:0n,B=W-H,z={srcToken:t,dstToken:n,amountIn:u,amountOutMin:B,steps:S,intermediateTokens:E,deadline:p,quoteId:e.request_id?h.ethers.id(e.request_id).slice(0,66):h.ethers.ZeroHash,expectAmountOut:B,feeReceiver:b,feeBps:_},J={routes:[{steps:i.map(f=>({pool:{address:f.pool,token0:f.token_in,token1:f.token_out,protocol:this.getProtocolForProvider(f.provider),fee:f.fee_rate?Math.round(parseFloat(f.fee_rate)*1e6):void 0},tokenIn:f.token_in,tokenOut:f.token_out,amountIn:BigInt(f.amount_in),amountOut:BigInt(f.amount_out)})),amountIn:u,amountOut:l,gasEstimate:BigInt(e.gas)}],percentages:[1e4],totalAmountIn:u,totalAmountOut:l,totalGasEstimate:BigInt(e.gas)};if(!e.contracts?.router)throw new g("API response missing contracts.router address",4002);return{srcToken:t,dstToken:n,amountIn:u,amountOut:B,priceImpact:parseFloat(e.deviation_ratio||"0"),route:J,params:z,gasEstimate:BigInt(e.gas),routerAddress:e.contracts?.router,customFee:_>0?{feeAccount:b,feeBps:_,feeAmount:H}:void 0}}async getAvailableProviders(){return this.apiClient.getAvailableProviders()}async simulate(e,t,n,r){const o=n||h.ethers.ZeroAddress,{to:s,data:a,value:i,method:c}=this.encodeSwapCalldata(e,t);if(r){const u=this.getJsonRpcProviderForStateOverrides(),l=i>0n?"0x"+i.toString(16):void 0,p=await u.send("eth_call",[{from:o,to:s,data:a,value:l},"latest",r]),[A]=this.routerContract.interface.decodeFunctionResult(c,p);return{amountOut:A,method:c}}else{const u=await this.provider.call({from:o,to:s,data:a,value:i>0n?i:void 0}),[l]=this.routerContract.interface.decodeFunctionResult(c,u);return{amountOut:l,method:c}}}getJsonRpcProviderForStateOverrides(){if(typeof this.provider.send!="function")throw new Error("stateOverrides require a JsonRpcProvider-compatible provider with send(method, params).");return this.provider}formatSimulateError(e,t,n,r){const o=e instanceof Error?e:new Error(String(e)),s=e;let a="unknown";if(s.reason&&typeof s.reason=="string")a=s.reason;else if(s.revert&&typeof s.revert=="object"){const l=s.revert;l.args&&Array.isArray(l.args)&&(a=l.args.join(", "))}else o.message&&(a=o.message);const i=t.params.steps.map((l,p)=>` Step ${p}: ${l.tokenIn.slice(0,10)}→${l.tokenOut.slice(0,10)} via adapter ${l.adapter.slice(0,10)} pool ${l.pool.slice(0,10)}`).join(`
|
|
2
2
|
`),c=[`Simulate ${n} failed: ${a}`,` Route: ${t.srcToken} → ${t.dstToken}`,` AmountIn: ${t.amountIn}`,` AmountOutMin: ${t.params.amountOutMin}`,` Router: ${t.routerAddress}`,` Caller: ${r}`,` Steps (${t.params.steps.length}):`,i].join(`
|
|
3
|
-
`),u=new Error(c);return u.cause=o,u.reason=a,u}async findFailingStep(e,t,n,r,o,s=50){const a=e.params.steps;if(!a.length)return null;const i=o!=null?this.normalizeRevertReason(o):void 0;for(let c=1;c<=a.length;c++){const u=this.buildTruncatedQuote(e,c);let l,p;try{l=(await this.simulate(u,t,n,r)).amountOut}catch(v){p=v}if(p!==void 0){const v=this.normalizeRevertReason(p),I=p?.shortMessage??p?.reason??p?.message;if(i==null||v===i)return{stepIndex:c-1,step:a[c-1],error:p,revertMessage:typeof I=="string"?I:v,fullRouteRevertMessage:i};continue}if(l===void 0)continue;const A=this.getQuotedStepAmountOut(e,c-1);if(A===void 0||A===0n)continue;const m=A>l?A-l:0n;if(m===0n)continue;const S=m*P/A;if(S>BigInt(s)){const v=`Step ${c-1} deviates: quoted=${A} simulated=${l} shortfall=${m} (${S}bps, threshold=${s}bps). Likely cause: transfer-tax on ${a[c-1].tokenOut}, stale pool data, or dynamic-fee drift.`;return{stepIndex:c-1,step:a[c-1],error:new Error(v),revertMessage:v,fullRouteRevertMessage:i}}}return null}normalizeRevertReason(e){if(e==null)return;const t=e;if(typeof t.reason=="string"&&t.reason.length>0)return t.reason;const n=this.extractRevertData(e);if(n&&n.length>=10){const c=n.slice(0,10).toLowerCase();return O.KNOWN_ERROR_SELECTORS[c]??c}const r=t.shortMessage??t.message;if(typeof r!="string")return;const o=r.match(/reason="([^"]+)"/);if(o)return o[1];const s=r.match(/reverted:\s*"([^"]+)"/);if(s)return s[1];const a=r.match(/execution reverted:\s*"([^"]+)"/);if(a)return a[1];const i=r.match(/\b0x[0-9a-fA-F]{8}\b/);if(i){const c=i[0].toLowerCase();return O.KNOWN_ERROR_SELECTORS[c]??c}}extractRevertData(e){const t=new Set,n=[e];for(;n.length;){const r=n.shift();if(r==null||typeof r!="object"||t.has(r))continue;t.add(r);const o=r,s=o.data;if(typeof s=="string"&&/^0x[0-9a-fA-F]{8,}$/.test(s))return s;for(const a of["info","error","cause","revert","originalError"])o[a]&&n.push(o[a])}}getQuotedStepAmountOut(e,t){return e.route?.routes?.[0]?.steps?.[t]?.amountOut}buildTruncatedQuote(e,t){const n=e.params.steps.slice(0,t),o=n[n.length-1].tokenOut,s=o.toLowerCase(),a=e.srcToken.toLowerCase(),i=t===e.params.steps.length,c=new Set,u=[];for(const l of n){const p=l.tokenOut.toLowerCase();p===s||p===a||c.has(p)||(c.add(p),u.push(l.tokenOut))}return{...e,dstToken:o,dstNative:i?e.dstNative:!1,amountOut:i?e.amountOut:0n,customFee:i?e.customFee:void 0,params:{...e.params,dstToken:o,amountOutMin:i?e.params.amountOutMin:0n,expectAmountOut:i?e.params.expectAmountOut:0n,feeReceiver:i?e.params.feeReceiver:h.ethers.ZeroAddress,feeBps:i?e.params.feeBps:0,steps:n,intermediateTokens:u}}}buildStateOverrides(e,t,n,r,o,s){if(s?.isNative||T(e))return{};if(!n||n===h.ethers.ZeroAddress)throw new Error("buildStateOverrides requires a non-zero routerAddress.");const a=h.ethers.AbiCoder.defaultAbiCoder(),i=r||h.ethers.parseUnits("1000000",18),c=o??n,u=h.ethers.zeroPadValue(h.ethers.toBeHex(i),32),l=h.ethers.zeroPadValue(h.ethers.toBeHex(h.ethers.MaxUint256),32),p={},A=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,50,51,52,100,101,102];for(const m of A){const S=h.ethers.keccak256(a.encode(["address","uint256"],[t,m]));p[S]=u;const v=h.ethers.keccak256(a.encode(["address","uint256"],[t,m])),I=h.ethers.keccak256(a.encode(["address","bytes32"],[c,v]));p[I]=l}return{[e.toLowerCase()]:{stateDiff:p}}}};O.KNOWN_ERROR_SELECTORS={"0x2c19b8b8":"InsufficientOutput"};let D=O;const G=["function token0() external view returns (address)","function token1() external view returns (address)","function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)"],ue=["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)"],de="0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73",le=["function getPair(address tokenA, address tokenB) external view returns (address pair)"],pe="0x86407bEa2078ea5f5EB5A52B2caA963bC1F889Da",he=["function getPair(address tokenA, address tokenB) external view returns (address pair)"],fe="0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865",we=["function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool)"],me=[100,500,2500,1e4],Ae=60000n,Se=120000n,ve=100000n;class ge{constructor(e,t){this.poolCache=new Map,this.provider=e,this.v2Factory=new h.ethers.Contract(de,le,e),this.v3Factory=new h.ethers.Contract(fe,we,e),this.babySwapFactory=new h.ethers.Contract(pe,he,e)}async findBestRoute(e,t,n){const r=await this.discoverPools(e,t);if(r.length===0)throw new Error(`No pools found for ${e} -> ${t}`);const s=(await this.calculateRoutes(r,e,t,n)).reduce((a,i)=>i.amountOut>a.amountOut?i:a);return{routes:[s],percentages:[1e4],totalAmountIn:n,totalAmountOut:s.amountOut,totalGasEstimate:s.gasEstimate}}async discoverPools(e,t){const n=[];try{const r=await this.findV2Pool(e,t);r&&n.push(r)}catch{}for(const r of me)try{const o=await this.findV3Pool(e,t,r);o&&n.push(o)}catch{}try{const r=await this.findBabySwapPool(e,t);r&&n.push(r)}catch{}return n}async findV2Pool(e,t){const n=`v2-${e}-${t}`;if(this.poolCache.has(n))return this.poolCache.get(n);const r=await this.v2Factory.getPair(e,t);if(r===h.ethers.ZeroAddress)return null;const o=new h.ethers.Contract(r,G,this.provider),[s,a,i]=await Promise.all([o.token0(),o.token1(),o.getReserves()]),c={address:r,token0:s,token1:a,protocol:d.PancakeV2,reserve0:i.reserve0,reserve1:i.reserve1};return this.poolCache.set(n,c),c}async findV3Pool(e,t,n){const r=`v3-${e}-${t}-${n}`;if(this.poolCache.has(r))return this.poolCache.get(r);const o=await this.v3Factory.getPool(e,t,n);if(o===h.ethers.ZeroAddress)return null;const s=new h.ethers.Contract(o,ue,this.provider),[a,i,c]=await Promise.all([s.token0(),s.token1(),s.liquidity()]);if(c===0n)return null;const u={address:o,token0:a,token1:i,protocol:d.PancakeV3,fee:n,liquidity:c};return this.poolCache.set(r,u),u}async findBabySwapPool(e,t){const n=`babyswap-${e}-${t}`;if(this.poolCache.has(n))return this.poolCache.get(n);const r=await this.babySwapFactory.getPair(e,t);if(r===h.ethers.ZeroAddress)return null;const o=new h.ethers.Contract(r,G,this.provider),[s,a,i]=await Promise.all([o.token0(),o.token1(),o.getReserves()]),c={address:r,token0:s,token1:a,protocol:d.BabySwap,reserve0:i.reserve0,reserve1:i.reserve1};return this.poolCache.set(n,c),c}async calculateRoutes(e,t,n,r){const o=[];for(const s of e)try{const a=await this.getAmountOut(s,t,n,r);a>0n&&o.push({steps:[{pool:s,tokenIn:t,tokenOut:n,amountIn:r,amountOut:a}],amountIn:r,amountOut:a,gasEstimate:s.protocol===d.PancakeV2||s.protocol===d.BabySwap?Ae:s.protocol===d.Dodo?ve:Se})}catch{}return o}async getAmountOut(e,t,n,r){if(e.protocol===d.PancakeV2)return this.getV2AmountOut(e,t,r);if(e.protocol===d.BabySwap)return this.getBabySwapAmountOut(e,t,r);if(e.protocol===d.Dodo)throw new Error("DODO amount out not supported in local route discovery");return this.estimateV3AmountOut(e,t,r)}getV2AmountOut(e,t,n){const r=t.toLowerCase()===e.token0.toLowerCase(),[o,s]=r?[e.reserve0,e.reserve1]:[e.reserve1,e.reserve0],a=n*9975n,i=a*s,c=o*10000n+a;return i/c}getBabySwapAmountOut(e,t,n){const r=t.toLowerCase()===e.token0.toLowerCase(),[o,s]=r?[e.reserve0,e.reserve1]:[e.reserve1,e.reserve0],a=n*998n,i=a*s,c=o*1000n+a;return i/c}estimateV3AmountOut(e,t,n){const o=1000000n-BigInt(e.fee||2500);return n*o/1000000n}clearCache(){this.poolCache.clear()}}class Ee{constructor(e){this.adapters=e}build(e,t,n,r,o=R){const s=this.flattenRoutes(e),a=this.mergeIdenticalPools(s),i=this.topologicalSort(a,t),c=this.convertToSwapSteps(i),u=this.extractIntermediates(i,t,n),l=BigInt(Math.floor(Date.now()/1e3)+o);return{srcToken:t,dstToken:n,amountIn:e.totalAmountIn,amountOutMin:r,steps:c,intermediateTokens:u,deadline:l,quoteId:h.ethers.ZeroHash,expectAmountOut:e.totalAmountOut,feeReceiver:h.ethers.ZeroAddress,feeBps:0}}flattenRoutes(e){const t=[];for(let n=0;n<e.routes.length;n++){const r=e.routes[n],o=e.percentages[n],s=e.totalAmountIn*BigInt(o)/P;for(let a=0;a<r.steps.length;a++){const i=r.steps[a];t.push({pool:i.pool,tokenIn:i.tokenIn,tokenOut:i.tokenOut,amountIn:a===0?s:0n,routeIndex:n,stepIndex:a})}}return t}mergeIdenticalPools(e){const t=new Map,n=[];for(const r of e){const o=`${r.pool.address}-${r.tokenIn}-${r.tokenOut}`;if(t.has(o)){const s=t.get(o);r.amountIn>0n&&s.amountIn>0n&&(s.amountIn=0n)}else{const s={...r};t.set(o,s),n.push(s)}}for(const r of n){const o=`${r.pool.address}-${r.tokenIn}-${r.tokenOut}`;e.filter(a=>`${a.pool.address}-${a.tokenIn}-${a.tokenOut}`===o).length>1&&(r.amountIn=0n)}return n}topologicalSort(e,t){const n=new Map,r=new Map,o=new Map;for(const i of e){const c=this.stepKey(i);r.set(c,i),n.set(c,0),o.set(c,[])}for(const i of e){const c=this.stepKey(i);if(i.tokenIn!==t){for(const u of e)if(u.tokenOut===i.tokenIn){const l=this.stepKey(u);l!==c&&(o.get(l).push(c),n.set(c,(n.get(c)||0)+1))}}}const s=[];for(const[i,c]of n)c===0&&s.push(i);const a=[];for(;s.length>0;){const i=s.shift(),c=r.get(i);a.push(c);for(const u of o.get(i)||[]){const l=(n.get(u)||0)-1;n.set(u,l),l===0&&s.push(u)}}if(a.length!==e.length)throw new Error("Circular dependency detected in route");return a}convertToSwapSteps(e){return e.map(t=>{const n=this.adapters.get(t.pool.protocol);if(!n)throw new Error(`No adapter for protocol: ${t.pool.protocol}`);return{adapter:n,pool:t.pool.address,tokenIn:t.tokenIn,tokenOut:t.tokenOut,amountIn:t.amountIn,extraData:this.encodeExtraData(t.pool)}})}encodeExtraData(e){switch(e.protocol){case d.PancakeV2:return"0x";case d.PancakeV3:case d.UniswapV3:case d.UniswapV4:case d.SquadSwapV3:case d.ThenaFusion:return"0x";case d.PancakeInfinityCl:case d.PancakeInfinityLb:return"0x";case d.Dodo:return"0x";case d.NomiswapStable:return"0x";case d.Biswap:return"0x";case d.Apeswap:return"0x";case d.BabyDogeSwap:return"0x";case d.BabySwap:return"0x";case d.SquadSwapV2:return"0x";case d.SushiSwapV2:return"0x";case d.SushiSwapV3:return"0x";case d.PancakeStable:return"0x";case d.ListaStable:return"0x";case d.Wombat:return"0x";default:return"0x"}}extractIntermediates(e,t,n){const r=new Set;for(const o of e)o.tokenOut!==n&&r.add(o.tokenOut);return r.delete(t),r.delete(n),Array.from(r)}stepKey(e){return`${e.pool.address}-${e.tokenIn}-${e.tokenOut}`}}exports.API_DEFAULTS=_;exports.ApiClient=Z;exports.ApiError=g;exports.BPS_DENOMINATOR=P;exports.BSC_MAINNET_CONFIG=ne;exports.BSC_TESTNET_CONFIG=re;exports.CustomFeeError=x;exports.DEFAULT_API_URL=Y;exports.DEFAULT_DEADLINE_SECONDS=R;exports.DEFAULT_EXECUTE_TIMEOUT_MS=V;exports.DEFAULT_SLIPPAGE_BPS=J;exports.DEFAULT_TRANSACTION_RESPONSE_POLL_INTERVALS_MS=F;exports.ERR_ZERO_AMOUNT_PATHS=te;exports.ExecuteTimeoutError=U;exports.MAX_FEE_BPS=N;exports.MAX_PLATFORM_CUT_BPS=ee;exports.NATIVE_TOKEN_ADDRESS=Q;exports.PeachClient=D;exports.ProtocolType=d;exports.RouteDiscovery=ge;exports.SwapBuilder=Ee;exports.isNativeTokenAddress=T;exports.withWalletSendTimeout=L;
|
|
3
|
+
`),u=new Error(c);return u.cause=o,u.reason=a,u}async findFailingStep(e,t,n,r,o,s=50){const a=e.params.steps;if(!a.length)return null;const i=o!=null?this.normalizeRevertReason(o):void 0;for(let c=1;c<=a.length;c++){const u=this.buildTruncatedQuote(e,c);let l,p;try{l=(await this.simulate(u,t,n,r)).amountOut}catch(v){p=v}if(p!==void 0){const v=this.normalizeRevertReason(p),E=p?.shortMessage??p?.reason??p?.message;if(i==null||v===i)return{stepIndex:c-1,step:a[c-1],error:p,revertMessage:typeof E=="string"?E:v,fullRouteRevertMessage:i};continue}if(l===void 0)continue;const A=this.getQuotedStepAmountOut(e,c-1);if(A===void 0||A===0n)continue;const w=A>l?A-l:0n;if(w===0n)continue;const S=w*k/A;if(S>BigInt(s)){const v=`Step ${c-1} deviates: quoted=${A} simulated=${l} shortfall=${w} (${S}bps, threshold=${s}bps). Likely cause: transfer-tax on ${a[c-1].tokenOut}, stale pool data, or dynamic-fee drift.`;return{stepIndex:c-1,step:a[c-1],error:new Error(v),revertMessage:v,fullRouteRevertMessage:i}}}return null}normalizeRevertReason(e){if(e==null)return;const t=e;if(typeof t.reason=="string"&&t.reason.length>0)return t.reason;const n=this.extractRevertData(e);if(n&&n.length>=10){const c=n.slice(0,10).toLowerCase();return C.KNOWN_ERROR_SELECTORS[c]??c}const r=t.shortMessage??t.message;if(typeof r!="string")return;const o=r.match(/reason="([^"]+)"/);if(o)return o[1];const s=r.match(/reverted:\s*"([^"]+)"/);if(s)return s[1];const a=r.match(/execution reverted:\s*"([^"]+)"/);if(a)return a[1];const i=r.match(/\b0x[0-9a-fA-F]{8}\b/);if(i){const c=i[0].toLowerCase();return C.KNOWN_ERROR_SELECTORS[c]??c}}extractRevertData(e){const t=new Set,n=[e];for(;n.length;){const r=n.shift();if(r==null||typeof r!="object"||t.has(r))continue;t.add(r);const o=r,s=o.data;if(typeof s=="string"&&/^0x[0-9a-fA-F]{8,}$/.test(s))return s;for(const a of["info","error","cause","revert","originalError"])o[a]&&n.push(o[a])}}getQuotedStepAmountOut(e,t){return e.route?.routes?.[0]?.steps?.[t]?.amountOut}buildTruncatedQuote(e,t){const n=e.params.steps.slice(0,t),o=n[n.length-1].tokenOut,s=o.toLowerCase(),a=e.srcToken.toLowerCase(),i=t===e.params.steps.length,c=new Set,u=[];for(const l of n){const p=l.tokenOut.toLowerCase();p===s||p===a||c.has(p)||(c.add(p),u.push(l.tokenOut))}return{...e,dstToken:o,dstNative:i?e.dstNative:!1,amountOut:i?e.amountOut:0n,customFee:i?e.customFee:void 0,params:{...e.params,dstToken:o,amountOutMin:i?e.params.amountOutMin:0n,expectAmountOut:i?e.params.expectAmountOut:0n,feeReceiver:i?e.params.feeReceiver:h.ethers.ZeroAddress,feeBps:i?e.params.feeBps:0,steps:n,intermediateTokens:u}}}buildStateOverrides(e,t,n,r,o,s){if(s?.isNative||x(e))return{};if(!n||n===h.ethers.ZeroAddress)throw new Error("buildStateOverrides requires a non-zero routerAddress.");const a=h.ethers.AbiCoder.defaultAbiCoder(),i=r||h.ethers.parseUnits("1000000",18),c=o??n,u=h.ethers.zeroPadValue(h.ethers.toBeHex(i),32),l=h.ethers.zeroPadValue(h.ethers.toBeHex(h.ethers.MaxUint256),32),p={},A=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,50,51,52,100,101,102];for(const w of A){const S=h.ethers.keccak256(a.encode(["address","uint256"],[t,w]));p[S]=u;const v=h.ethers.keccak256(a.encode(["address","uint256"],[t,w])),E=h.ethers.keccak256(a.encode(["address","bytes32"],[c,v]));p[E]=l}return{[e.toLowerCase()]:{stateDiff:p}}}};C.KNOWN_ERROR_SELECTORS={"0x2c19b8b8":"InsufficientOutput"};let U=C;const Q=["function token0() external view returns (address)","function token1() external view returns (address)","function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)"],he=["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)"],me="0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73",fe=["function getPair(address tokenA, address tokenB) external view returns (address pair)"],we="0x86407bEa2078ea5f5EB5A52B2caA963bC1F889Da",Ae=["function getPair(address tokenA, address tokenB) external view returns (address pair)"],Se="0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865",ve=["function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool)"],ge=[100,500,2500,1e4],Ie=60000n,Ee=120000n,Pe=100000n;class _e{constructor(e,t){this.poolCache=new Map,this.provider=e,this.v2Factory=new h.ethers.Contract(me,fe,e),this.v3Factory=new h.ethers.Contract(Se,ve,e),this.babySwapFactory=new h.ethers.Contract(we,Ae,e)}async findBestRoute(e,t,n){const r=await this.discoverPools(e,t);if(r.length===0)throw new Error(`No pools found for ${e} -> ${t}`);const s=(await this.calculateRoutes(r,e,t,n)).reduce((a,i)=>i.amountOut>a.amountOut?i:a);return{routes:[s],percentages:[1e4],totalAmountIn:n,totalAmountOut:s.amountOut,totalGasEstimate:s.gasEstimate}}async discoverPools(e,t){const n=[];try{const r=await this.findV2Pool(e,t);r&&n.push(r)}catch{}for(const r of ge)try{const o=await this.findV3Pool(e,t,r);o&&n.push(o)}catch{}try{const r=await this.findBabySwapPool(e,t);r&&n.push(r)}catch{}return n}async findV2Pool(e,t){const n=`v2-${e}-${t}`;if(this.poolCache.has(n))return this.poolCache.get(n);const r=await this.v2Factory.getPair(e,t);if(r===h.ethers.ZeroAddress)return null;const o=new h.ethers.Contract(r,Q,this.provider),[s,a,i]=await Promise.all([o.token0(),o.token1(),o.getReserves()]),c={address:r,token0:s,token1:a,protocol:d.PancakeV2,reserve0:i.reserve0,reserve1:i.reserve1};return this.poolCache.set(n,c),c}async findV3Pool(e,t,n){const r=`v3-${e}-${t}-${n}`;if(this.poolCache.has(r))return this.poolCache.get(r);const o=await this.v3Factory.getPool(e,t,n);if(o===h.ethers.ZeroAddress)return null;const s=new h.ethers.Contract(o,he,this.provider),[a,i,c]=await Promise.all([s.token0(),s.token1(),s.liquidity()]);if(c===0n)return null;const u={address:o,token0:a,token1:i,protocol:d.PancakeV3,fee:n,liquidity:c};return this.poolCache.set(r,u),u}async findBabySwapPool(e,t){const n=`babyswap-${e}-${t}`;if(this.poolCache.has(n))return this.poolCache.get(n);const r=await this.babySwapFactory.getPair(e,t);if(r===h.ethers.ZeroAddress)return null;const o=new h.ethers.Contract(r,Q,this.provider),[s,a,i]=await Promise.all([o.token0(),o.token1(),o.getReserves()]),c={address:r,token0:s,token1:a,protocol:d.BabySwap,reserve0:i.reserve0,reserve1:i.reserve1};return this.poolCache.set(n,c),c}async calculateRoutes(e,t,n,r){const o=[];for(const s of e)try{const a=await this.getAmountOut(s,t,n,r);a>0n&&o.push({steps:[{pool:s,tokenIn:t,tokenOut:n,amountIn:r,amountOut:a}],amountIn:r,amountOut:a,gasEstimate:s.protocol===d.PancakeV2||s.protocol===d.BabySwap?Ie:s.protocol===d.Dodo?Pe:Ee})}catch{}return o}async getAmountOut(e,t,n,r){if(e.protocol===d.PancakeV2)return this.getV2AmountOut(e,t,r);if(e.protocol===d.BabySwap)return this.getBabySwapAmountOut(e,t,r);if(e.protocol===d.Dodo)throw new Error("DODO amount out not supported in local route discovery");return this.estimateV3AmountOut(e,t,r)}getV2AmountOut(e,t,n){const r=t.toLowerCase()===e.token0.toLowerCase(),[o,s]=r?[e.reserve0,e.reserve1]:[e.reserve1,e.reserve0],a=n*9975n,i=a*s,c=o*10000n+a;return i/c}getBabySwapAmountOut(e,t,n){const r=t.toLowerCase()===e.token0.toLowerCase(),[o,s]=r?[e.reserve0,e.reserve1]:[e.reserve1,e.reserve0],a=n*998n,i=a*s,c=o*1000n+a;return i/c}estimateV3AmountOut(e,t,n){const o=1000000n-BigInt(e.fee||2500);return n*o/1000000n}clearCache(){this.poolCache.clear()}}class ke{constructor(e){this.adapters=e}build(e,t,n,r,o=R){const s=this.flattenRoutes(e),a=this.mergeIdenticalPools(s),i=this.topologicalSort(a,t),c=this.convertToSwapSteps(i),u=this.extractIntermediates(i,t,n),l=BigInt(Math.floor(Date.now()/1e3)+o);return{srcToken:t,dstToken:n,amountIn:e.totalAmountIn,amountOutMin:r,steps:c,intermediateTokens:u,deadline:l,quoteId:h.ethers.ZeroHash,expectAmountOut:e.totalAmountOut,feeReceiver:h.ethers.ZeroAddress,feeBps:0}}flattenRoutes(e){const t=[];for(let n=0;n<e.routes.length;n++){const r=e.routes[n],o=e.percentages[n],s=e.totalAmountIn*BigInt(o)/k;for(let a=0;a<r.steps.length;a++){const i=r.steps[a];t.push({pool:i.pool,tokenIn:i.tokenIn,tokenOut:i.tokenOut,amountIn:a===0?s:0n,routeIndex:n,stepIndex:a})}}return t}mergeIdenticalPools(e){const t=new Map,n=[];for(const r of e){const o=`${r.pool.address}-${r.tokenIn}-${r.tokenOut}`;if(t.has(o)){const s=t.get(o);r.amountIn>0n&&s.amountIn>0n&&(s.amountIn=0n)}else{const s={...r};t.set(o,s),n.push(s)}}for(const r of n){const o=`${r.pool.address}-${r.tokenIn}-${r.tokenOut}`;e.filter(a=>`${a.pool.address}-${a.tokenIn}-${a.tokenOut}`===o).length>1&&(r.amountIn=0n)}return n}topologicalSort(e,t){const n=new Map,r=new Map,o=new Map;for(const i of e){const c=this.stepKey(i);r.set(c,i),n.set(c,0),o.set(c,[])}for(const i of e){const c=this.stepKey(i);if(i.tokenIn!==t){for(const u of e)if(u.tokenOut===i.tokenIn){const l=this.stepKey(u);l!==c&&(o.get(l).push(c),n.set(c,(n.get(c)||0)+1))}}}const s=[];for(const[i,c]of n)c===0&&s.push(i);const a=[];for(;s.length>0;){const i=s.shift(),c=r.get(i);a.push(c);for(const u of o.get(i)||[]){const l=(n.get(u)||0)-1;n.set(u,l),l===0&&s.push(u)}}if(a.length!==e.length)throw new Error("Circular dependency detected in route");return a}convertToSwapSteps(e){return e.map(t=>{const n=this.adapters.get(t.pool.protocol);if(!n)throw new Error(`No adapter for protocol: ${t.pool.protocol}`);return{adapter:n,pool:t.pool.address,tokenIn:t.tokenIn,tokenOut:t.tokenOut,amountIn:t.amountIn,extraData:this.encodeExtraData(t.pool)}})}encodeExtraData(e){switch(e.protocol){case d.PancakeV1:return"0x";case d.PancakeV2:return"0x";case d.UniswapV2:return"0x";case d.PancakeV3:case d.UniswapV3:case d.UniswapV4:case d.SquadSwapV3:case d.ThenaFusion:return"0x";case d.PancakeInfinityCl:case d.PancakeInfinityLb:return"0x";case d.Dodo:return"0x";case d.NomiswapStable:return"0x";case d.Biswap:return"0x";case d.Apeswap:return"0x";case d.BabyDogeSwap:return"0x";case d.BabySwap:return"0x";case d.BakerySwap:return"0x";case d.SquadSwapV2:return"0x";case d.SushiSwapV2:return"0x";case d.SushiSwapV3:return"0x";case d.PancakeStable:return"0x";case d.ListaStable:return"0x";case d.Wombat:return"0x";default:return"0x"}}extractIntermediates(e,t,n){const r=new Set;for(const o of e)o.tokenOut!==n&&r.add(o.tokenOut);return r.delete(t),r.delete(n),Array.from(r)}stepKey(e){return`${e.pool.address}-${e.tokenIn}-${e.tokenOut}`}}exports.API_DEFAULTS=P;exports.ApiClient=X;exports.ApiError=g;exports.BPS_DENOMINATOR=k;exports.BSC_MAINNET_CONFIG=se;exports.BSC_TESTNET_CONFIG=oe;exports.CustomFeeError=y;exports.DEFAULT_API_URL=Z;exports.DEFAULT_DEADLINE_SECONDS=R;exports.DEFAULT_EXECUTE_TIMEOUT_MS=M;exports.DEFAULT_SLIPPAGE_BPS=te;exports.DEFAULT_TRANSACTION_RESPONSE_POLL_INTERVALS_MS=F;exports.ERR_ZERO_AMOUNT_PATHS=re;exports.ExecuteTimeoutError=$;exports.MAX_FEE_BPS=D;exports.MAX_PLATFORM_CUT_BPS=ne;exports.NATIVE_TOKEN_ADDRESS=j;exports.PERMIT2_ADDRESS=O;exports.PERMIT2_MAX_AMOUNT=L;exports.PeachClient=U;exports.ProtocolType=d;exports.RouteDiscovery=_e;exports.SwapBuilder=ke;exports.isNativeTokenAddress=x;exports.withWalletSendTimeout=V;
|
|
4
4
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.d.ts
CHANGED
|
@@ -318,7 +318,7 @@ export declare function isNativeTokenAddress(address: string): boolean;
|
|
|
318
318
|
/**
|
|
319
319
|
* Known DEX providers supported by the SDK.
|
|
320
320
|
*/
|
|
321
|
-
export declare type KnownProvider = "PANCAKEV2" | "PANCAKEV3" | "PANCAKE_INFINITY_CL" | "PANCAKE_INFINITY_LB" | "UNISWAPV3" | "UNISWAPV4" | "DODO" | "THENAV3" | "THENA_FUSION" | "NOMISWAP_STABLE" | "BISWAP" | "APESWAP" | "BABYDOGESWAP" | "BABYSWAP" | "PANCAKE_STABLE" | "LISTA_STABLE" | "SQUADSWAP_V3" | "SQUADSWAP_V2" | "WOMBAT" | "SUSHISWAP_V2" | "SUSHISWAP_V3";
|
|
321
|
+
export declare type KnownProvider = "PANCAKEV1" | "PANCAKEV2" | "PANCAKEV3" | "PANCAKE_INFINITY_CL" | "PANCAKE_INFINITY_LB" | "UNISWAPV2" | "UNISWAPV3" | "UNISWAPV4" | "DODO" | "THENAV3" | "THENA_FUSION" | "NOMISWAP_STABLE" | "BISWAP" | "APESWAP" | "BABYDOGESWAP" | "BABYSWAP" | "BAKERYSWAP" | "PANCAKE_STABLE" | "LISTA_STABLE" | "SQUADSWAP_V3" | "SQUADSWAP_V2" | "WOMBAT" | "SUSHISWAP_V2" | "SUSHISWAP_V3";
|
|
322
322
|
|
|
323
323
|
/**
|
|
324
324
|
* Client-side default cap on the integrator-set custom fee. Mirrors the PeachRouter v2
|
|
@@ -394,6 +394,60 @@ export declare class PeachClient {
|
|
|
394
394
|
private buildApprovalRequest;
|
|
395
395
|
private buildApprovalTransactionRequest;
|
|
396
396
|
private getAllowance;
|
|
397
|
+
/**
|
|
398
|
+
* Read the current Permit2 AllowanceTransfer state for (owner, token, spender).
|
|
399
|
+
* `spender` defaults to the router address. The returned `nonce` feeds the next PermitSingle;
|
|
400
|
+
* `amount` / `expiration` decide whether a fresh signature is needed.
|
|
401
|
+
*/
|
|
402
|
+
getPermit2Allowance(token: string, owner: string, spender?: string): Promise<{
|
|
403
|
+
amount: bigint;
|
|
404
|
+
expiration: number;
|
|
405
|
+
nonce: number;
|
|
406
|
+
}>;
|
|
407
|
+
/**
|
|
408
|
+
* Build the ONE-TIME `token.approve(PERMIT2, max)` tx, or undefined when the token's existing
|
|
409
|
+
* Permit2 allowance already covers `amount`. This is the only on-chain approval Permit2 needs;
|
|
410
|
+
* once set, every future swap of this token is signature-only.
|
|
411
|
+
*/
|
|
412
|
+
buildPermit2TokenApproval(token: string, owner: string, amount: bigint, options?: {
|
|
413
|
+
gasPrice?: bigint;
|
|
414
|
+
}): Promise<SwapTxRequest | undefined>;
|
|
415
|
+
/**
|
|
416
|
+
* Build the EIP-712 PermitSingle the user signs to grant the router a Permit2 allowance.
|
|
417
|
+
* Reads the current sequential nonce from Permit2; `permitSingle.spender` is the router.
|
|
418
|
+
* Pass the result straight into `signer.signTypedData(domain, types, values)`.
|
|
419
|
+
*/
|
|
420
|
+
buildPermit2TypedData(quote: Quote, owner: string, options?: {
|
|
421
|
+
permitAmount?: bigint;
|
|
422
|
+
expiration?: number;
|
|
423
|
+
sigDeadline?: bigint;
|
|
424
|
+
}): Promise<Permit2TypedData>;
|
|
425
|
+
/**
|
|
426
|
+
* Encode calldata for swapWithPermit2 / swapETHWithPermit2. Routes to swapETHWithPermit2
|
|
427
|
+
* when the quote's dstToken is native. Pass `"0x"` as `signature` to reuse an existing
|
|
428
|
+
* on-chain Permit2 allowance (the router then skips the permit() call).
|
|
429
|
+
*/
|
|
430
|
+
encodeSwapWithPermit2Calldata(quote: Quote, slippageBps: number, permitSingle: Permit2PermitSingle, signature: string): {
|
|
431
|
+
to: string;
|
|
432
|
+
data: string;
|
|
433
|
+
value: bigint;
|
|
434
|
+
method: "swapWithPermit2" | "swapETHWithPermit2";
|
|
435
|
+
};
|
|
436
|
+
/**
|
|
437
|
+
* Prepare a single-transaction Permit2 swap. Returns the optional one-time token->Permit2
|
|
438
|
+
* approval, the EIP-712 payload to sign (or undefined when an existing allowance can be
|
|
439
|
+
* reused), and a `buildSwapTx(signature)` closure for the final swap tx.
|
|
440
|
+
*
|
|
441
|
+
* Integrator flow:
|
|
442
|
+
* const req = await client.swapWithPermit2(quote, owner, { slippageBps: 50 });
|
|
443
|
+
* if (req.permit2Approval) await signer.sendTransaction(req.permit2Approval); // once per token
|
|
444
|
+
* const sig = req.typedData
|
|
445
|
+
* ? await signer.signTypedData(req.typedData.domain, req.typedData.types, req.typedData.values)
|
|
446
|
+
* : "0x";
|
|
447
|
+
* await signer.sendTransaction(req.buildSwapTx(sig));
|
|
448
|
+
*/
|
|
449
|
+
swapWithPermit2(quote: Quote, owner: string, options: Permit2Options): Promise<Permit2SwapRequest>;
|
|
450
|
+
private assertUint160;
|
|
397
451
|
private applyTxOverrides;
|
|
398
452
|
private sendTransactionWithTimeout;
|
|
399
453
|
private waitForTransactionResponse;
|
|
@@ -598,6 +652,88 @@ export declare interface PeachConfig {
|
|
|
598
652
|
adapters: AdapterConfig[];
|
|
599
653
|
}
|
|
600
654
|
|
|
655
|
+
/** Canonical Uniswap Permit2 singleton. Same address on every EVM chain (incl. BSC). */
|
|
656
|
+
export declare const PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
657
|
+
|
|
658
|
+
/** Max value of a Permit2 amount (uint160). */
|
|
659
|
+
export declare const PERMIT2_MAX_AMOUNT: bigint;
|
|
660
|
+
|
|
661
|
+
export declare interface Permit2Options {
|
|
662
|
+
/** Slippage tolerance in basis points (e.g. 50 = 0.5%). Required. */
|
|
663
|
+
slippageBps: number;
|
|
664
|
+
/** Gas price in wei. Applied to the swap tx. */
|
|
665
|
+
gasPrice?: bigint;
|
|
666
|
+
/** Gas limit for the swap tx. */
|
|
667
|
+
gasLimit?: bigint;
|
|
668
|
+
/** Permit2 allowance amount to request (default: MaxUint160 so repeat swaps need no new sign). */
|
|
669
|
+
permitAmount?: bigint;
|
|
670
|
+
/** Permit2 allowance expiration, unix-seconds (default: now + 30 days). */
|
|
671
|
+
expiration?: number;
|
|
672
|
+
/** EIP-712 signature deadline, unix-seconds (default: now + 30 min). */
|
|
673
|
+
sigDeadline?: bigint;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
export declare interface Permit2PermitDetails {
|
|
677
|
+
token: string;
|
|
678
|
+
/** uint160 */
|
|
679
|
+
amount: bigint;
|
|
680
|
+
/** uint48 unix-seconds expiration of the allowance. */
|
|
681
|
+
expiration: number;
|
|
682
|
+
/** uint48 sequential nonce per (owner, token, spender). */
|
|
683
|
+
nonce: number;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
export declare interface Permit2PermitSingle {
|
|
687
|
+
details: Permit2PermitDetails;
|
|
688
|
+
/** Spender — the PeachRouter address. */
|
|
689
|
+
spender: string;
|
|
690
|
+
/** uint256 deadline on the EIP-712 signature itself. */
|
|
691
|
+
sigDeadline: bigint;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/** Matches the on-chain `IPeachRouter.Permit2Sig` tuple. */
|
|
695
|
+
export declare interface Permit2Sig {
|
|
696
|
+
permitSingle: Permit2PermitSingle;
|
|
697
|
+
/** `"0x"` (empty) reuses an existing on-chain Permit2 allowance (skips the permit() call). */
|
|
698
|
+
signature: string;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
export declare interface Permit2SwapRequest {
|
|
702
|
+
routerAddress: string;
|
|
703
|
+
method: "swapWithPermit2" | "swapETHWithPermit2";
|
|
704
|
+
/**
|
|
705
|
+
* One-time `token.approve(PERMIT2, max)` tx — present ONLY when the token's existing
|
|
706
|
+
* Permit2 allowance is insufficient. Send it once per token; afterwards it stays undefined.
|
|
707
|
+
*/
|
|
708
|
+
permit2Approval?: SwapTxRequest;
|
|
709
|
+
/**
|
|
710
|
+
* EIP-712 payload to sign with `signer.signTypedData(domain, types, values)`. Undefined when
|
|
711
|
+
* an existing on-chain Permit2 allowance is still valid (zero-signature repeat swap).
|
|
712
|
+
*/
|
|
713
|
+
typedData?: Permit2TypedData;
|
|
714
|
+
/** The PermitSingle the `typedData` was built from (current nonce snapshot). */
|
|
715
|
+
permitSingle: Permit2PermitSingle;
|
|
716
|
+
/**
|
|
717
|
+
* Build the final swap tx from the user's EIP-712 signature. Pass `"0x"` to reuse an
|
|
718
|
+
* existing valid allowance (only when `typedData` is undefined).
|
|
719
|
+
*/
|
|
720
|
+
buildSwapTx: (signature: string) => SwapTxRequest;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/** EIP-712 payload for `signer.signTypedData(domain, types, values)`. */
|
|
724
|
+
export declare interface Permit2TypedData {
|
|
725
|
+
domain: {
|
|
726
|
+
name: string;
|
|
727
|
+
chainId: number;
|
|
728
|
+
verifyingContract: string;
|
|
729
|
+
};
|
|
730
|
+
types: Record<string, {
|
|
731
|
+
name: string;
|
|
732
|
+
type: string;
|
|
733
|
+
}[]>;
|
|
734
|
+
values: Permit2PermitSingle;
|
|
735
|
+
}
|
|
736
|
+
|
|
601
737
|
/** @deprecated Use {@link ProtocolFeeConfig}. Kept as an alias for the pre-v2 name. */
|
|
602
738
|
export declare type PlatformConfig = ProtocolFeeConfig;
|
|
603
739
|
|
|
@@ -627,10 +763,12 @@ export declare interface ProtocolFeeConfig {
|
|
|
627
763
|
}
|
|
628
764
|
|
|
629
765
|
export declare enum ProtocolType {
|
|
766
|
+
PancakeV1 = "PancakeV1",
|
|
630
767
|
PancakeV2 = "PancakeV2",
|
|
631
768
|
PancakeV3 = "PancakeV3",
|
|
632
769
|
PancakeInfinityCl = "Pancake_Infinity_Cl",
|
|
633
770
|
PancakeInfinityLb = "Pancake_Infinity_Lb",
|
|
771
|
+
UniswapV2 = "UniswapV2",
|
|
634
772
|
UniswapV3 = "UniswapV3",
|
|
635
773
|
UniswapV4 = "UniswapV4",
|
|
636
774
|
Dodo = "Dodo",
|
|
@@ -641,6 +779,7 @@ export declare enum ProtocolType {
|
|
|
641
779
|
Apeswap = "Apeswap",
|
|
642
780
|
BabyDogeSwap = "BabyDogeSwap",
|
|
643
781
|
BabySwap = "BabySwap",
|
|
782
|
+
BakerySwap = "BakerySwap",
|
|
644
783
|
PancakeStable = "Pancake_Stable",
|
|
645
784
|
ListaStable = "Lista_Stable",
|
|
646
785
|
SquadSwapV3 = "SquadSwap_V3",
|