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

ERC20代币买卖税计算问题:eth_call模拟Swap无法返回含税金额

如何通过eth_call准确计算带交易税的代币Swap实际到账金额?

要计算代币在transfer()中实现的买卖税,你当前用eth_call模拟Uniswap的swapExactETHForTokens的方式存在局限:该调用返回的是Uniswap Pair基于流动性储备计算的理论输出金额,不会触发代币合约内部的transfer扣税逻辑,所以无法直接得到扣税后的实际到账金额。

问题核心

swapExactETHForTokens的返回值仅反映Pair合约计算的预期输出,而代币的交易税是在transfer执行时由代币合约自行扣除的,这部分逻辑不会体现在Router的函数返回结果里。

可行解决方案

方案1:直接模拟代币transfer计算税费

既然税费逻辑在代币的transfer函数中,直接模拟该操作即可准确计算扣税比例:

const ethers = require("ethers");
// 替换为你的代币地址、ABI和Provider
const tokenAddress = "0x...";
const ERC20_ABI = ["function balanceOf(address) view returns (uint256)", "function transfer(address to, uint256 amount) returns (bool)"];
const provider = new ethers.JsonRpcProvider("你的RPC地址");

const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
const fromAddr = "你的测试地址(需有足够代币余额)";
const toAddr = "任意接收地址";
// 替换为要测试的转账数量和代币小数位
const transferAmount = ethers.parseUnits("100", 18);

// 获取接收地址初始余额
const initialBalance = await tokenContract.balanceOf(toAddr);

// 用eth_call模拟transfer操作
await provider.call({
  to: tokenAddress,
  data: tokenContract.interface.encodeFunctionData("transfer", [toAddr, transferAmount]),
  from: fromAddr // 指定转出地址,确保transfer的余额检查通过
});

// 获取模拟后的接收地址余额
const finalBalance = await tokenContract.balanceOf(toAddr);

// 计算税费
const actualReceived = finalBalance - initialBalance;
const taxAmount = transferAmount - actualReceived;
const taxRate = (taxAmount / transferAmount) * 100;

console.log(`实际到账金额: ${ethers.formatUnits(actualReceived, 18)}`);
console.log(`税费比例: ${taxRate.toFixed(2)}%`);

方案2:结合Swap模拟+代币转账模拟

如果你需要结合Swap场景计算实际到账代币数量,可以先获取Swap的理论输出,再模拟该金额从Pair地址转出到用户地址的扣税过程:

const ethers = require("ethers");
const UNISWAP_ROUTER_ABI = require("./UniswapRouterABI.json");
const ERC20_ABI = ["function balanceOf(address) view returns (uint256)", "function transfer(address to, uint256 amount) returns (bool)"];
const provider = new ethers.JsonRpcProvider("你的RPC地址");

const routerAddress = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"; // Uniswap V2 Router
const router = new ethers.Contract(routerAddress, UNISWAP_ROUTER_ABI, provider);
const tokenAddress = "0x..."; // 目标代币地址
const wethAddress = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
const toAddr = "你的接收地址";
const ethAmountIn = ethers.parseUnits("2", 18);
const deadline = Math.floor(Date.now() / 1000) + 3600;
const path = [wethAddress, tokenAddress];

// 1. 模拟Swap获取理论输出金额
const encodedSwapData = router.interface.encodeFunctionData("swapExactETHForTokens", [
  0,
  path,
  toAddr,
  deadline,
]);
const swapCallResult = await provider.call({
  data: encodedSwapData,
  value: ethAmountIn,
  to: routerAddress,
});
const decodedSwapResult = router.interface.decodeFunctionResult("swapExactETHForTokens", swapCallResult);
const theoreticalTokenOut = decodedSwapResult[1];

// 2. 获取Uniswap Pair地址
const pairAddress = await router.getPair(wethAddress, tokenAddress);
const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);

// 3. 获取用户初始代币余额
const initialUserBalance = await tokenContract.balanceOf(toAddr);

// 4. 模拟Pair地址转理论输出金额给用户
await provider.call({
  to: tokenAddress,
  data: tokenContract.interface.encodeFunctionData("transfer", [toAddr, theoreticalTokenOut]),
  from: pairAddress,
});

// 5. 获取用户最终代币余额并计算实际到账
const finalUserBalance = await tokenContract.balanceOf(toAddr);
const actualTokenOut = finalUserBalance - initialUserBalance;
const taxAmount = theoreticalTokenOut - actualTokenOut;
const taxRate = (taxAmount / theoreticalTokenOut) * 100;

console.log(`Swap理论输出: ${ethers.formatUnits(theoreticalTokenOut, 18)}`);
console.log(`实际到账代币: ${ethers.formatUnits(actualTokenOut, 18)}`);
console.log(`税费比例: ${taxRate.toFixed(2)}%`);

注意事项

  • 模拟transfer时,from地址需要有足够的代币余额,否则模拟会失败;可以用测试网地址或本地节点的测试账户。
  • 部分代币的transfer逻辑可能有额外限制(比如黑名单、大额转账额外扣税),需要根据代币实际合约逻辑调整模拟方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 05:10:18