你真的了解区块链吗?智能合约如何接收eth

目前来看,智能合约接受以太共有五种可能性;

1. receive()

一个合约最多有一个 receive 函数, 声明函数为: receive() external payable {}

无需 function 关键字,也没有参数和返回值并且必须是external可见性和payable 修饰。 它可以是 virtual 的,可以被重载也可以有修改器modifier 。

在对合约没有任何附加数据调用(通常是对合约转账)是会执行 receive 函数。

例如通过 .send() or .transfer() 如果 receive 函数不存在,但是有payable的 fallback 回退函数 那么在进行纯以太转账时,fallback 函数会被调用。

如果两个函数都没有,这个合约就没法通过常规的转账交易接收以太,会抛出异常。

并且,receive 函数只有 2300 gas 可以使用, 除了基础的日志输出之外,进行其他操作的余地很小。下面的操作消耗会操作 2300 gas :

  • 写入存储
  • 创建合约
  • 调用消耗大量 gas 的外部函数
  • 发送以太币

不过,与任何其他函数一样,只要有足够的 gas 传递给它,回退函数就可以执行复杂的操作。

pragma solidity ^0.6.0;

// 这个合约会保留所有发送给它的以太币,没有办法取回。
contract Sink {
    event Received(address, uint);
    receive() external payable {
        emit Received(msg.sender, msg.value);
    }
}

2. 实现payable的fallback()

合约可以最多有一个回退函数。函数声明为: fallback() external [payable]fallback() (bytes calldata input) external [payable] returns (bytes memory output)

没有function关键字。必须是external可见性,它可以是 virtual 的,可以被重载也可以有 修改器modifier 。

fallback 函数始终会接收数据,但为了同时接收以太时,必须标记为payable

如果使用了带参数的版本, input 将包含发送到合约的完整数据(等于 msg.data ),并且通过 output 返回数据。 返回数据不是 ABI 编码过的数据,相反,它返回不经过修改的数据。

如果回退函数在接收以太时调用,只有 2300 gas 可以使用。

不过,与任何其他函数一样,只要有足够的 gas 传递给它,回退函数就可以执行复杂的操作。

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.2 <0.9.0;

contract Test {
    // 发送到这个合约的所有消息都会调用此函数(因为该合约没有其它函数)。
    // 向这个合约发送以太币会导致异常,因为 fallback 函数没有 `payable` 修饰符
    fallback() external { x = 1; }
    uint x;
}


// 这个合约会保留所有发送给它的以太币,没有办法返还。
contract TestPayable {
    uint x;
    uint y;

    // 除了纯转账外,所有的调用都会调用这个函数.
    // (因为除了 receive 函数外,没有其他的函数).
    // 任何对合约非空calldata 调用会执行回退函数(即使是调用函数附加以太).
    fallback() external payable { x = 1; y = msg.value; }

    // 纯转账调用这个函数,例如对每个空empty calldata的调用
    receive() external payable { x = 2; y = msg.value; }
}

contract Caller {
    function callTest(Test test) public returns (bool) {
        (bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
        require(success);
        //  test.x 结果变成 == 1。

        // address(test) 不允许直接调用 ``send`` ,  因为 ``test`` 没有 payable 回退函数
        //  转化为 ``address payable`` 类型 , 然后才可以调用 ``send``
        address payable testPayable = payable(address(test));


        // 以下将不会编译,但如果有人向该合约发送以太币,交易将失败并拒绝以太币。
        // test.send(2 ether);
    }

    function callTestPayable(TestPayable test) public returns (bool) {
        (bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
        require(success);
        // 结果 test.x 为 1  test.y 为 0.
        (success,) = address(test).call{value: 1}(abi.encodeWithSignature("nonExistingFunction()"));
        require(success);
        // 结果test.x 为1 而 test.y 为 1.

        // 发送以太币, TestPayable 的 receive 函数被调用.

        // 因为函数有存储写入, 会比简单的使用 ``send`` or ``transfer``消耗更多的 gas。
        // 因此使用底层的call调用
        (success,) = address(test).call{value: 2 ether}("");
        require(success);

        // 结果 test.x 为 2 而 test.y 为 2 ether.

        return true;
    }

}

注:实现payable的fallback() 和receive() 的区别

  1. receive()优先接受纯以太的交易
  2. 实现payable的fallback()优先接受附带msg.data的交易

3. 实现payable的函数

这种方式无需多言,加个payable关键词就可以了!

4. selfdestruct()

自毁函数是具有攻击性的一种让其它合约被迫接受以太的方式

contract Attack{

 address private owner;

 constructor(){
 owner = msg.sender;
 }
 
 event Received(address, uint);
    receive() external payable {
        emit Received(msg.sender, msg.value);
  }
  
 //自毁转账
 function selfdestructAttack(address _to) external public{
     require(msg.sender == this.owner,"you are not the owner");
     selfdestruct(_to);
 }

}

如上例所示,当调用合约的selfdestructAttack函数时,此合约将会自毁,并且将合约内的余额强制发送到 _ to这个合约地址上,无论_ to合约是否实现接受以太的函数,都不得不接受这笔转账。

5. miner区块奖励

我们常说的挖矿奖励,当挖出了一个新区块时,以太奖励将会达到挖出人指定的地址上,无论这个地址是什么,它都会多出这笔奖励余额;

如今以太坊转型为PoS权益证明机制,miner区块奖励将会逐渐销声匿迹。

更多区块链技术干货请关注

岚链技术论坛
77Brother的技术小栈(个人博客)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值