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

ERC20代币存取款智能合约transfer/transferFrom调用报错如何解决?

问题根因与解决方案

一、depositToken 报错原因与修复

你写的IERC20(_token).approve(address(this), _amount)逻辑完全不生效:

  • ERC20的approve方法的调用者必须是代币所有者本人,作用是所有者给指定地址(spender)授权可划转的代币额度
  • 你在合约内部调用approve,本质是用合约地址作为调用方,给合约自身授权花用户的代币,权限完全不对,自然会触发transfer amount exceeds allowance报错。

修复步骤:

  1. 删掉depositToken方法里的IERC20(_token).approve(address(this), _amount);这行无效代码
  2. 用户调用depositToken之前,必须先主动调用对应ERC20代币合约的approve方法,给你的TestFunding合约地址授权不低于_amount的额度,再调用depositToken即可成功。

二、depositToken2 报错原因与修复

ERC20的transfer方法的调用者是代币所有者,作用是把调用者自己持有的代币转给接收方:

  • 你在合约内部调用transfer(address(this), _amount),本质是让合约把自己持有的代币转给自身,和「用户存币到合约」的需求完全不符
  • 如果你要靠transfer实现用户存币,应该让用户直接调用ERC20代币合约的transfer方法,接收方填你的TestFunding合约地址即可,不需要调用合约的depositToken2方法,这个方法本身逻辑错误可以直接删除。

三、额外优化:withdrawToken 冗余逻辑修复

你当前的withdrawToken用了approve+transferFrom的组合,属于冗余操作:

  • 合约本身就是自身持有代币的所有者,直接调用transfer方法转给用户即可,不需要先授权再划转。
  • 原有记账逻辑仅用单层级mapping,不区分不同代币的用户余额,存在严重逻辑漏洞,需要调整为双层mapping按用户+代币维度记账。

修正后完整合约代码

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

contract TestFunding{
    // 调整为双层mapping,按用户+代币维度记账
    mapping(address => mapping(address => uint)) public userTokenBalances;
    address public owner;
    uint public minimumDeposit;
    uint public totalDeposit;
    uint public noOfUsers;

    constructor(){
        owner = msg.sender;
    }
    
    function depositToken(address _token,uint _amount) public {
        require(IERC20(_token).transferFrom(msg.sender,address(this),_amount), "Deposit failed");
        // 新增用户统计逻辑
        if(userTokenBalances[msg.sender][_token] == 0) {
            noOfUsers += 1;
        }
        userTokenBalances[msg.sender][_token] += _amount;
        totalDeposit += _amount;
    }
    
    function getUserBalance(address _token) public view returns(uint)
    {
        return userTokenBalances[msg.sender][_token];   
    }
    
    function getCurrentBalance(address _token) public view returns(uint)
    {
        return IERC20(_token).balanceOf(address(this)) ; 
    }
    
    function getTokenBalance(address _token,address _account) public view returns(uint)
    {
        return IERC20(_token).balanceOf(_account) ; 
    }
    
    function withdrawToken(address _token,uint _amount) public
    {
        require(userTokenBalances[msg.sender][_token] >= _amount, "Insufficient balance");
        userTokenBalances[msg.sender][_token] -= _amount;
        totalDeposit -= _amount;
        require(IERC20(_token).transfer(msg.sender, _amount), "Withdraw failed");
    }
    
}

存币标准操作流程

  1. 用户调用对应ERC20代币的approve方法,spender参数填TestFunding合约地址,amount参数填要存入的数量
  2. 用户调用TestFunding合约的depositToken方法,传入代币地址和存入数量,即可完成存币。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 01:54:08