Solidity 可支付函数

文章介绍了如何在Solidity中与以太坊互动,特别是如何通过payable函数接收和发送以太币,并解释了以太币的单位及在区块链中的处理方式。

到目前为止,我们一直在使用代币来表示价值,但以太坊(Ether)呢?让我们介绍一下智能合约如何与以太坊进行交互。

contract ExampleContract {
    function payMe()
        public
        payable {

    }

    function howMuchEtherIHave()
        public
        view
        returns (uint256) {
            return address(this).balance;
    }
}

在你在 Remix 上部署此合约后,你会注意到与 payMe 交互的按钮变为红色。这意味着你可以在上面的字段中指定在调用此函数时要发送多少价值(以太坊)。

https://static.wixstatic.com/media/61a666_207085b0008a42d4b5dc8349cf7c802f~mv2.png

你可以以 Wei、Gwei、Finney 或以太坊为单位发送以太坊。

1 Wei 是 1/10^18 以太坊,1 gwei 是以太坊的十亿分之一,而 1 Finney 是十分之一。

让我们简单一点,直接发送一个以太坊。

当我们点击 “howMuchEtherIHave” 时,实际上我们会得到

1000000000000000000

这并不意味着我们凭空创造了大量以太坊。记住,浮点数在区块链上并不存在,因此以太坊使用与 ERC20 代币相同的小数策略。以太坊的一个单位实际上是一 Wei,而我们传统上认为的一个以太坊是 10^18 Wei。

顺便说一下,.balance 结构可以用于任意地址。智能合约可以通过以下函数确定你有多富有(或多贫穷)

function howMuchEtherYouHave()
    public
    view
    returns (uint256) {
        return msg.sender.balance;
}

function howMuchEtherTheyHave(address them)
    public
    view
    returns (uint256) {
        return them.balance;
}

10000000000000000000

除非函数具有 payable 修饰符,否则如果它们接收以太坊,则会 revert。

为什么要有这样的结构?如果有人想要给我们发送以太坊,为什么不接受呢?

这一直是一个争论的话题,但一般的想法是,一个函数应该被限制成具有极其明确定义的行为。任何超出这一范围的内容都应该受到限制。行为越受限,越容易理清智能合约的功能。

顺便说一下,solidity 提供了一个非常方便的关键字来处理与以太坊有关的所有零。这两个函数做的是同一件事,但一个更具可读性。

function moreThanOneEtherV1()
    public
    view
    returns (bool) {
        if (msg.sender.balance > 1 ether) {
            return true;
        }
        return false;
}

function moreThanOneEtherV2()
    public
    view
    returns (bool) {
        if (msg.sender.balance > 10**18) {
            return true;
        }
        return false;
}

如果你希望你的智能合约一开始就有特权和优势,可以将构造函数设为 payable。但你仍需在构造时显式发送以太坊。

仅仅因为一个函数是 payable,并不意味着调用该函数的人必须发送以太坊。

contract ExampleContract {

    constructor() payable {
        // 以资金开始生活
    }
}

发送以太坊

如果你从 Remix 发起交易,很清楚如何发送以太坊,但是如果另一个智能合约想要发送以太坊呢?

你将使用我们之前描述的 call 函数,但增加一个“元参数”。一开始可能看起来很奇怪,但你会习惯的。

contract ReceiveEther {
    function takeMoney()
        public
        payable {

    }

    function myBalance()
        public
        view
        returns (uint256) {
            return address(this).balance;
    }
}

contract SendMoney {
    constructor()
        payable {

    }

    function sendMoney(address receiveEtherContract)
        public
        payable {
            uint256 amount = myBalance();
            (bool ok, ) = receiveEtherContract.call{value: amount}(
                abi.encodeWithSignature("takeMoney()")
            );
            require(ok, "转账失败");
    }

    function myBalance()
        public
        view
        returns (uint256) {
            return address(this).balance;
    }
}

让我们拆解一下我们所看到的内容。

  • call 之间有一个看起来像 JSON 的对象,这是发送以太坊的方法。 “value” 键确定发送的金额。默认值为零。
  • 我们有一个带空的第二个参数的元组; (bool ok,)。这意味着我们忽略了 takeMoney() 的返回值。如果我们不关心返回值,就使用这个结构。
  • 我们仍然关心转账是否失败。因此我们有 require(ok) 结构来处理。

一些实验

  • 部署两个合约,但在构造时向 SendMoney 提供以太坊。在调用 sendMoney 之前和之后查看两个合约的 myBalance。
  • 移除 takeMoney 的 “payable” 修饰符,看看会发生什么(应该会 revert)。
  • 调用 sendMoney 时使用以太坊,请注意 receiveEther 的余额如何增加。

Payable 函数不能是 view 或 pure

改变智能合约的以太坊余额是区块链上的“状态变化”。这是一个永久性改变,甚至在交易结束后仍会保留,类似于更新一个存储变量。因此,payable 函数不能是 view 或 pure。编译器不接受这一点。

练习问题

PriceIsRight

进一步学习

请查看 区块链培训营, 以了解更多有关智能合约开发和代币标准的信息。

  • 原文链接: rareskills.io/learn-soli...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
RareSkills
RareSkills
https://www.rareskills.io/