向智能合约存入ERC20代币报错,求所有者提取代币实现方案
问题分析与合约修复
核心问题点
- 所有者提取代币逻辑错误:
ownerWithdrawal函数误用transferFrom,该方法需要合约自身给自己授权,完全没必要。合约作为USDC的持有者,直接调用transfer即可转出代币。 - 存入函数缺少必要校验:
depositTokens未检查存入金额是否大于0,且参数名$USDC不符合Solidity常规命名规范,易造成混淆。 withdrawalTokens逻辑无效:仅重置用户的stakingBalance但未实际转出代币,当前逻辑是所有者清零任意用户余额,不符合常规业务逻辑。- 权限校验冗余:多处函数重复编写
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
相关产品推荐
相关产品推荐

