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

向智能合约存入ERC20代币报错,求所有者提取代币实现方案

问题分析与合约修复

核心问题点

  1. 所有者提取代币逻辑错误:ownerWithdrawal函数误用transferFrom,该方法需要合约自身给自己授权,完全没必要。合约作为USDC的持有者,直接调用transfer即可转出代币。
  2. 存入函数缺少必要校验:depositTokens未检查存入金额是否大于0,且参数名$USDC不符合Solidity常规命名规范,易造成混淆。
  3. withdrawalTokens逻辑无效:仅重置用户的stakingBalance但未实际转出代币,当前逻辑是所有者清零任意用户余额,不符合常规业务逻辑。
  4. 权限校验冗余:多处函数重复编写require(msg.sender == owner),可封装为修饰符简化代码。

修改后的完整合约代码

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

interface USDC {
    function balanceOf(address account) external view returns (uint256);
    function allowance(address owner, address spender) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}

contract transferUSDc {
    USDC public USDc;
    address public owner;
    mapping(address => uint) public stakingBalance;

    // 仅所有者可调用的权限修饰符
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    constructor() {
        USDc = USDC(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56);
        owner = msg.sender;
    }

    // 用户存入USDC代币
    function depositTokens(uint _amount) public {
        // 校验存入金额大于0
        require(_amount > 0, "Amount must be greater than 0");
        uint amount = _amount * 10 ** 6;

        // 从用户地址转至合约(用户需提前调用approve授权)
        bool success = USDc.transferFrom(msg.sender, address(this), amount);
        require(success, "Transfer failed");

        // 更新用户存入余额
        stakingBalance[msg.sender] += amount;
    }

    // 所有者提取合约内USDC代币
    function ownerWithdrawal(uint _amount) public onlyOwner {
        uint amount = _amount * 10 ** 6;
        // 校验合约内有足够余额
        require(USDc.balanceOf(address(this)) >= amount, "Insufficient contract balance");
        
        // 直接从合约转至所有者地址
        bool success = USDc.transfer(msg.sender, amount);
        require(success, "Withdrawal failed");
    }

    // 检查用户存入余额
    function checkBalance(address _address) public view returns (uint) {
        return stakingBalance[_address];
    }

    // 可选:用户提取自己存入的代币(若有需求)
    function userWithdrawal(uint _amount) public {
        uint amount = _amount * 10 ** 6;
        require(stakingBalance[msg.sender] >= amount, "Insufficient staking balance");
        
        stakingBalance[msg.sender] -= amount;
        bool success = USDc.transfer(msg.sender, amount);
        require(success, "Withdrawal failed");
    }
}

关键修改说明

  • 权限修饰符:新增onlyOwner修饰符,统一处理所有者权限校验,减少代码冗余。
  • 所有者提取逻辑修正:将transferFrom替换为transfer,并添加合约余额校验,确保提取金额不超过合约实际持有量。
  • 存入函数优化:添加金额大于0的校验,修改参数名为_amount符合规范,同时校验transferFrom返回值,避免转账失败后错误更新余额。
  • 新增用户提取函数:若需求包含用户可提取自己存入的代币,userWithdrawal函数完善了存提闭环逻辑。

存入代币注意事项

用户调用depositTokens前,必须先调用USDC合约的approve方法,授权本合约可转移指定数量的USDC,否则会导致transferFrom调用失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 01:45:35