1.概述:
它是一个开发环境,,是关于“以太坊合约”和 “dApp" 开发的。
可以帮助“管理和自动化构建固有的重复性任务”。
它内置了 Hardhat Network(一个专为开发而设计的本地以太坊网络)(可以部署合约、运行测试和调试代码)。
-------------------------------------
必要的前置:
- Write code in JavaScript 用 JavaScript 编写代码(···)
- Operate a terminal 操作终端(vs code)
- Use git 使用 git(···)
- Understand the basics of how smart contracts work(···)
了解智能合约工作原理的基础知识 - Set up a Coinbase or Metamask wallet(正使用:Metamask)
设置 Coinbase 或 Metamask 钱包
--------------------------------------------
Write code in JavaScript 用 JavaScript 编写代码:
参考:https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics
概念:它可以为网站增加交互性、功能强大的编程语言。
??(“随着经验的增加,您将能够创建游戏、动画 2D 和 3D 图形、全面的数据库驱动应用程序等等!”)
JavaScript 本身相对紧凑,但非常灵活;提供各种工具,解锁大量功能。(本地接口、第三方接口、第三方框架和库)—>具体为:
1.浏览器应用程序编程接口(API)内置于Web浏览器中,提供的功能:动态创建HTML、设置CSS样式,等等;从用户的网络摄像头 “收集和操作视频流” or “生成 3D 图形和音频样本” 。
2.第三方 API,允许开发人员将功能整合到其他内容提供商网站中。(即:功能整合到别的网站上使用)
3.可应用于 HTML 的第三方框架和库,以加速构建站点和应用程序的工作。(就是框架和库的一些应用)
-----------
接下来,根据网站的提示,创建 “Hello world!”的实例:
1.转到测试站点并创建名为 scripts
的新文件夹。在 scripts 文件夹中,创建一个名为 main.js
的新文本文档并保存它。
实际:我们没有测试站点,或者说我们一开始也不知道什么是测试站点,所以先在适当位置(主要D盘)建文件夹scripts,创建main.js文件,
2.在文件中 index.html
,在结束 </body>
标记之前的新行中输入以下代码:
实际:没有index.html文件,新建一个。打开vs code打开这个文件夹(根目录打开),看到index.html,里面什么都没有,直接凭借我的经验,敲ht瞄准html:5回车就可以。然后在下面位置放代码进去。
3.这与CSS <link>
的元素相同。它将 JavaScript 应用于页面,因此它可以对 HTML(以及 CSS 以及页面上的任何其他内容)产生影响。
4.将以下代码添加到 main.js
文件中:
const myHeading = document.querySelector(“h1”);
myHeading.textContent = “Hello world!”;
实际:直接瞄准index.html里面的main.js,按住Ctrl点左键进去,把代码扔进去。
5.确保保存了 HTML 和 JavaScript 文件。然后在浏览器中加载 index.html
。您应该看到相关内容。
实际:原来h1里面的标题文本,被js修饰掉了。
注意:说明(上面)将 <script>
元素放在 HTML 文件底部附近的原因,是浏览器按照 代码在文件中的出现顺序读取代码。如果 JavaScript 首先加载,并且它应该影响尚未加载的 HTML,则可能存在问题。将 JavaScript 放在 HTML 页面底部附近是适应这种依赖关系的一种方法。
原理:/**
** 使用调用 querySelector() 的函数来获取对标题的引用,然后将其存储在名为 myHeading 的变量中。*
** 这类似于我们使用 CSS 选择器所做的。*
** 当您想对元素执行某些操作时,需要先选择它。*
*/
---------------
更好地理解 JavaScript 的工作原理(有关于–所有编程语言的通用功能):
Variable变量,变量是存储值的容器。
行尾的分号表示语句的结束位置(不是一定要用,对于何时应该和不应该使用分号,还有其他规则。)
let myVariable; //首先使用 let 关键字声明一个变量,然后声明您为该变量指定的名称
--------
注意:JavaScript 区分大小写。—>myhome和myHOME不一样。(代码有问题可以注意下区分这个)
-----------
声明变量后,可以为其指定一个值:
let myVariable;
myVariable = "Bob";
或者,可以在同一行上执行这两个操作:
let myVariable = "Bob";
可以通过调用变量名称来检索该值:
let myVariable = "Bob";
myVariable;//这个时候它会和上面一起发亮,或是保持一致的明目显眼的颜色。
为变量赋值后,稍后可以在代码中更改它:
let myVariable = "Bob";
myVariable = "Steve";
变量的数据类型:
String字符串:文本序列,括在单引号或双引号中。let myVariable = “Bob”;
Number数:数字周围没有引号。let myVariable = 10;
Boolean布尔:True/False 值,不需要引号。let myVariable = true;
Array数组:这是一种结构,允许您在单个引用中存储多个值(说白了就是,一个容器可以放很多东西进去的意思)。let myVariable = [1,‘Bob’,‘Steve’,10];myVariable[0]
;myVariable[1]
等等。
Object对象:这可以是任何东西。JavaScript 中的所有内容都是一个对象(字符串、数、布尔、数组、其他的等等等,都可以说是对象),可以存储在变量中。let myVariable = document.querySelector(‘h1’);
注释:
单条:// This is a comment
多条:/*
Everything in between is a comment.
*/
An operator
An operator
是一个数学符号,它基于两个值(或变量)生成结果(有2个或2个以上的值存在时,可以使用它)。
可以在 JavaScript 控制台中尝试。
加: + 符合数学加法,也可以用来“合并”两个字符串。 'Hello ’ + ‘world!’;
减乘除:- * / 完全按数学规律来
分配: = 相当于我给你个啥(或你给我个啥)的意思,一定是右边给左边,let myVariable = ‘Bob’;//把Bob给myVariable
严格平等: ===三个等于号 这将执行测试,以查看两个值是否相等且数据类型是否相同。它返回一个 true
/ false
(布尔值)结果。
不: ! 一个感叹号 返回相反的逻辑值true/false
let myVariable = 3;
!(myVariable === 3);//这个就是false
不相等: !== 当它与相等运算符一起使用时,否定运算符将测试两个值是否不相等
let myVariable = 3;
myVariable !== 3;//这个是false
Conditionals条件
非常常见的形式是 if...else
语句(条件语句是用于测试表达式是否返回 true 的代码结构)
let iceCream = “chocolate”;
if (iceCream === “chocolate”) {
alert(“Yay, I love chocolate ice cream!”);
} else {
alert(“Awwww, but chocolate is my favorite…”);
}
上面这个:如果此比较返回 true
,则运行第一个代码块。如果比较不成立,则改为运行 else
语句后面的第二个代码块。
Functions 功能/函数/方法
函数是一种打包要重用的功能的方式。
可以将代码正文(比如某某标签下的代码、某某文件夹位置的代码)定义为在代码中调用函数名称时执行的函数。
(就是说,某段代码在某个啥啥位置,不想每次使用的时候都写一便一样的拿来用,就用js把那些代码片段整合好扔给它一个新名字,下次用到的时候就直接调用这个新名字,就可以了;这些玩意也被我们叫做函数/方法—因为js里有很多本来就写好了的函数/方法,所以一般我们直接拿来用就可以了;当然自己如果有需求,也可以自己定义代码然后自定义成自己的函数/方法)
这是重复编写相同代码的一个很好的替代方法。
例如:
let myVariable = document.querySelector("h1");alert("hello!");
(这些函数 document.querySelector
和 alert
都内置在浏览器中。)
一般是这样的:functionName() 这种是一个参数的
还有这样的:functionName(,) 这种是多个参数的
还可以定义自己的函数:
例如:简单的两数相乘
function multiply(num1, num2) {
let result = num1 * num2;
return result;
}
multiply(4, 7);//直接调用自定义的函数/方法
multiply(20, 20);
multiply(0.5, 3);
event事件
网站上真正的交互性需要事件处理程序。
这些代码结构用于侦听浏览器中的活动,并运行代码作为响应。
例如:最明显的例子是处理 click 事件,当您用鼠标单击某些内容时,浏览器会触发该事件。要演示这一点,请在控制台中输入以下内容,然后单击当前网页。
document.querySelector(“html”).addEventListener(“click”, function () {
alert(“Ouch! Stop poking me!”);
});
实际:控制台不允许我粘贴内容,直接打开设置,把 XSS攻击(自我跨站脚本攻击)这个勾去掉,再尝试,就可以了。参考:chrome控制台无法粘贴,报Warning: Don’t paste code into the DevTools Console that you don’t underst_console控制台无法粘贴代码-CSDN博客
这个东西只要你不刷新网页,他就会一直存在(点任何地方一次都会弹出这个提示框来)
更高级的实践操作:
操作1:添加图像转化器(学习如何使用 JavaScript 和 DOM API 功能来交替显示两个图像之一。当用户单击显示的图像时,将发生此更改)
1.选择要在示例网站上展示的图片。理想情况下,图像的大小应与之前添加的图像相同,或尽可能接近。
2.将此图像保存在您的 images
文件夹中。
这里的疑问:不知道标签里的alt是干嘛的。
---- 标签的 alt 属性指定了替代文本,用于在图像无法显示或者用户禁用图像显示时,代替图像显示在浏览器中的内容。
3.将映像重命名为firefox2.png。
4.将以下 JavaScript 代码添加到文件中 main.js
。
5.保存所有文件并 index.html
加载到浏览器中。现在,当您单击图像时,它应该更改为另一个图像。
原理(如下图):
操作2:添加个性化欢迎消息(让我们在用户首次访问网站时将页面标题更改为个性化的欢迎消息。此欢迎信息将持续存在。如果用户离开网站并稍后返回,我们将使用 Web Storage API 保存消息。我们还将包括一个更改用户的选项,并且,也有欢迎消息。)
1.在 index.html
中,在 <script>
元素前面添加以下行:
2.在 main.js
中,将以下代码放在文件底部,与所写代码完全相同。这将引用新按钮和标题,并存储每个内部变量。
3.添加以下功能以设置个性化问候语。这不会做任何事情,但这很快就会改变。
prompt()方法:用于弹出可以输入内容的对话框,方法返回用户输入的字串符。
语法格式:
prompt(msg,defaultText);
msg(可选项):需要在对话框中显示的纯文本(不是HTML格式的文本)。
defaultText(可选项):默认的输入文本,可以不写,也可以为空。
textContent 属性表示节点及其后代的文本内容,它会获取所有元素的内容,会返回节点中的每个元素,一般用来做内容的替换操作。
参考:Web APIs - Node.textContent (runebook.dev)
4.在函数声明后添加以下条件块。我们可以调用此初始化代码,因为它在首次加载时构建应用程序。
5.将此 onclick
事件处理程序(如下)放在按钮上。单击后, setUserName()
将运行。这允许用户通过按下按钮输入不同的名称。
到此为止,Javascript的简单前置弄完了。
–后面再继续弄其他的前置。
下一个前置
Use git 使用 git(···)
参考:https://git-scm.com/doc
参考:Git简介-CSDN博客
Git是什么:Git是一款免费、开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目,可以有效、高速的处理从很小到非常大的项目版本管理。
版本控制的作用:可以将某个文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态;可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致怪异问题出现的原因。(Git 就是一个典型的分布式版本控制系统)
分布式版本控制:把代码仓库完整地镜像下来作为本地仓库,可以用这个本地仓库恢复项目以前的版本。(不用联网就可以工作,有分支管理等功能)
Git的工作流程:
- 从远程仓库中克隆 Git 资源作为本地仓库;
- 从本地仓库中checkout代码然后进行代码修改;
- 在提交本地仓库前先将代码提交到暂存区;
- 提交修改,提交到本地仓库;本地仓库中保存修改的各个历史版本;
- 在需要和团队成员共享代码时,可以将修改代码push到远程仓库。
结构图:
Workspace:工作区,就是我们C盘(或D盘)放项目的地方
Index / Stage:暂存区,用于临时存放你的改动
Repository:仓库区(或本地仓库),就是那个 .git 文件夹
Remote:远程仓库(github或gitee上面的东西)
git常用命令:
git安装:
参考:Git 详细安装教程(详解 Git 安装过程的每一个步骤)_git安装-CSDN博客
翻译译文:
Use Git from Git Bash only
This is the most cautious choice as your PATH will not be modified at all. You w only be able to use the Git command line tools from Git Bash.
仅从 Git Bash 使用 Git
这是最谨慎的选择,因为您的 PATH 根本不会被修改。您将只能使用 Git Bash 中的 Git 命令行工具。
Git from the command line and also from 3rd-party software
(Recommended) This option adds only some minimal Git wrappers to your PATH to avoid cluttering your environment with optional Unix tools.
You will be able to use Git from Git Bash, the Command Prompt and the Windov PowerShell as well as any third-party software looking for Git in PATH.
从命令行以及第三方软件进行 Git
(推荐)此选项仅将一些最小的 Git 包装器添加到PATH中,以避免使用可选的 Unix 工具使环境混乱。
您将能够使用 Git Bash 中的 Git,命令提示符和 Windov PowerShell 以及在 PATH 中寻找 Git 的任何第三方软件。
Use Git and optional Unix tools from the Command Prompt
Both Git and the optional Unix tools will be added to your PATH.
Warning: This will override Windows tools like "find"and “sort”. Only use this option if you understand the implications.
使用命令提示符中的 Git 和可选的 Unix 工具
Git 和可选的 Unix 工具都将添加到您的 PATH 中。
警告:这将覆盖 Windows 工具,例如 “find” and “sort”. 仅在了解其含义后使用此选项。
Use Git from Git Bash only
This is the most cautious choice as your PATH will not be modified at all. You w only be able to use the Git command line tools from Git Bash.
仅从 Git Bash 使用 Git
这是最谨慎的选择,因为您的 PATH 根本不会被修改。您将只能使用 Git Bash 中的 Git 命令行工具。
Git from the command line and also from 3rd-party software
(Recommended) This option adds only some minimal Git wrappers to your PATH to avoid cluttering your environment with optional Unix tools.
You will be able to use Git from Git Bash, the Command Prompt and the Windov PowerShell as well as any third-party software looking for Git in PATH.
从命令行以及第三方软件进行 Git
(推荐)此选项仅将一些最小的 Git 包装器添加到PATH中,以避免使用可选的 Unix 工具使环境混乱。
您将能够使用 Git Bash 中的 Git,命令提示符和 Windov PowerShell 以及在 PATH 中寻找 Git 的任何第三方软件。
Use Git and optional Unix tools from the Command Prompt
Both Git and the optional Unix tools will be added to your PATH.
Warning: This will override Windows tools like "find"and “sort”. Only use this option if you understand the implications.
使用命令提示符中的 Git 和可选的 Unix 工具
Git 和可选的 Unix 工具都将添加到您的 PATH 中。
警告:这将覆盖 Windows 工具,例如 “find” and “sort”. 仅在了解其含义后使用此选项。
安装完成后:
C:\Program\Files\dotnet 是什么?(莫名其妙多出来的,之前好像没见过这个):
DOTNET就是.NET, 严格说是:.Net Framework框架 。但为什么叫DOTNET(.NET)呢?在计算机行业DOT是Distributed Object Technology的简称,意为分布式对象技术。DOT恰好与“点”的英语单词相同,DOT的音标[dɔt]。.NET是一个微软开发的编程环境,里面可以使用C#,VB等多种编程语言。
git初始化操作:
参考:Git使用教程,最详细,最傻瓜,最浅显,真正手把手教 - 知乎 (zhihu.com)
git version查看版本信息
git config --global user.name 获取当前登录的用户
git config --global user.email 获取当前登录用户的邮箱
git config --global user.name ‘userName’ 配置用户名
git config --global user.email ‘email’ 配置邮箱
git config –list 获取Git配置信息
创建版本库Repository(就是本地仓库 .git 文件夹)
pwd 用于显示当前的目录
git init 通过命令 git init 把这个目录变成git可以管理的仓库
这样版本库\本地仓库就创建好了。
到这里,关于git的前置就做完了。
继续前置准备。
-----------
- Understand the basics of how smart contracts work(···)
了解智能合约工作原理的基础知识
-------------------
以太坊(简):
它是加密货币(用于以太坊网络上许多事物的加密货币)(加密货币使任何人都可以在全球范围内转账)
它可以运行代码,使人们能够创建应用程序和组织
它是公开的区块链
任何计算机程序都可以在以太坊上运行
------------------------
以太坊(细化):
由世界各地的计算机组成的网络(遵循一套称为以太坊协议的规则)
可以随时随地创建一个以太坊帐户,探索\构建应用程序世界(不需要信任中心化组织)
支持银行服务—仅需联网,即可访问以太坊,并使用上面的借贷和储蓄产品。
任何人都可以在以太坊网络交互\创建应用程序—支持自己管控自己的资产和身份信息(去中心化、去第三方化)
P2P网络—没有中间商,交易有且仅有你和我
不受任何政府或公司的管控—因为它是去中心化的性质\属性
---------------------
区块链(简):
区块链是一个交易数据库。(每次添加一组新交易时,就称其为“区块”,”区块“多了就成了”链“)
允许任何人添加数据,但不能删除数据,保障了它的安全性。
”比如:以太坊这类去中心化区块链具备很高的安全性。”
---------
加密货币(简):
用来描述通过区块链保证安全的多种同质化数字代币
------------
智能合约(简):
**智能合约是存在于以太坊区块链上的计算机程序(**仅在由用户发出的交易触发时执行)。
一旦智能合约发布到以太坊,只要以太坊存在,它就会一直在线并运行。
智能合约是自动化的。
----------------
web3(简):
- Web3 将权力赋予个人而非公司。
- **Web3 是去中心化的:**大部分互联网不是由中心化实体控制和拥有的,而是由构建者和用户分配所有权。
- **Web3 无需许可:**每个人都有参与 Web3 的平等权限,没有人被排除在外。
- **Web3 具有原生支付功能:**它使用加密货币进行线上消费和汇款,而不是依赖传统银行或第三方支付机构过时的基础设施。
- **Web3 去信任:**它通过激励措施和经济机制运转,而不是依赖受信任的第三方。
其他版本:
Web 1.0 主要是由公司拥有的静态网站
Web2.0 互联网公司除了向用户提供内容外,还开始提供平台来共享用户生产的内容,并参与用户间的交互。
---------------------------------------
非同质化代币:
每个非同质化代币都是独一无二的。
每个非同质化代币都有不同的属性(非同质化),并且可证明其稀缺性。
同质化—完全相同的属性,且具有同样的价值。
----------------
智能合约前置完成。
所有前置都完成了。
--------
2.设置环境
建议的扩展程序:
安装nodejs的稳定版20.11.1
安装npm 和 git
到此为止,环境就设置好了。
3.创建新的Hardhat项目
新建文件夹(可以自定义,也可以直接用Hardhat-tutorial),再进入它,使用npm init 初始化npm项目,弹出的信息直接默认回车选择,
然后会在文件夹内,多一个package.json文件,使用dir可以查看文件夹内的信息,
接下来,继续用npm 下载Hardhat,
下载完了,再输入命令初始化一下Hardhat,初始化之后,文件夹内会多一个hardhat.config.js文件,
再继续,下载一个Hardhat的工具,下载好了使用require把它引用到hardhat.config.js这个配置文件里,
-----到此为止,Hardhat新项目的创建就完成了。
4.撰写和编制合同
例子:“我们将创建一个简单的智能合约,实现一个可以转移的代币。代币合约最常用于交换或存储价值。”
需要知道的实现的逻辑:
1.有一个固定的代币总供应量,无法更改。
2.整个供应被分配给部署合约的地址。
3.任何人都可以接收代币。
4.任何拥有至少一个代币的人都可以转移代币。
5.令牌是不可分割的。您可以转移 1、2、3 或 37 个代币,但不能转移 2.5 个代币。
其他:
“您可能听说过 ERC-20,它是以太坊中的代币标准。DAI 和 USDC 等代币实施 ERC-20 标准,允许它们与任何可以处理 ERC-20 代币的软件兼容。为了简单起见,我们将要构建的代币不实现 ERC-20 标准。”
实际操作:
首先创建一个名为 contracts
的新目录,并在名为 Token.sol
的目录中创建一个文件,把提供的sol合约的代码放进去,尝试理解它们的意思。
接下来,使用命令编译它。(可以在dos窗口输入命令,也可以在vs code终端输入命令)
弹出 “Compiled 1 Solidity file successfully (evm target: paris).” 表示合约已成功编译。
------到此为止,第4节的撰写和编制合约完成了。
5.测试合同
以上面的项目为基础,再继续进行test文件夹的创建,其中建同样的Token.js文件,把文档内提供的js代码放进去,在终端里运行 npx hardhat test ,应该得到 “Deployment should assign the total supply of tokens to the owner (3730ms) ”,就表示测试通过了。
接下来,慢慢分析相应的js语句的作用:
const [owner] = await ethers.getSigners();
此处的ethers.getSigners(),表示以太坊账户的对象,它用于将交易发送到合约和其他账户。按理来说,这个[]数组里面存放的是一系列帐户列表,但是我们这里 [owner] 只取了一个来使用。
问题:对于ethers的这个方法,可能会觉得有疑问,没有require它,为什么也可以直接拿来用。
—因为ethers是一个内置在hardhat里面的库,它可以直接拿来用,但是如果我们 “想完全显示的表达” ,也可以用require引入一下它,const { ethers } = require(“hardhat”); 这样就可以更加明显易懂代码的逻辑和结构。
--------------------
const hardhatToken = await ethers.deployContract(“Token”);
这里的ethers.deployContract(“Token”) 表示开始部署我们的代币合约。
这个hardhatToken作为代表此部署合约的对象,–接下来可以用这个对象来进行方法的调用等操作。
---------------------------
const ownerBalance = await hardhatToken.balanceOf(owner.address);
这里使用合约对象 hardhatToken 调用合约方法balanceOf(),–获取所有者帐户的余额。
expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);
这里的totalSupply() 方法表示返回令牌的供应量。
上面的to.equal.()那个 “左边.to.equal(右边)” 表示检查左边是否等于右边。
--------------------------
到此为止,分析了一遍代码,但是不够清晰,
所以问问ChatGPT:
//问题
const { ethers } = require("hardhat");
const { expect } = require("chai");
// 一个流行的 JavaScript 断言库。这些断言函数被称为“匹配器”,可用于测试智能合约。
describe("Token contract", function () {
it("Deployment should assign the total supply of tokens to the owner", async function () {
const [owner] = await ethers.getSigners();
//ethers.getSigners()表示以太坊账户的对象。它用于将交易发送到合约和其他账户。
//在这里,我们得到了我们连接到的节点中的帐户列表;在本例中是 Hardhat Network,我们只保留第一个。
const hardhatToken = await ethers.deployContract("Token");
// ethers.deployContract("Token") 表示开始部署我们的代币合约,
// 这个hardhatToken作为代表此部署合约的对象,--接下来可以用这个对象来进行方法的调用等操作
const ownerBalance = await hardhatToken.balanceOf(owner.address);
//使用合约对象 hardhatToken 调用合约方法balanceOf(),--获取所有者帐户的余额。
expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);
// totalSupply() 返回令牌的供应量
// 左边.to.equal(右边) 表示检查左边是否等于右边
});
});
这段代码我有问题问你:
1.describe是什么,干嘛用的,这里为什么要用到它。
2.it("",async function() {});这个是啥,为什么是用it开始,而不是用其他的,用其他的可以吗?
3.await是什么意思,干嘛用的,这里不使用await可不可以,或者使用其他的啥可不可以?
4.expect是什么意思,它为什么要这样写,这里可不可以写别的什么玩意?
请帮我解答一下。
解答:
1.describe()
是 Mocha 测试框架提供的一个函数,用于组织测试用例。它接受两个参数,
第一个参数是字符串,用于描述测试套件的名称或主题;
第二个参数是一个回调函数,在这个**回调函数内部 “**编写测试用例”。
2.it("", async function() {})
是 Mocha 提供的另一个函数,用于编写单个测试用例。它也接受两个参数,
第一个参数是字符串,用于描述测试用例的名称或主题;
第二个参数是一个回调函数,在这个回调函数内部编写具体的测试逻辑。
it的意思:
it
是 Mocha 的语法规范,用来定义测试用例,其他的诸如 test
等可能在不同的测试框架中有不同的使用方式,但在 Mocha 中,通常使用 it
来定义测试用例
3.await
是用于等待异步操作完成的关键字。
在 JavaScript 中,当你在一个函数中使用 await
关键字时,它会暂停函数的执行,直到 await
后面的异步操作完成并返回结果,然后继续执行函数。
**await—简单说:**它用来做异步操作的,有await的地方先执行,再执行其他的。(有await的地方一定是异步的)
异步和同步:
同步–理解同步就是2个或好几个方法一起执行;
异步–异步就是方法不会一起执行,会有执行的先后顺序。
**如果不使用await:**如果不使用 await
,则可能会在异步操作还未完成时就继续执行下一步操作,导致逻辑错误。
-----
4.expect
是断言库(这里使用的是 Chai 库),用于编写断言来检查代码的行为是否符合预期。
就是说,这里的代码符不符合我们的需求,可以使用export来处理。
支持使用其他的:Chai 提供了多种断言风格,例如 expect
、should
、assert
等,你也可以根据自己的喜好来选择使用哪种风格。
-----------------------
接下来,看第二种需求,
需求:如果需要通过从默认帐户,发送事务(比如:默认账户给账户1转账,接着账户1给账户2转账),来测试代码,则可以使用 Contract 对象上 connect() 的方法将其连接到其他帐户。
实际:这个其实和上面第一种差不多,差别就在于:
第一种是使用默认账户,给现有的某一个账户转账,然后使用expect看—是否转成功了、是否转了这么多。直接用:**合约对象.**balanceOf(用户2.地址, 具体金额)
第二种是使用默认账户,先给账户1转50,然后验证转了没、转对了没;再继续账户1给账户2转50,然后一样的验证。这里比第一种多了个connect(),意思很简单:合约对象.connect(用户1).transfer(用户2.地址, 具体金额)
第三种使用了fixtures-夹具,他可以减少代码复杂性,避免代码重复,提高性能。需求和第二种一样,默认账户转给1,1转给2。但是它更加有逻辑性,它直接写了个方法,然后把 “转账” 、 “验证转账” 用调用方法的方式完成了,而且用的方法还是fixtures夹具的设置方法,保障了高效,还实现了功能。(虽然代码看起来是复杂了点。。。)
此处的疑问:
const {
loadFixture,
} = require("@nomicfoundation/hardhat-toolbox/network-helpers");
const { expect } = require("chai");
describe("Token contract", function () {
async function deployTokenFixture() {
const [owner, addr1, addr2] = await ethers.getSigners();
const hardhatToken = await ethers.deployContract("Token");
// Fixtures can return anything you consider useful for your tests
return { hardhatToken, owner, addr1, addr2 };
}
it("Should assign the total supply of tokens to the owner", async function () {
const { hardhatToken, owner } = await loadFixture(deployTokenFixture);
const ownerBalance = await hardhatToken.balanceOf(owner.address);
expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);
});
it("Should transfer tokens between accounts", async function () {
const { hardhatToken, owner, addr1, addr2 } = await loadFixture(
deployTokenFixture
);
// Transfer 50 tokens from owner to addr1
await expect(
hardhatToken.transfer(addr1.address, 50)
).to.changeTokenBalances(hardhatToken, [owner, addr1], [-50, 50]);
// Transfer 50 tokens from addr1 to addr2
// We use .connect(signer) to send a transaction from another account
await expect(
hardhatToken.connect(addr1).transfer(addr2.address, 50)
).to.changeTokenBalances(hardhatToken, [addr1, addr2], [-50, 50]);
});
});
你看看这个,// Transfer 50 tokens from owner to addr1
await expect(
hardhatToken.transfer(addr1.address, 50)
).to.changeTokenBalances(hardhatToken, [owner, addr1], [-50, 50]);
// Transfer 50 tokens from addr1 to addr2
// We use .connect(signer) to send a transaction from another account
await expect(
hardhatToken.connect(addr1).transfer(addr2.address, 50)
).to.changeTokenBalances(hardhatToken, [addr1, addr2], [-50, 50]);
疑问:
这段为什么给addr1转了之后又使用了to.changeTokenBalances,这个是啥?
为啥要用这个,它发挥了什么作用;
如果不使用to.changeTokenBalances这个,会怎么样?
解答:
to.changeTokenBalances
是一个 Chai 插件提供的断言方法,用于检查在一次交易中代币余额的变化情况。
这个方法用于验证在转移代币时余额的变化是否符合预期。
如果不使用 to.changeTokenBalances
这个断言方法,你就需要手动获取每个地址的余额,并在每次转账后进行比较。(这样就会更麻烦,所以直接用这个方法,就不会这么繁琐)
参考:ChatGPT
-------------------------
关键的地方:hardhatToken.transfer(addr1.address, 50).to.changeTokenBalances(hardhatToken, [owner, addr1], [-50, 50]);
意思是:合约对象.transfer(用户1或账户1.地址,具体多少钱).to.changeTokenBalances(合约对象,[转账者,收钱者],[转出多少钱,收入多少钱])
到此为止,第5章-- “测试合同” 就学完了。
6.使用Hardhat Network进行调试
"在 Hardhat Network 上运行合约和测试时,您可以打印从 Solidity 代码调用 console.log()
的日志记录消息和合约变量。要使用它,您必须导入 hardhat/console.sol
您的合约代码。这是它的样子:import “hardhat/console.sol”; "
这里的问题:
1.“在 Hardhat Network 上运行合约和测试”,我不理解这句话的意思,难道说是要跳转到啥啥网页吗?
2.我没有这个所谓的console.sol,也没有写过这个文件,怎么就可以直接引用来用;
3.而且它给的例子是在.sol文件里/合约文件里导入,但是.sol
文件是用于编写智能合约的代码文件,而 .js
文件是用于编写测试脚本的代码文件;为什么不是放在js文件里呢。
解答:
1.不是跳转网页,是直接在控制台\本地,输入 ‘npx hardhat node’ 就可以启动 Hardhat Network。
//参考ChatGPT:
运行 `npx hardhat node` 命令不会打开网页或跳转到任何网站。这个命令将在你的终端或命令提示符中启动一个本地的以太坊节点,也就是 Hardhat Network。这个节点将在你的计算机上以本地的方式运行,而不是通过网络浏览器访问。
一旦你在终端中运行了这个命令,你将会看到 Hardhat Network 的输出信息,包括一些有关节点运行状态的信息,例如节点的地址、端口等等。这表示 Hardhat Network 已经成功地在你的计算机上启动了,你可以在此节点上部署和测试你的智能合约。
2.这里的 import “hardhat/console.sol”; 的console.sol文件是Hardhat本来就有的、自带的,不需要我们自己写一个这样的文件;
我们只需要知道,把它拿来用就可以了。–(用来打印日志消息)
//参考:ChatGPT
实际上,hardhat/console.sol 文件并不是一个实际存在的 Solidity 文件,而是 Hardhat 提供的一个虚拟文件,用于在 Solidity 合约中模拟 JavaScript 中的 console.log() 函数。所以你不需要手动引入这个文件。
相反,你只需要在你的 Solidity 合约代码中直接调用 console.log() 函数,就像在 JavaScript 中一样,而不需要引入任何文件。这个函数会被 Hardhat 编译器识别并在运行测试时输出日志消息到控制台。
3.要理解一个逻辑:
.sol文件是合约,它就放在那里,我们不去触发它,它就不会有什么作用;
.js文件是javascript的内容,我们用这个js文件来触发合约的编译、部署、实例化、测试、运行等操作。
所以回到我们的问题:"import "hardhat/console.sol"放在.sol文件里而不是.js文件里 。-----------------这里的话 .sol文件代表不了什么,它是一个前置条件,console.sol也是作为功能来说的一个前置条件,我们肯定要用什么东西来让它运行起来—就是js文件,所以是:先定义功能(把"import "hardhat/console.sol"放到.sol里),再去实现它们(再到.js里进行相应操作)。
比较形象化的比喻:
一个物品被我买回来,只要我不去使用它,它对我来说就是无价值的,只有我使用了它,它的花里胡哨的功能对我来说才会有价值。
同样的,.sol不被.js触发运行,那也是无价值的。
接下来,我们在终端输入了 npx hardhat node 启动了Hardhat Network,控制台有一堆东西("8545的端口"和"20个账户+私钥+以太币"组合):
“ Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/ ”
这部分信息表示: Hardhat Network 已经成功地在本地启动,并且监听在 http://127.0.0.1:8545 地址上,这是一个 JSON-RPC 服务器,用于与以太坊客户端进行交互。
文档中警告说,不要向这些账户发送任何真正的以太币,因为它们可能会丢失。(因为这些账户是用于在本地测试环境中进行开发和测试的,私钥公开,被称为 “预置账户”)
这是一组账户信息,
每个账户都有一个地址(以 0x
开头的字符串),一个私钥(以 0x
开头的字符串)和初始的以太币数量(在括号中)
这些账户用于在 Hardhat Network 上进行交易和测试。
我发现有两个命令都可以做合约测试,npx hardhat run和npx hardhat test有什么区别?
- npx hardhat run:
npx hardhat run
命令用于运行任意的 JavaScript 脚本或任务。你可以使用这个命令来运行任何你想要在你的项目中执行的 JavaScript 脚本,不仅仅局限于测试脚本。比如,你可以使用它来部署合约、初始化数据或执行其他与你的智能合约项目相关的任务。- 这个命令的语法是:
npx hardhat run <JavaScript 文件路径> [参数]
。你需要指定要运行的 JavaScript 文件的路径,并且可以选择性地提供一些参数。
- npx hardhat test:
npx hardhat test
命令是用于运行你的测试脚本的命令。测试脚本通常用于编写和运行你的智能合约的测试用例,以验证合约在各种情况下的行为是否符合预期。- 这个命令的语法是:
npx hardhat test [选项]
。你可以在命令中提供一些选项来配置测试的行为,但通常情况下你只需要运行这个命令而不需要提供任何选项。
简单说:npx hardhat run
用于运行任意的 JavaScript 脚本或任务;而 npx hardhat test
专门用于运行测试脚本。
新问题:npx hardhat node输入了之后,进入了hardhat Network终端,然后可以输入npx hardhat text进行合约测试。这个和普通的直接输入npx hardhat text进行合约测试有什么区别?
解答:
1.当你输入 npx hardhat node
启动 Hardhat Network 后,你实际上启动了一个本地的以太坊节点,它会在你的计算机上模拟一个完整的以太坊网络。在这个本地节点上,你可以部署和交互智能合约,而不会影响真实的以太坊网络。
2.一旦 Hardhat Network 运行起来后,你可以使用 npx hardhat test
命令来运行你的测试脚本,以验证你的智能合约的功能是否符合预期。这个命令会连接到 Hardhat Network,并在其上执行你的测试脚本。
3.相比之下,如果你直接使用 npx hardhat test
命令而没有先启动 Hardhat Network,Hardhat 将会在内存中启动一个临时的网络,运行你的测试脚本,然后关闭该网络。这意味着每次测试运行都会重新部署你的合约,并且没有持久化的状态,所以测试可能会比较慢。(直接npx hardhat test进行测试会更慢)
4.区别在于:当你先启动 Hardhat Network 后,你的测试脚本会在一个持久化的本地网络上运行,这样你可以更快地进行测试,并且在多次运行测试时保留合约的部署状态,从而提高了效率。而直接使用 npx hardhat test
命令会在每次测试运行时重新部署合约,测试可能会比较慢。
(所以建议是:先npx hardhat node启动Hardhat Network,再npx hardhat text,测试会更快)
(这个是直接npx hardhat text的情况)
(这个是先npx hardhat node启动Hardhat Network,再npx hardhat text的情况)
明显地,第二种速度更快。
关于npx hardhat node启动Hardhat Network后,终端无法输入内容的情况:
简单点,直接再新开启一个终端,第一个终端里已经开启了Hardhat Network,所以在第二个终端内可以直接npx hardhat text进行合约测试。
7.部署到实时网络
这里涉及到合约部署的问题了。
“在软件级别,部署到测试网与部署到主网相同。唯一的区别是您连接到哪个网络。让我们看看使用 Hardhat Ignition 部署合约的代码会是什么样子。”——它这样说,意思就是要使用Hardhat Ignition 这个玩意来部署合约,先了解下它。
了解Hardhat Ignition :
1.在 Hardhat Ignition 中,部署是通过 Ignition 模块定义的。
2.我们可以把 Ignition 模块视为在概念上类似于 JavaScript 模块。
在 JavaScript 中,创建一个模块来对函数、类和值的定义进行分组,然后导出其中的一些。
在 Hardhat Ignition 中,也可以创建一个模块,在其中对智能合约实例和操作的定义进行分组,并导出其中一些合约。
(就是常规的模块化思维)
参考:https://hardhat.org/ignition/docs/getting-started#overview
创建ignition模块:
在项目根目录中创建一个新目录ignition,然后在 该目录内创建一个名为 modules
的目录。将相关内容粘贴到该目录中的 Token.js
文件中。
问题:要告诉 Hardhat 连接到特定的以太坊网络,您可以在运行任何任务时使用该 --network
参数,命令为“npx hardhat ignition deploy ./ignition/modules/Token.js --network ”,“使用我们当前的配置,在不带 --network
参数的情况下运行它将导致代码针对 Hardhat Network 的嵌入式实例运行。在此方案中,当 Hardhat 完成运行时,部署实际上会丢失,但测试我们的部署代码是否正常工作仍然很有用。”
用了这个不带–networkd命令,完成运行后,部署会丢失,为什么呢?
那既然这样会丢失,为什么要这样使用它。?
解答:
1.使用 --network
参数: 当你运行 Hardhat 来执行部署任务时,你可以通过 --network
参数指定连接到特定的以太坊网络。这个参数告诉 Hardhat 使用指定的网络来执行部署任务
2.不带 --network
参数: 如果你在运行部署任务时不带 --network
参数,Hardhat 将默认连接到内置的 Hardhat Network,这个内置的网络是临时的,当 Hardhat 运行结束后会自动关闭,任何在此网络上的部署都会丢失。
3.为什么要在本地的 Hardhat Network 上运行部署任务呢?这主要是为了在开发和测试过程中能够快速迭代和验证你的部署代码。你可以在本地模拟的网络上进行测试,而不必担心影响到真实的以太坊网络。一旦你的部署代码测试通过,你可以使用 --network
参数指定连接到实际的网络进行真正的部署。(说白了就是,先部署到本地看看有没有问题,没问题再部署到需要的网络上去)
所以本地部署一下,
部署到本地(本地模拟的以太坊网络):
命令:npx hardhat ignition deploy ./ignition/modules/Token.js
看到这里直接部署成功了。
这里部署更快速的方法:
1.使用npx hardhat node 打开Hardhat Network
2.使用ignition进行本地部署 (命令:npx hardhat ignition deploy ./ignition/modules/Token.js)
(也可以直接用命令npx hardhat ignition deploy ./ignition/modules/Token.js进行本地部署,但是速度会慢非常多。)
部署到远程网络:
“要部署到远程网络(如主网或任何测试网),您需要向 hardhat.config.js
文件添加一个 network
条目。在此示例中,我们将使用 Sepolia,但您可以添加任何网络。对于密钥存储,请使用 configuration variables
.”
问题:这里的问题,不确定要不要使用 “它建议的密钥存储的这个玩意”。
参考chatgpt:
- 密钥存储:
- 在你的项目中,你可能需要使用私钥来进行一些操作,比如部署智能合约或发送交易。私钥是非常敏感的信息,泄露可能会导致资金损失或其他安全问题。
- 配置变量:
- 配置变量是一种将敏感信息(比如私钥、API 密钥等)存储在项目配置中的方法。通过将这些敏感信息存储在配置变量中,你可以在不暴露实际值的情况下使用它们。这样可以提高安全性,防止敏感信息被意外泄露。
- vars 配置变量:
- 在 Hardhat 中,你可以使用
vars
对象来访问配置变量。这使得在你的 Hardhat 配置文件中使用配置变量变得更加方便。
- 在 Hardhat 中,你可以使用
就是说,我可以选择性的使用密钥存储的功能,它主要是在保障安全方面,和密钥管理方面,有点作用。
先做配置变量的操作:
先把配置文件的模板放进去(放在hardhat.config.js里面),
然后这个玩意 “INFURA_API_KEY” ,我没有,所以要去Indura官网注册、创建项目、拿到api。
参考:https://www.infura.io/zh
直接选以太坊
慢慢注册,弄好相应信息。
然后,看到了这玩意,
点进去,然后是这个长长的一串,
这个就是我的Infura的私钥,把它拿过来,放在刚才代码的位置
然后,直接在终端输入,npx hardhat vars set INFURA_API_KEY,回车后,提示输入我的Infura密钥,就可以了,再跟着文档往下做就可以了。
参考:https://hardhat.org/hardhat-runner/docs/guides/configuration-variables
const SEPOLIA_PRIVATE_KEY = vars.get(“SEPOLIA_PRIVATE_KEY”);里面的SEPOLIA_PRIVATE_KEY是metamask钱包的私钥,全部配置完了即可。