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

为何使用send()和transfer()无法向其他合约发送ETH?

Solidity send/transfer转账失败,call成功的原因与解决

核心原因

send()和transfer()方法仅会发送2300固定gas,而你的Receiver合约receive()函数中执行了payable(receiver).transfer(msg.value)操作,该操作消耗的gas远超2300,导致调用失败;call()默认会分配当前可用的全部gas,足以支撑该操作,因此成功。

详细解释

Solidity设计时为防范重入攻击,给send()和transfer()设置了2300 gas的上限,这个gas量仅能完成最基础的EVM操作(比如简单的状态变量修改)。但你的receive()函数里的转账操作本身需要消耗额外gas(包括修改账户余额、触发接收方的receive/fallback函数等),2300gas不足以覆盖这些消耗,所以交易回滚。

而call{value: msg.value}("")没有固定gas限制(除非手动指定),会使用当前交易剩余的全部gas,足够支撑receive()函数内的转账逻辑,因此能成功执行。

修复方案

如果要保留send()/transfer()的使用,需要修改Receiver合约的receive()函数,避免在其中执行高gas消耗的操作,或者将内部转账改用call()并指定足够gas:

修改后的Receiver合约:

pragma solidity ^0.8.0;

contract Receiver {
    address receiver;
    uint public prize;
    address public owner;

    constructor() payable {
        receiver = msg.sender;
        owner = msg.sender;  
        prize = 0.001 ether;
    }

    receive() external payable {
        require(msg.value >= prize || msg.sender == owner);
        // 改用call并指定足够gas,避免gas不足
        (bool success, ) = payable(receiver).call{value: msg.value, gas: 200000}("");
        require(success, "Internal transfer failed");
        receiver = msg.sender;
        prize = msg.value;
    }
}

最佳实践

当前Solidity官方推荐优先使用call()进行ETH转账,因为它更灵活,可以自定义gas量,也不会因为固定gas限制导致合法操作失败。


原问题背景

我刚学习Solidity,对ETH的发送方法存在疑问。编写了两个Solidity智能合约,通过Remix IDE(环境为Remix VM(Merge))部署,尝试以send()、transfer()、call()三种方式从Sender合约向Receiver合约发送1 ETH,结果仅call()方法成功,send()和transfer()均失败。

原合约代码:

pragma solidity ^0.8.0;

contract Receiver {
    address receiver;
    uint public prize;
    address public owner;

    constructor() payable {
      receiver = msg.sender;
      owner = msg.sender;  
      prize = 0.001 ether;
    }

    receive() external payable {
      require(msg.value >= prize || msg.sender == owner);
      payable(receiver).transfer(msg.value);
      receiver = msg.sender;
      prize = msg.value;
    }

}


contract Sender {

    constructor() payable{
    }

    function sendWithSend(address payable _to) public payable{
        bool success = _to.send(msg.value);
        require(success, "failed");
    }
    function sendWithTransfer(address payable _to) public payable{
        _to.transfer(msg.value);      
    }
    function sendWithCall(address payable _to) public payable{
        (bool sent, ) = _to.call{value: msg.value}("");
        require(sent, "Failed" );
    }
}

尝试用每个函数发送1 ETH,预期Receiver合约最终收到3 ETH,但实际仅收到1 ETH,send()和transfer()多次尝试仍无法成功。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 13:30:32