You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Uniswap V2 getAmountsOut查询极小面值代币价格返回0问题

问题原因

你遇到的返回0、计算偏差问题来自两个核心机制:

  • Solidity本身只支持整数运算,所有除法默认向下取整。CAW、SHINJA这类极低单价代币,1个最小单位对应的ETH数量远小于1wei(ETH的最小单位,1wei=1e-18 ETH),用1单位代币走getAmountsOut查询时,整数运算直接把小于1wei的部分截断,返回0。
  • getAmountsOut是交易场景下的报价函数,计算时会扣除Uniswap 0.3%的交易手续费,同时根据恒定乘积公式计算滑点。你把输入金额放大到10亿单位时,大额交易对应的滑点已经非常明显,用这个结果反推单币价格必然存在严重偏差,输入金额越大偏差越高。
正确实现方法

不要用路由合约的交易报价函数查现货价,直接读取交易对池内的储备金,通过恒定乘积公式计算无滑点现货价,这个方法不受代币单价高低、输入金额大小影响,结果和Uniswap官方信息页展示的价格完全一致。
实现步骤:

  • 从路由合约读取工厂合约地址,通过工厂合约查询目标代币和WETH的交易对地址
  • 实例化交易对合约,读取池内两种资产的储备量,同时确认两个代币的排列顺序(Uniswap V2交易对会按代币地址大小排序为token0、token1,不会和你传入的顺序一致)
  • 把两种资产的储备量按照各自的小数位转换为统一单位,直接做除法得到单枚代币对应的ETH价格,计算时建议用Decimal类型避免浮点数精度损失。

参考实现代码:

from web3 import Web3
from decimal import Decimal

# 最小化Uniswap V2合约ABI
pair_abi = [
    {"inputs":[],"name":"getReserves","outputs":[{"internalType":"uint112","name":"_reserve0","type":"uint112"},{"internalType":"uint112","name":"_reserve1","type":"uint112"},{"internalType":"uint32","name":"_blockTimestampLast","type":"uint32"}],"stateMutability":"view","type":"function"},
    {"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},
    {"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}
]
factory_abi = [
    {"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"getPair","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}
]

# 初始化合约实例
router = w3.eth.contract(address=Web3.toChecksumAddress(IUNISWAP_V2_ROUTER), abi=router_contract_abi)
factory_addr = router.functions.factory().call()
factory = w3.eth.contract(address=Web3.toChecksumAddress(factory_addr), abi=factory_abi)

token_addr = Web3.toChecksumAddress(token_metadata['address'])
weth_addr = Web3.toChecksumAddress(WETH)

# 校验交易对存在性
pair_addr = factory.functions.getPair(token_addr, weth_addr).call()
if pair_addr == "0x0000000000000000000000000000000000000000":
    raise Exception("目标代币对应WETH交易对不存在")
pair = w3.eth.contract(address=Web3.toChecksumAddress(pair_addr), abi=pair_abi)

# 读取储备和代币顺序
res0, res1, _ = pair.functions.getReserves().call()
t0 = pair.functions.token0().call()
t1 = pair.functions.token1().call()

# 单位转换后计算价格
token_decimals = int(token_metadata['decimals'])
weth_decimals = tokens_decimals["WETH"]

if t0 == token_addr:
    token_reserve = Decimal(res0) / Decimal(10 ** token_decimals)
    weth_reserve = Decimal(res1) / Decimal(10 ** weth_decimals)
else:
    token_reserve = Decimal(res1) / Decimal(10 ** token_decimals)
    weth_reserve = Decimal(res0) / Decimal(10 ** weth_decimals)

# 最终结果:1枚目标代币对应的ETH数量
token_eth_price = weth_reserve / token_reserve

如果需要得到USDC计价的代币价格,只需要用同样的方法读取WETH/USDC交易对的储备算出WETH价格,再和代币的ETH计价相乘即可。
不要用getAmountsOut计算现货基准价,这个函数仅适合估算实际交易时能拿到的到账金额,本身包含手续费和滑点计算,不适合作为定价依据。

内容的提问来源于stack exchange,提问作者AtiqGauri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 05:09:30