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
相关产品推荐
相关产品推荐

