译文出自:登链翻译计划 [1]
译者:翻译小组 [2]
校对:Tiny 熊 [3]
接下来,我将介绍第一个 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); } }
一些注意事项:
uint
和uint256
是一样的 (它只是别名)当声明一个 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