Uniswap V3:部署含交易税机制的ERC20代币后无法添加流动性
解决Uniswap V3添加流动性失败问题并保留ERC20交易税机制
问题根源
你的ERC20合约在_update函数中对所有转账强制收取4%交易税,但Uniswap V3添加流动性时,合约会校验实际收到的代币数量是否等于预期值。由于扣税导致Pool实际到账数量仅为预期的96%,触发Uniswap的校验逻辑,交易直接回滚,无法完成流动性添加。
解决方案
修改合约逻辑,对Uniswap V3流动性操作和Pool间转账豁免交易税,同时保证普通转账、用户兑换场景的交易税正常收取。
修改后的完整合约代码
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; import "@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol"; import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; contract T2T is ERC20, Ownable { uint256 public constant TAX_PERCENT = 4; // 4%交易税 ISwapRouter public immutable uniswapRouter; INonfungiblePositionManager public immutable positionManager; address public immutable WETH; address public immutable taxReceiver; mapping(address => bool) public isExcludedFromTax; // 免税地址列表 constructor( address _uniswapRouter, address _positionManager, address _WETH ) ERC20("T2T", "T2T") Ownable(msg.sender) { _mint(msg.sender, 1_000_000 * 10 ** decimals()); // 铸造初始供应量 uniswapRouter = ISwapRouter(_uniswapRouter); positionManager = INonfungiblePositionManager(_positionManager); WETH = _WETH; taxReceiver = msg.sender; // 默认豁免合约自身和税接收地址的交易税 isExcludedFromTax[address(this)] = true; isExcludedFromTax[taxReceiver] = true; } // 仅合约所有者可配置免税地址 function setExcludedFromTax(address account, bool excluded) external onlyOwner { isExcludedFromTax[account] = excluded; } function _update(address sender, address recipient, uint256 amount) internal override { // 触发免税的场景: // 1. 发送方或接收方在免税列表中 // 2. 调用者是Uniswap V3流动性管理合约(PositionManager) // 3. 接收方是Uniswap V3 Pool地址 bool isTaxExempt = isExcludedFromTax[sender] || isExcludedFromTax[recipient] || msg.sender == address(positionManager) || _isUniswapV3Pool(recipient); if (isTaxExempt) { super._update(sender, recipient, amount); return; } // 正常收取交易税 uint256 taxAmount = (amount * TAX_PERCENT) / 100; uint256 amountAfterTax = amount - taxAmount; super._update(sender, taxReceiver, taxAmount); super._update(sender, recipient, amountAfterTax); } // 辅助函数:判断地址是否为Uniswap V3 Pool合约 function _isUniswapV3Pool(address account) internal view returns (bool) { (bool success, ) = account.staticcall(abi.encodeWithSignature("slot0()")); return success; } }
关键修改说明
- 新增免税地址管理:通过
isExcludedFromTax映射和setExcludedFromTax函数,允许所有者灵活配置无需扣税的地址 - 自动识别免税场景:
- 豁免Uniswap V3流动性操作(调用者为
NonfungiblePositionManager),确保添加/移除流动性时代币数量完全匹配Uniswap的校验要求 - 自动识别Uniswap V3 Pool地址,避免兑换过程中Pool间的代币转移被征税
- 默认豁免合约自身和税接收地址,防止循环转账问题
- 豁免Uniswap V3流动性操作(调用者为
- 保留核心税机制:普通用户转账、通过Uniswap兑换代币时,仍会自动扣除4%交易税并转入所有者地址
使用注意事项
- 部署合约时,需传入对应链的Uniswap V3
SwapRouter和NonfungiblePositionManager地址(以太坊主网地址分别为0xE592427A0AEce92De3Edee1F18E0157C05861564和0xC36442b4a4522E871399CD717aBDD847Ab11FE88,测试网需对应调整) - 添加流动性需通过Uniswap官方界面或调用
NonfungiblePositionManager的mint函数,此时合约会自动豁免交易税
内容的提问来源于stack exchange,提问作者Nitesh Mehra
相关产品推荐
相关产品推荐

