Uniswap V3(Polygon)按价格切换交易池代码修改求助
Uniswap V3 自动选择最优交易池实现方案(Polygon链)
要实现根据价格自动选择最优交易池,核心思路是遍历同交易对的所有手续费层级池子,通过Quoter计算每个池子完成ExactOutput兑换所需的输入量,最终选用输入量最少(兑换成本最低)的池子。下面是具体实现步骤和修改后的代码:
实现步骤
- 第一步:获取目标交易对的所有可用Uniswap V3池子
你需要先通过Uniswap V3的PoolFactory合约或Subgraph,查询出sellToken和buyToken之间所有存在的手续费层级(0.05%、0.3%、1%等)对应的池子地址和实例。 - 第二步:批量调用Quoter计算每个池子的输入量
对每个有效池子,调用quoteExactOutputSingle方法,计算固定输出量下所需的输入代币数量。 - 第三步:筛选最优池子
比较所有池子的输入量,选择输入量最小的那个池子作为交易路由。 - 第四步:用最优池子构建交易并执行
基于选中的池子创建Route和Trade,生成SwapRouter调用参数并发起交易。
修改后的完整代码
// 定义Polygon链上Uniswap V3常用手续费层级,对应0.05%、0.3%、1% const POOL_FEES = [500, 3000, 10000]; async split(signer: Signer): Promise<void> { const address = await signer.getAddress(); const { buyToken, sellToken, buyAmount } = await this.calculateSplitPurchase(address); if (buyAmount.lessThan(0) || buyAmount.equalTo(0)) { return; } console.log(`Buying ${buyAmount.toFixed()} ${buyToken.name} with ${sellToken.name}`); // 获取所有可用池子实例 const pools = await Promise.all(POOL_FEES.map(async (fee) => { try { // 通过PoolFactory查询池子地址 const poolAddress = await this.poolFactory.getPool( sellToken.isNative ? WETH_ADDRESS : sellToken.address, buyToken.isNative ? WETH_ADDRESS : buyToken.address, fee ); if (poolAddress === ethers.constants.AddressZero) return null; // 初始化池子实例 const poolContract = this.poolContractFactory.attach(poolAddress); const [slot0, liquidity, tickSpacing] = await Promise.all([ poolContract.slot0(), poolContract.liquidity(), poolContract.tickSpacing() ]); return new Pool( sellToken, buyToken, fee, slot0.sqrtPriceX96.toString(), liquidity.toString(), tickSpacing ); } catch (err) { console.warn(`Failed to fetch pool with fee ${fee}:`, err); return null; } })).then(pools => pools.filter(p => p !== null)); if (pools.length === 0) { throw new Error("No available pools for the token pair"); } // 批量计算每个池子的输入量 const poolQuotes = await Promise.all(pools.map(async (pool) => { try { const amountIn = await this.quoter.callStatic.quoteExactOutputSingle( sellToken.isNative ? WETH_ADDRESS : sellToken.address, buyToken.isNative ? WETH_ADDRESS : buyToken.address, pool.fee, buyAmount.quotient.toString(10), 0 ); return { pool, amountIn: BigInt(amountIn) }; } catch (err) { console.warn(`Failed to quote pool with fee ${pool.fee}:`, err); return null; } })).then(quotes => quotes.filter(q => q !== null)); if (poolQuotes.length === 0) { throw new Error("No valid quotes from available pools"); } // 筛选输入量最小的最优池子 const bestQuote = poolQuotes.reduce((prev, curr) => { return curr.amountIn < prev.amountIn ? curr : prev; }); // 构建交易路由和参数 const route = new Route([bestQuote.pool], sellToken, buyToken); const trade = Trade.createUncheckedTrade({ route, tradeType: TradeType.EXACT_OUTPUT, inputAmount: CurrencyAmount.fromRawAmount(sellToken, bestQuote.amountIn.toString()), outputAmount: CurrencyAmount.fromRawAmount(buyToken, buyAmount.quotient.toString(10)), }); const params = SwapRouter.swapCallParameters([trade], { slippageTolerance: new Percent(2, 1000), recipient: address, deadline: ethers.constants.MaxUint256.toString(), }); await this.swap(signer, params); } async swap(signer: Signer, params: MethodParameters): Promise<void> { const tx = await signer.sendTransaction({ to: this.router.address, from: await signer.getAddress(), data: params.calldata, value: params.value, //gasPrice: await getFastGasPrice(), //gasLimit: 1300000, }); await tx.wait(3); }
关键注意事项
- 池子获取方式:如果追求效率,建议提前从Uniswap V3 Subgraph批量查询常用交易对的池子信息,避免实时调用合约带来的Gas成本和延迟。
- 异常处理:要处理池子不存在、Quoter调用失败(比如流动性不足)的情况,避免交易中断。
- 滑点设置:可以根据不同池子的流动性情况动态调整滑点,流动性高的池子可以设置更小的滑点。
- 原生币处理:代码中已兼容原生MATIC和WETH的转换,确保交易时正确处理
value参数。
内容的提问来源于stack exchange,提问作者Dajeir
相关产品推荐
相关产品推荐

