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

Ganache环境中调用Solidity合约payable buy()函数时发送方ETH余额未减少的问题排查与修复

问题分析与修复:调用buy()获取代币但ETH余额未减少

我来帮你拆解这个问题的核心原因,然后给出具体的修复步骤:

核心原因

1. 调用时的金额单位错误

你在Truffle控制台执行的MyCoinDEX.buy({value: 1})里,value:1的单位是wei(ETH的最小单位),1wei约等于0.000000000000000001ETH——这个金额太小了,Ganache的余额显示通常只保留到小数点后4-6位,所以视觉上看起来账户ETH余额没有变化,但实际上你的账户已经扣除了1wei的ETH。

2. 合约兑换逻辑的单位不匹配

你的合约注释写着“1 token == 1 ether”,但代码里直接把msg.value(ETH的wei数)作为代币数量来转账。如果你的MyCoinSupply代币小数位数是18(和ETH一致),这意味着你发送1wei ETH只能拿到1wei的代币(也就是0.000000000000000001个完整代币),这和你预期的“1ETH换1代币”的兑换比例完全不匹配。


修复方案

第一步:正确调用合约方法

在Truffle控制台里,必须使用web3.utils.toWei()把你要发送的ETH金额转换成wei单位,同时指定调用的测试账户。比如要发送1ETH兑换代币,执行以下命令:

// 先获取部署好的DEX合约实例
const dexInstance = await MyCoinDEX.deployed();
// 调用buy方法,发送1ETH,从第一个测试账户发起交易
await dexInstance.buy({
  value: web3.utils.toWei("1", "ether"),
  from: accounts[0]
});

执行完后再去Ganache查看账户余额,你会发现ETH余额减少了1ETH加上对应的Gas费用,同时代币余额会增加1个完整代币(根据你的代币小数位调整)。

第二步:修复合约的兑换逻辑

修改MyCoinDEX合约,确保兑换比例和你注释里的“1 token == 1 ether”一致,同时增加必要的安全检查:

pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./MyCoinSupply.sol";

contract MyCoinDEX {
    IERC20 public token;
    uint8 public tokenDecimals; // 存储代币的小数位数
    event Bought(uint256 amount);
    event Sold(uint256 amount);

    constructor() {
        token = new MyCoinSupply();
        tokenDecimals = token.decimals(); // 初始化代币小数位数
    }

    // 保留你的调试辅助函数
    function getSenderAddress() public view returns (address) {
        return msg.sender;
    }

    function getAddress() public view returns (address) {
        return address(this);
    }

    function getTokenAddress() public view returns (address) {
        return address(token);
    }

    function buy() payable public {
        uint256 ethWei = msg.value;
        require(ethWei > 0, "You need to send some ether");
        
        // 计算兑换的代币数量:1 ETH = 1 Token,适配代币小数位
        uint256 tokenAmount = (ethWei * 10 ** tokenDecimals) / 1 ether;
        uint256 dexTokenBalance = token.balanceOf(address(this));
        require(tokenAmount <= dexTokenBalance, "Not enough tokens in the reserve");
        
        token.transfer(msg.sender, tokenAmount);
        emit Bought(tokenAmount);
    }

    function sell(uint256 tokenAmount) public {
        require(tokenAmount > 0, "You need to sell at least some tokens");
        uint256 allowance = token.allowance(msg.sender, address(this));
        require(allowance >= tokenAmount, "Check the token allowance");
        
        // 计算返还的ETH数量:1 Token = 1 ETH
        uint256 ethWei = (tokenAmount * 1 ether) / 10 ** tokenDecimals;
        require(address(this).balance >= ethWei, "Not enough ETH in the reserve");
        
        token.transferFrom(msg.sender, address(this), tokenAmount);
        payable(msg.sender).transfer(ethWei);
        emit Sold(tokenAmount);
    }
}

这里做了几个关键优化:

  • 自动获取代币的小数位数,保证兑换逻辑适配不同小数位的代币
  • 使用Solidity内置的1 ether常量(等于1e18wei)简化计算,避免手动写死数值出错
  • 在sell函数里增加了合约ETH余额的检查,防止合约ETH不足时转账失败

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.01 01:02:32