如何基于PancakeSwap合约实现单次调用同时兑换多个代币
多代币批量兑换实现方案
核心思路是在单次调用内循环执行原有的单代币兑换逻辑,提前预估所需总BNB金额完成校验,兑换完成后统一退还剩余BNB。
核心改动点
- 新增批量兑换接口,入参为各目标代币的期望兑换数量数组,顺序对应你预先定义的
crypto1、crypto2、crypto3 - 调用路由合约的
getAmountsIn接口预计算每个代币兑换所需的BNB总额,校验用户传入的msg.value足够支付所有兑换 - 循环执行单代币兑换逻辑,每次兑换使用合约当前剩余的BNB支付
- 所有兑换完成后统一退还剩余BNB给调用者
完整修改后代码
pragma solidity 0.7.1; import "https://github.com/pancakeswap/pancake-swap-periphery/blob/master/contracts/interfaces/IPancakeRouter02.sol"; contract UniswapExample { address internal constant UNISWAP_ROUTER_ADDRESS = 0x9Ac64Cc6e4415144C455BD8E4837Fea55603e5c3 ; //Pancake测试网路由地址 IPancakeRouter02 public uniswapRouter; address private crypto1 = 0x7ef95a0FEE0Dd31b22626fA2e10Ee6A223F8a684; address private crypto2 = 0x78867BbEeF44f2326bF8DDd1941a4439382EF2A7; address private crypto3 = 0x8a9424745056Eb399FD19a0EC26A14316684e274; constructor() { uniswapRouter = IPancakeRouter02(UNISWAP_ROUTER_ADDRESS); } // 原有单代币兑换函数保留 function convertEthToCrypto(uint cryptoAmount) public payable { uint deadline = block.timestamp + 15; uniswapRouter.swapETHForExactTokens{ value: msg.value }(cryptoAmount, getPathForETH(crypto1), address(this), deadline); (bool success,) = msg.sender.call{ value: address(this).balance }(""); require(success, "refund failed"); } // 新增批量兑换函数 function convertEthToMultipleCrypto(uint[] calldata cryptoAmounts) public payable { // 数量数组长度需对应3个预设代币 require(cryptoAmounts.length == 3, "Amount array length must be 3"); uint deadline = block.timestamp + 15; uint totalRequiredBNB = 0; // 预计算所有兑换所需的总BNB金额 for(uint i = 0; i < cryptoAmounts.length; i++) { address targetToken = _getTokenByIndex(i); uint[] memory amountsIn = uniswapRouter.getAmountsIn(cryptoAmounts[i], getPathForETH(targetToken)); totalRequiredBNB += amountsIn[0]; } require(msg.value >= totalRequiredBNB, "Insufficient BNB for all swaps"); // 循环执行每个代币的兑换 for(uint i = 0; i < cryptoAmounts.length; i++) { address targetToken = _getTokenByIndex(i); uniswapRouter.swapETHForExactTokens{value: address(this).balance}( cryptoAmounts[i], getPathForETH(targetToken), address(this), // 如需直接转到调用者钱包,改为msg.sender即可 deadline ); } // 退还剩余BNB (bool success,) = msg.sender.call{ value: address(this).balance }(""); require(success, "refund failed"); } // 辅助方法:按索引取对应代币地址 function _getTokenByIndex(uint index) internal view returns(address) { if(index == 0) return crypto1; else if(index == 1) return crypto2; else return crypto3; } function getPathForETH(address crypto) public view returns (address[] memory) { address[] memory path = new address[](2); path[0] = uniswapRouter.WETH(); path[1] = crypto; return path; } function getETH() public view returns(address) { return uniswapRouter.WETH(); } receive() payable external {} }
注意事项
- 调用批量兑换接口时,传入的BNB金额需要略高于预计算的总金额,覆盖兑换滑点,避免交易失败
- 主网部署时建议把deadline参数改为前端传入,不要在合约内写死固定时长,避免区块拥堵时交易过期失败
- 若需要兑换获得的代币直接打到调用者钱包,可将
swapETHForExactTokens的第三个参数从address(this)改为msg.sender - 如果需要支持任意数量的自定义代币兑换,可修改批量兑换函数的入参,同时传入代币地址数组和对应数量数组,去掉固定的长度校验即可
内容的提问来源于stack exchange,提问作者holistic
相关产品推荐
相关产品推荐

