Solidity调用Uniswap V2配对合约获取币价触发交易回退问题求解
错误触发原因
- 缺少接口声明:代码中直接使用了
IUniswapV2Pair和IERC20接口,但没有提前定义或导入这两个接口,合约编译时无法识别接口对应的方法,调用时会直接回滚。 - 函数修饰符配置错误:
getTokenPrice仅做链上数据读取,不需要修改状态也不需要接收ETH,你额外加了payable修饰符且未加view修饰符,Remix会默认将该调用识别为状态修改类交易发起,而非本地只读调用。如果调用时你传入了超过账户余额的ETH,或是账户没有足够ETH支付gas,都会触发你看到的报错。 - 代币顺序逻辑有漏洞:Uniswap V2交易对的token0、token1是按地址哈希大小排序的,ETH/USDT交易对中token0为WETH、token1为USDT,你当前代码固定取token1的小数位计算,没有适配你要查询的价格方向,极端情况会出现除零报错(该交易对不会出现,但逻辑不通用)。
- 数值溢出风险:如果你使用的是0.8以下版本的Solidity,
Res0*(10**token1.decimals())的运算很容易触发整数溢出,直接导致交易回滚。
修复方案
代码调整
首先在合约顶部补充两个接口的定义,再修改getTokenPrice的修饰符和计算逻辑,参考代码如下:
// 补充接口定义 interface IERC20 { function decimals() external view returns (uint8); } interface IUniswapV2Pair { function token0() external view returns (address); function token1() external view returns (address); function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); } contract Uniswap { constructor () public payable { } function getBalance() public view returns (uint) { return address(this).balance; } // 优化后的价格查询函数:isToken0In为true表示输入token0数量,查询可兑换的token1数量,反之则相反 function getTokenPrice(address pairAddress, uint inAmount, bool isToken0In) public view returns(uint outAmount) { IUniswapV2Pair pair = IUniswapV2Pair(pairAddress); IERC20 token0 = IERC20(pair.token0()); IERC20 token1 = IERC20(pair.token1()); (uint112 reserve0, uint112 reserve1,) = pair.getReserves(); require(reserve0 > 0 && reserve1 > 0, "交易对储备为空"); uint8 inDecimals = isToken0In ? token0.decimals() : token1.decimals(); uint8 outDecimals = isToken0In ? token1.decimals() : token0.decimals(); uint inReserve = isToken0In ? reserve0 : reserve1; uint outReserve = isToken0In ? reserve1 : reserve0; // 统一精度后计算,避免小数位差异导致结果错误 uint inAmountAdjusted = inAmount * (10 ** 18) / (10 ** inDecimals); uint outAmountAdjusted = (inAmountAdjusted * outReserve) / inReserve; outAmount = outAmountAdjusted * (10 ** outDecimals) / (10 ** 18); return outAmount; } }
如果使用的是0.8以下版本的Solidity,建议导入SafeMath库处理算术运算,避免溢出回滚。
调用逻辑调整
你使用的ETH/USDT交易对中token0为WETH(18位小数)、token1为USDT(6位小数):
- 要查询1 ETH可兑换的USDT数量时,
pairAddress填交易对地址,inAmount填1000000000000000000(对应1 WETH的精度),isToken0In填true。 - 调用时不要传入任何ETH,因为调整后函数为
view类型,Remix会走本地只读调用,不需要支付gas,也不会发起链上交易。
内容的提问来源于stack exchange,提问作者Viktor.w
相关产品推荐
相关产品推荐

