与合约交互

让我们回顾一下我们迄今为止学到的东西:以太坊合约是控制货币的程序,运行在名为EVM的虚拟机内。它们是由一个特殊的交易创建的,该交易提交它们的字节码以记录在区块链中。一旦他们在区块链上创建,他们就拥有一个以太坊地址,就像钱包一样。只要有人将交易发送到合约地址,它就会导致合约在EVM中运行,并将交易作为其输入。发送到合约地址的交易可能包含以太网或数据或两者都有。如果它们含有ether,则将其“存入”合约余额。如果它们包含数据,则数据可以在合约中指定一个命名函数并调用它,并将参数传递给该函数。

在区块浏览器中查看合约地址

现在,我们在区块链中登记了一份合约,我们可以看到它有一个以太坊地址。让我们在 ropsten.etherscan.io 区块浏览器中查看它,看看合约是什么样子。通过点击名称旁边的剪贴板图标来复制合约的地址。

Copy the contract address from Remix

Figure 18. Copy the contract address from Remix

保持Remix打开在标签中,我们稍后会再回来。现在,将浏览器导航至 ropsten.etherscan.io 并将地址粘贴到搜索框中。你应该看到合约的以太坊地址记录:

View the Faucet contract address in the etherscan block explorer

Figure 19. View the Faucet contract address in the etherscan block explorer

为合约提供资金

现在,合约其历史上只有一笔交易:合约创建交易。如你所见,合约也没有ether(零余额)。这是因为我们没有在创建交易中向合约发送任何提示,尽管我们可以提供。

让我们向合约发一些ether!你仍然应该在剪贴板中拥有合约的地址(如果没有,请从Remix再次复制)。打开MetaMask,然后向它发送1个ether,就像任何其他以太坊地址一样:

与合约交互 - 图3

Figure 20. Send 1 ether to the contract address

一分钟后,如果你刷新etherscan区块浏览器,它会向合约地址显示另一个交易,并更新1 ether的余额。

还记得我们的 Faucet.sol 代码中的未命名默认公共付费功能?它看起来像这样:

  1. function () public payable {}

当你将交易发送到合约地址时,没有指定要调用哪个函数的数据,它将调用默认函数。由于我们将它声明为+payable+,因此它接受1 ether并存入合约账户余额中。你的交易导致合约在EVM中运行,更新其余额。我们资助了我们的faucet!

从我们的合约中提取

接下来,让我们从faucet中提取一些资金。要提取,我们必须构造一个调用 withdraw 函数并将 withdraw_amount 参数传递给它的交易。为了保持现在简单,Remix将为我们构建该交易,并且MetaMask将提交它以供我们批准。

返回到Remix选项卡并在“Run”选项卡下查看合约。你应该看到一个标记为 withdraw 的红色框,其中带有一个标记为 uint256 withdraw_amount:

The withdraw function of Faucet.sol, in Remix

Figure 21. The withdraw function of Faucet.sol, in Remix

这是合约的Remix界面。它允许我们构造调用合约中定义的函数的交易。我们将输入 withdraw_amount 并点击 withdraw 按钮以生成交易。

首先,我们来看看 withdraw_amount。我们要试着提取0.1 ether,这是我们合约允许的最高金额。请记住,以太坊中的所有货币值都以 wei + 计价,而我们的 +withdraw 函数预期 withdraw_amount 也以 wei 计价。我们想要的数量是0.1 ether,这是 100000000000000000 wei(1后面跟着17个零)。

Tip

由于JavaScript的限制,Remix无法处理10^17这样大的数字。相反,我们用双引号括起来,让Remix以字符串的形式接收它,并将它作为 BigNumber 进行操作。如果我们不把它放在引号中,那么Remix IDE将无法处理它并显示“Error encoding arguments:Error:Assertion failed” 。 译者注:翻译此书时,已经支持直接输入数字

输入“100000000000000000”(带引号)到 withdraw_amount 框中,然后单击 withdraw 按钮:

与合约交互 - 图5

Figure 22. Click “withdraw” in Remix to create a withdrawal transaction

MetaMask将弹出一个交易窗口供你批准。点击“Submit”将你的提款通知发送至合约:

MetaMask transaction to call the withdraw function

Figure 23. MetaMask transaction to call the withdraw function

等一下,然后重新加载 etherscan 区块浏览器以查看在ether合约地址历史记录中反映的交易:

Etherscan shows the transaction calling the withdraw function

Figure 24. Etherscan shows the transaction calling the withdraw function

我们现在看到一个新的交易,其中合约地址是目标地址,0 ether。合约余额已经改变,现在是0.9 ether,因为它按要求给了我们0.1 ether。但是我们在合约地址历史记录中看不到“OUT”交易。

提款的交易在哪里?合约的地址历史记录页面中出现了一个名为“内部交易”的新选项卡。由于0.1 ether传输源于合约代码,因此它是一个内部交易(也称为_message_)。点击“内部交易”标签查看:

Etherscan shows the internal transaction transferring ether out from the contract

Figure 25. Etherscan shows the internal transaction transferring ether out from the contract

这个“内部交易”是由合约在这行代码中发送的(Faucet.sol 的 withdraw 方法)

  1. msg.sender.transfer(withdraw_amount);

回顾一下:我们从MetaMask钱包发送了一个包含数据指令的交易,以 0.1 ether 的+withdraw_amount+ 参数调用 withdraw 函数。该交易导致合约在EVM内部运行。当EVM运行faucet合约的 withdraw 功能时,首先它调用+require+函数并验证我们的金额小于或等于最大允许提款0.1 ether。然后它调用 transfer 函数向我们发送ether。运行 transfer 函数生成一个内部交易,从合约的余额中将0.1以太币存入我们的钱包地址。这就是 etherscan 中“内部交易”标签中显示的内容。