• 译文出自:登链翻译计划 [1]

  • 译者:翻译小组 [2]

  • 校对:Tiny 熊 [3]

scaffold-eth 挑战:实现质押 dApp (Part1)

接下来,我将介绍第一个 scaffold-eth 学习项目:创建一个质押 dApp[4]。

质押 dApp 是干什么的

这个项目的最终目标是模仿以太坊 2.0 的质押合约,需求非常简单:

  • 允许任何人质押 ETH 并跟踪余额

  • 如果时间到了 deadline 或者质押金额达到阀值,用户就不能提款了(这些资金会被用于未来的项目,如以太坊 POS )

可以学到什么?

  • 设置 scaffold-eth 项目

  • 编写一个质押合约

  • 调用一个外部合约

  • 为你的 Solidity 合约创建单元测试

  • 在你的本地机器上用 React 测试合约

  • 在以太坊测试网上部署质押合约 !

你可以把这看作是我们开发旅程的第一步。

一些你时常会用到的链接

  • Solidity 示例 [5]

  • Solidity 文档(中文)[6]

  • Hardhat 文档(中文)[7]

  • Ethers-js 文档(中文)[8]

  • OpenZeppelin 文档(中文)[9]

  • OpenZeppelin Ethernaut 教程 [10]

  • CryptoZombies 教程 [11]

设置 scaffold-eth 项目

首先,我们需要设置克隆 scaffold-eth 库,切换到 challenge 1 分支,并安装所有需要的依赖:

    git clone https://github.com/austintgriffith/scaffold-eth.git challenge-1-decentralized-staking      cd challenge-1-decentralized-staking      git checkout challenge-1-decentralized-staking      yarn install  

可用的 CLI 命令概述

这些命令并不是只适用于本次挑战,而是适用于每个 scaffold-eth 项目:

    yarn chain  

这个命令会启动本地 Hardhat 网络并配置好地址 http://localhost:8545

    yarn start  

这个命令会启动你的本地 react 网站,地址为 http://localhost:3000/

    yarn deploy  

这个命令会部署所有合约并刷新 react 程序。准确地说,这个命令会运行两个 javascript 脚本(部署和发布)。

打开三个不同的终端,执行这些命令。每次修改合约,你只需要重新执行 deploy 命令。

练习 part1:实现 stake() 方法

在这部分练习中,我们想做到用户可以在合约中质押 ETH,并可以随时查看余额变动。

重要概念

  • Payable 方法 [12] - 当一个函数被声明为 payable 时,意味着允许用户向其发送 ETH (见文档 [13])。

  • 映射 [14] - 这是 Solidity 支持的变量类型 [15] 之一。它可以将一个 key 与一个 value 映射起来(见文档 [16])。

  • 事件 [17] - 事件可以让合约通知其他实体(如:web3 应用程序等等)发生了什么。当你声明一个事件时,你最多可以指定 3 个索引的参数,并且可以用第三方程序为这种特殊的参数过滤事件(见文档 [18])。

尝试实现

  • 声明一个保存余额的映射

  • 声明一个 1 ETH 的常量的阀值

  • 声明一个 Stake 事件,用来记录质押者地址和质押金额

  • 实现一个 payable 函数 stake(),用来更新质押者的余额。

更新合约代码

    // SPDX-License-Identifier: MIT      pragma solidity ^0.8.4;      import "hardhat/console.sol";      import "./ExampleExternalContract.sol";      /**     * @title Stacker Contract      * @author scaffold-eth      * @notice A contract that allow users to stack ETH      */      contract Staker {        // External contract that will old stacked funds        ExampleExternalContract public exampleExternalContract;        // Balances of the user's stacked funds        mapping(address => uint256) public balances;        // Staking threshold        uint256 public constant threshold = 1 ether;        // 合约事件        event Stake(address indexed sender, uint256 amount);        /**       * @notice Contract Constructor        * @param exampleExternalContractAddress Address of the external contract that will hold stacked funds        */        constructor(address exampleExternalContractAddress) public {          exampleExternalContract = ExampleExternalContract(exampleExternalContractAddress);        }        /**       * @notice Stake method that update the user's balance        */        function stake() public payable {          // update the user's balance          balances[msg.sender]  = msg.value;          // emit the event to notify the blockchain that we have correctly Staked some fund for the user          emit Stake(msg.sender, msg.value);        }      }  

