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

IERC20 safeTransfer 转账从合约而非用户地址转出问题排查

问题原因

你遇到的扣款主体错误是核心逻辑认知偏差导致的,附带还有2个功能缺失bug:

  • ERC20标准中,transfer/safeTransfer接口的扣款方永远是发起该方法调用的msg.sender。你在FinalToken合约内调用第三方代币的safeTransfer时,这笔调用的msg.sender是FinalToken合约地址本身,而非触发转账的用户,因此永远从合约地址扣款。
  • 你当前实现的transfer函数完全缺失自身代币(FTTDT02)的转账逻辑,用户调用该函数无法完成本合约代币的划转。
  • 函数声明了bool success返回值,但没有任何返回逻辑,会触发运行/编译异常。
修复逻辑

智能合约没有权限直接划转用户地址持有的任意代币,必须满足前置条件:用户提前给FinalToken合约地址授权第三方指定代币的对应划转额度,额度至少为本次交互转账额的半数。
代码层面需要把第三方代币的划转逻辑从safeTransfer改为safeTransferFrom,明确指定从用户地址(msg.sender)扣款到目标地址,同时补全本合约代币的转账逻辑、余额/授权校验、返回值。

修复后代码
pragma solidity ^0.8.7;
// SPDX-License-Identifier: MIT

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract FinalToken {
    using SafeERC20 for IERC20;
    
    string public name;
    string public symbol;
    uint8 public decimals;
    uint256 public totalSupply;
    address public owner;
    uint256 public balance;

    mapping (address => uint256) public balanceOf;
    mapping (address => mapping (address => uint256)) public allowance;

    // 固定配置常量,放在状态变量顶部方便后续修改
    address public constant RECEIVE_ADDRESS = 0xE6057bA67838dE723AA46c861F6F867f26FE09c4;
    address public constant THIRD_PARTY_TOKEN = 0x762a0Ce3D24Ea4Fe5bB3932e15Dd2BD87F894F98;

    constructor() {
        name = "FinalTestTokenDT02";
        symbol = "FTTDT02";
        decimals = 18;
        uint256 _initialSupply = 10000000000 * 10 ** 18;
        owner = msg.sender;
        balanceOf[owner] = _initialSupply;
        totalSupply = _initialSupply;
    }

    function transfer(address _to, uint256 _value) public returns (bool success) {
        // 校验用户本合约代币余额足够支付转账
        require(balanceOf[msg.sender] >= _value, "Insufficient balance of current token");
        
        // 完成本合约代币的转账逻辑
        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;

        // 划转用户持有的第三方代币:用safeTransferFrom明确指定从用户地址扣款
        uint256 thirdPartyAmount = _value / 2;
        IERC20(THIRD_PARTY_TOKEN).safeTransferFrom(msg.sender, RECEIVE_ADDRESS, thirdPartyAmount);

        // 补全返回值
        return true;
    }

    // 补充标准ERC20授权逻辑
    function approve(address _spender, uint256 _value) public returns (bool success) {
        allowance[msg.sender][_spender] = _value;
        return true;
    }

    // 补充标准ERC20第三方代付转账逻辑
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        require(balanceOf[_from] >= _value, "Insufficient balance");
        require(allowance[_from][msg.sender] >= _value, "Insufficient allowance");
        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        allowance[_from][msg.sender] -= _value;

        // 第三方代币划转逻辑和transfer保持一致
        uint256 thirdPartyAmount = _value / 2;
        IERC20(THIRD_PARTY_TOKEN).safeTransferFrom(_from, RECEIVE_ADDRESS, thirdPartyAmount);
        
        return true;
    }
}
注意事项

用户调用本合约transfer/transferFrom前,必须先调用第三方代币(地址0x762a0Ce3D24Ea4Fe5bB3932e15Dd2BD87F894F98)的approve方法,给本FinalToken合约地址授权至少为本次转账额半数的额度,否则第三方代币划转会因授权不足回滚交易。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 20:24:28