Solidity合约添加Uniswap流动性报错,如何实现代币兑ETH?
问题:Uniswap流动性添加失败(insufficient liquidity)
用户开发的Solidity合约中,调用swapTokensForEthAndAddLiquidity时出现Gas估算错误,控制台提示insufficient liquidity,无法完成代币兑换与流动性添加操作。
错误原因分析
- 资金流向错误:
swapExactTokensForETH的接收地址设为msg.sender,兑换得到的ETH直接转给了用户,合约账户没有ETH储备用于后续的流动性添加。 - 代币余额不足:swap操作已经将用户的代币全部兑换为ETH,后续调用
addLiquidityETH时传入的tokenAmount是初始代币数量,但此时合约内已无足够代币,导致流动性添加失败。 - 授权逻辑错误:
_approve(address(this), address(uniswapRouter), tokenAmount)是让合约自己授权Router使用代币,但用户的代币还在自己账户,Router无法直接转移用户的代币。
修正后的合约代码
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol"; import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol"; contract YourToken is ERC20, Ownable, ReentrancyGuard { IUniswapV2Router02 public uniswapRouter; address public uniswapPair; event TokensSwappedForEth(uint256 tokenAmount, uint256 ethAmount); event LiquidityAdded(uint256 tokenAmount, uint256 ethAmount, uint256 liquidityTokens); event TokensMinted(address indexed account, uint256 amount); constructor(address _uniswapRouter) ERC20("Infuraa Token", "INXT") Ownable(msg.sender) { uniswapRouter = IUniswapV2Router02(_uniswapRouter); uniswapPair = IUniswapV2Factory(uniswapRouter.factory()).createPair(address(this), uniswapRouter.WETH()); _mint(msg.sender, 980 * 10**12 * 10**18); } // 用户将代币兑换为ETH(前提是LP池已有流动性) function swapTokensForEth(uint256 tokenAmount) external nonReentrant { require(balanceOf(msg.sender) >= tokenAmount, "Insufficient balance"); // 用户需先在前端调用approve授权Router使用自己的代币 address[] memory path = new address[](2); path[0] = address(this); path[1] = uniswapRouter.WETH(); uniswapRouter.swapExactTokensForETH( tokenAmount, 1, // 设置最小接收ETH数量,防止滑点过大 path, msg.sender, block.timestamp + 300 // 延长过期时间,避免交易超时 ); emit TokensSwappedForEth(tokenAmount, address(msg.sender).balance); } // 用户添加代币+ETH到Uniswap流动性池 function addLiquidity(uint256 tokenAmount) external payable nonReentrant { require(balanceOf(msg.sender) >= tokenAmount, "Insufficient token balance"); require(msg.value > 0, "ETH amount cannot be zero"); // 转移用户的代币到合约,再授权Router使用 _transfer(msg.sender, address(this), tokenAmount); _approve(address(this), address(uniswapRouter), tokenAmount); (uint256 tokenAdded, uint256 ethAdded, uint256 liquidity) = uniswapRouter.addLiquidityETH{value: msg.value}( address(this), tokenAmount, tokenAmount * 95 / 100, // 5%滑点容忍度 msg.value * 95 / 100, msg.sender, block.timestamp + 300 ); emit LiquidityAdded(tokenAdded, ethAdded, liquidity); } // 合约所有者初始化流动性池(首次添加流动性) function initLiquidity(uint256 tokenAmount) external payable onlyOwner nonReentrant { require(balanceOf(msg.sender) >= tokenAmount, "Owner insufficient token balance"); require(msg.value > 0, "ETH amount cannot be zero"); _transfer(msg.sender, address(this), tokenAmount); _approve(address(this), address(uniswapRouter), tokenAmount); (uint256 tokenAdded, uint256 ethAdded, uint256 liquidity) = uniswapRouter.addLiquidityETH{value: msg.value}( address(this), tokenAmount, tokenAmount * 95 / 100, msg.value * 95 / 100, msg.sender, block.timestamp + 300 ); emit LiquidityAdded(tokenAdded, ethAdded, liquidity); } // 更新Uniswap Router地址 function setUniswapRouter(address _uniswapRouter) external onlyOwner { uniswapRouter = IUniswapV2Router02(_uniswapRouter); address pair = IUniswapV2Factory(uniswapRouter.factory()).getPair(address(this), uniswapRouter.WETH()); uniswapPair = pair == address(0) ? IUniswapV2Factory(uniswapRouter.factory()).createPair(address(this), uniswapRouter.WETH()) : pair; } // 铸造代币(仅所有者) function mint(address account, uint256 amount) external onlyOwner { _mint(account, amount); emit TokensMinted(account, amount); } receive() external payable {} }
关键修改说明
- 拆分功能:将原函数拆分为
swapTokensForEth(代币换ETH)和addLiquidity(添加流动性)两个独立函数,逻辑更清晰,符合Uniswap的使用流程。 - 修正资金流向:兑换ETH时直接转给用户,添加流动性时由用户通过
msg.value传入ETH,确保合约有足够的ETH用于流动性添加。 - 授权逻辑修正:通过
_transfer将用户代币转移到合约后,再授权Router使用,确保Router有足够的代币权限。 - 滑点与超时优化:设置5%的滑点容忍度和5分钟的交易过期时间,避免因市场波动导致交易失败。
- 初始化流动性:新增
initLiquidity函数,由合约所有者首次添加流动性,为用户兑换提供基础储备。
内容的提问来源于stack exchange,提问作者Kellen Rippin
相关产品推荐
相关产品推荐