一些注意事项:

  • uintuint256 是一样的 (它只是别名)

  • 当声明一个 public 变量,Solidity 即自动创建一个 getter 方法 [19]。这意味着有一个 yourVariableName() 方法可调用

  • 如果声明的变量没有初始化,那么它会自动被初始化为变量类型的默认值

  • Solidity 还有一些实用单位,如 wei, ethers, 或者 时间单位 [20]。

回顾一下:

  • 我们声明了 balances,可以保存每个用户的地址和其质押金额

  • 我们声明了一个阈值

  • 我们声明了一个 Stake 事件,当某用户质押了一定数量 ETH ,它会向区块链发出通知

  • 我们实现了一个 public payable 类型的 Stake 函数,它会更新用户的余额,并发出 Stake 事件。

你也许会有点奇怪,我们只是简单地更新质押金额,而没有初始化 balances[msg.sender] 的值。这是可行的,因为当声明变量时没有初始化,Solidity 会自动初始化为其类型的默认值,这里类型是 uint256,所以默认值是 0。

现在部署合约,从水龙头(Faucet)获取 ETH ,并尝试质押一些给合约。

检查下你是否完成下面所有项目,以便继续进行第二部分的练习:

  • 你能从水龙头(Faucet)获得 ETH 吗?

  • 你可以点击 Stake 按钮给合约转 0.5ETH 吗?

  • 这个事件是否通过用户界面(UI)触发的?

  • 你质押金额是否成功更新?

  • 合约余额是否更新?

以下是本次视频的演示:

https://www.youtube.com/watch?v=KfoNrlYxBKY


本翻译由 CellETF[21] 赞助支持。

来源:https://stermi.medium.com/how-to-write-your-first-decentralized-app-scaffold-eth-challenge-1-staking-dapp-b0b6a6f4d242

参考资料

[1]

登链翻译计划 :https://github.com/lbc-team/Pioneer

[2]

翻译小组 :https://learnblockchain.cn/people/412

[3]

Tiny 熊 :https://learnblockchain.cn/people/15

[4]

质押 dApp:https://github.com/austintgriffith/scaffold-eth/tree/challenge-1-decentralized-staking

[5]

Solidity 示例 :https://solidity-by-example.org/

[6]

Solidity 文档(中文):https://learnblockchain.cn/docs/solidity/

[7]

Hardhat 文档(中文):https://learnblockchain.cn/docs/hardhat/getting-started/

[8]

Ethers-js 文档(中文):https://learnblockchain.cn/docs/ethers.js/

[9]

OpenZeppelin 文档(中文):https://docs.openzeppelin.com/openzeppelin/

[10]

OpenZeppelin Ethernaut 教程 :https://ethernaut.openzeppelin.com/

[11]

CryptoZombies 教程 :https://cryptozombies.io/

[12]

Payable 方法 :https://solidity-by-example.org/payable/

[13]

文档 :https://learnblockchain.cn/docs/solidity/contracts.html#receive

[14]

映射 :https://solidity-by-example.org/mapping/

[15]

类型 :https://docs.soliditylang.org/en/v0.8.7/types.html

[16]

文档 :https://learnblockchain.cn/docs/solidity/types.html#mapping-types

[17]

事件 :https://solidity-by-example.org/events/

[18]

文档 :https://learnblockchain.cn/docs/solidity/contracts.html#events

[19]

getter 方法 :https://learnblockchain.cn/docs/solidity/contracts.html#getter

[20]

wei, ethers, 或者 时间单位 :https://learnblockchain.cn/docs/solidity/units-and-global-variables.html

[21]

CellETF:https://celletf.io/?utm_souce=learnblockchain

scaffold-eth 挑战:实现质押 dApp (Part1)