介绍

在本文中,我们将创建一个简单的智能合约并对其进行测试,然后使用 Hardhat 将其部署在测试网上.

如果你对区块链比较陌生,不用担心。 首先,在逐步编写智能合约之前,我们将回顾 Solidity 和 Hardhat 的一些基础知识。 在本教程结束时,您应该能够使用 Solidity 和 Hardhat 重新创建托管智能合约。 让我们开始吧!

什么是 Solidity?

智能合约是一个简单的程序,它按照作者设定的预定义规则在区块链上执行交易。以太坊的智能合约使用特定的编程语言 Solidity。 Solidity 是一种面向对象的编程语言,专为在以太坊虚拟机 (EVM) 上运行智能合约而构建,其语法类似于其他编程语言 C++、Python 和 JavaScript。

Solidity 将您的智能合约编译成一系列字节码,然后再将其部署到以太坊虚拟机中。每个智能合约都有它的地址。要调用特定函数,您需要一个应用程序二进制接口 (ABI) 来指定要执行的函数并返回您期望的格式。

创建智能合约需要一个用于在测试网上测试和部署合约的开发环境。有很多替代品可供选择,例如 Truffle 及其 Ganache 套件或 Remix、Solidity IDE。但是还有第三种选择,Hardhat

什么事 Hardhat

Hardhat 是一个使用 Node.js 构建的 Solidity 开发环境。 它于 2019 年首次发布 Beta 版,此后一直在增长。 使用 Hardhat,开发人员无需离开 JavaScript 和 Node.js 环境即可开发智能合约,就像使用 Truffle 一样。

测试使用 Hardhat 构建的智能合约也很容易,因为 Hardhat 具有即插即用的环境,并且不需要您设置个人以太坊网络来测试您的智能合约。 要连接到智能合约,您可以使用 Ethers.js,并且要测试它们,您可以使用著名的 JavaScript 测试库,例如 Chai

安装 Hardhat

首先安装 nodejs 环境

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.39.1/install.sh | bash
nvm install 16
nvm use 16
nvm alias default 16
npm install npm --global # Upgrade npm to the latest version

其次创建一个 hardhat 项目

mkdir hardhat-tutorial
cd hardhat-tutorial
npm init --yes
npm install --save-dev hardhat

编写你的第一个 Solidity 智能合约

在此示例中,您将创建一个简单的智能合约,类似于 Tornado Cash。 每个执行智能合约的用户都会向智能合约存入一定数量的代币,智能合约会返回一个哈希值。 您可以使用哈希将代币提取到不同的帐户中。

//SPDX-License-Identifier: UNLICENSED// Solidity files have to start with this pragma.
// It will be used by the Solidity compiler to validate its version.
pragma solidity ^0.8.0;// We import this library to be able to use console.log
import "hardhat/console.sol";// This is the main building block for smart contracts.
contract Token {// Some string type variables to identify the token.string public name = "My Hardhat Token";string public symbol = "MHT";// The fixed amount of tokens stored in an unsigned integer type variable.uint256 public totalSupply = 1000000;// An address type variable is used to store ethereum accounts.address public owner;// A mapping is a key/value map. Here we store each account balance.mapping(address => uint256) balances;/*** Contract initialization.** The `constructor` is executed only once when the contract is created.* The `public` modifier makes a function callable from outside the contract.*/constructor() {// The totalSupply is assigned to transaction sender, which is the account// that is deploying the contract.balances[msg.sender] = totalSupply;owner = msg.sender;}/*** A function to transfer tokens.** The `external` modifier makes a function *only* callable from outside* the contract.*/function transfer(address to, uint256 amount) external {// Check if the transaction sender has enough tokens.// If `require`'s first argument evaluates to `false` then the// transaction will revert.require(balances[msg.sender] >= amount, "Not enough tokens");// We can print messages and values using console.logconsole.log("Transferring from %s to %s %s tokens",msg.sender,to,amount);// Transfer the amount.balances[msg.sender] -= amount;balances[to] += amount;}/*** Read only function to retrieve the token balance of a given account.** The `view` modifier indicates that it doesn't modify the contract's* state, which allows us to call it without executing a transaction.*/function balanceOf(address account) external view returns (uint256) {return balances[account];}
}

使用 Hardhat 测试智能合约

使用 Hardhat 的最大优势之一是测试套件非常简单。 如果您已经熟悉 JavaScript 测试,您可以快速适应 Hardhat 的测试,特别是如果您经常使用 Chai

// This is an example test file. Hardhat will run every *.js file in `test/`,
// so feel free to add new ones.// Hardhat tests are normally written with Mocha and Chai.// We import Chai to use its asserting functions here.
const { expect } = require("chai");
const hre = require("hardhat");// `describe` is a Mocha function that allows you to organize your tests. It's
// not actually needed, but having your tests organized makes debugging them
// easier. All Mocha functions are available in the global scope.// `describe` receives the name of a section of your test suite, and a callback.
// The callback must define the tests of that section. This callback can't be
// an async function.
describe("Token contract", function () {// Mocha has four functions that let you hook into the the test runner's// lifecycle. These are: `before`, `beforeEach`, `after`, `afterEach`.// They're very useful to setup the environment for tests, and to clean it// up after they run.// A common pattern is to declare some variables, and assign them in the// `before` and `beforeEach` callbacks.let Token;let hardhatToken;let owner;let address1;let address2;let address;// `beforeEach` will run before each test, re-deploying the contract every// time. It receives a callback, which can be async.beforeEach(async function () {// Get the ContractFactory and Signers here.Token = await hre.ethers.getContractFactory("Token");[owner, address1, address2, ...address] = await hre.ethers.getSigners();// To deploy our contract, we just have to call Token.deploy() and await// for it to be deployed(), which happens onces its transaction has been// mined.hardhatToken = await Token.deploy();// We can interact with the contract by calling `hardhatToken.method()`await hardhatToken.deployed();});// You can nest describe calls to create subsections.describe("Deployment", function () {// `it` is another Mocha function. This is the one you use to define your// tests. It receives the test name, and a callback function.// If the callback function is async, Mocha will `await` it.it("Should set the right owner", async function () {// Expect receives a value, and wraps it in an assertion objet. These// objects have a lot of utility methods to assert values.// This test expects the owner variable stored in the contract to be equal// to our Signer's owner.expect(await hardhatToken.owner()).to.equal(owner.address);});it("Should assign the total supply of tokens to the owner", async function () {const ownerBalance = await hardhatToken.balanceOf(owner.address);expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);});});describe("Transactions", function () {it("Should transfer tokens between accounts", async function () {// Transfer 50 tokens from owner to address1await hardhatToken.transfer(address1.address, 50);const address1Balance = await hardhatToken.balanceOf(address1.address);expect(address1Balance).to.equal(50);// Transfer 50 tokens from address1 to address2// We use .connect(signer) to send a transaction from another accountawait hardhatToken.connect(address1).transfer(address2.address, 50);const address2Balance = await hardhatToken.balanceOf(address2.address);expect(address2Balance).to.equal(50);});it("Should fail if sender doesn’t have enough tokens", async function () {const initialOwnerBalance = await hardhatToken.balanceOf(owner.address);// Try to send 1 token from address1 (0 tokens) to owner (1000 tokens).// `require` will evaluate false and revert the transaction.await expect(hardhatToken.connect(address1).transfer(owner.address, 1)).to.be.revertedWith("Not enough tokens");// Owner balance shouldn't have changed.expect(await hardhatToken.balanceOf(owner.address)).to.equal(initialOwnerBalance);});it("Should update balances after transfers", async function () {const initialOwnerBalance = await hardhatToken.balanceOf(owner.address);// Transfer 100 tokens from owner to address1.await hardhatToken.transfer(address1.address, 100);// Transfer another 50 tokens from owner to address2.await hardhatToken.transfer(address2.address, 50);// Check balances.const finalOwnerBalance = await hardhatToken.balanceOf(owner.address);expect(finalOwnerBalance).to.equal(initialOwnerBalance - 150);const address1Balance = await hardhatToken.balanceOf(address1.address);expect(address1Balance).to.equal(100);const address2Balance = await hardhatToken.balanceOf(address2.address);expect(address2Balance).to.equal(50);});});
});

智能合约部署到测试网络

// This is a script for deploying your contracts. You can adapt it to deploy
// yours, or create new ones.const hre = require("hardhat");async function main() {// This is just a convenience checkif (hre.network.name === "hardhat") {console.warn("You are trying to deploy a contract to the Hardhat Network, which" +"gets automatically created and destroyed every time. Use the Hardhat" +" option '--network localhost'");}// ethers is available in the global scopeconst [deployer] = await hre.ethers.getSigners();console.log("Deploying the contracts with the account:",await deployer.getAddress());console.log("Account balance:", (await deployer.getBalance()).toString());const Token = await hre.ethers.getContractFactory("Token");const token = await Token.deploy();await token.deployed();console.log("Token address:", token.address);// We also save the contract's artifacts and address in the frontend directorysaveFrontendFiles(token);
}function saveFrontendFiles(token) {const fs = require("fs");const contractsDir = __dirname + "/../frontend/src/contracts";if (!fs.existsSync(contractsDir)) {fs.mkdirSync(contractsDir);}fs.writeFileSync(contractsDir + "/contract-address.json",JSON.stringify({ Token: token.address }, undefined, 2));const TokenArtifact = hre.artifacts.readArtifactSync("Token");fs.writeFileSync(contractsDir + "/Token.json",JSON.stringify(TokenArtifact, null, 2));
}main().then(() => process.exit(0)).catch((error) => {console.error(error);process.exit(1);});

使用 Hardhat 运行本地以太坊网络

npx hardhat node

上面的命令将在端口 8545 上本地启动一个新的 Ethereum RPC 服务器。您可以创建一个前端应用程序并使用 Metamask 连接到您的本地 RPC 服务器。

部署智能合约

在本地或像 Rinkeby 这样的测试网上部署您的智能合约非常相似。 您可以使用 --network 标志定义要将智能合约部署到哪个网络。 如果要部署到本地网络,命令如下:

npx hardhat run scripts/deploy.js --network localhost

怎样使用 Hardhat 开发 Solidity 智能合约相关推荐

  1. Solidity智能合约开发 — 1-以太坊开发工具和部署

    Solidity简介 solidity 是为实现智能合约而创建的一个高阶编程语言.也是以太坊虚拟机(EVM)智能合约的语言. Solidity开发工具remix remix是以太坊官方推荐的在线开发工 ...

  2. Solidity智能合约库:区块链工程师的随身工具箱

    编者荐语: Solidity使用起来不如其他语言那般丝滑顺畅?安全事故难以避免?社区迎来适用于FISCO BCOS的Solidity智能合约库,轻松破解合约的各种小难题,让合约开发更加快速高效.省时省 ...

  3. 微众银行Solidity智能合约库:区块链工程师的随身工具箱

    区块链技术在经历了十余年的发展后,渐呈"燎原之势",不断在各行业落地生根.但同时,从技术的角度看,区块链应用开发仍然有着较高的门槛,存在不少痛点.为了提升应用开发各环节的用户体验, ...

  4. 区块链应用开发(智能合约的开发和WeBASE合约IDE的使用)

    文章目录 四.智能合约的开发和WeBASE合约IDE的使用 一.实验概述 二.实验目标 三.实验环境及建议 四.实验步骤 4.1 启动Webase 4.2 智能合约开发 4.2.1 合约功能设计 4. ...

  5. web3j用于solidity智能合约maven插件

    web3j maven插件用于基于solidity智能合约文件创建java类. 用法 插件的基本配置将从src/main/resources获取solidity文件,并将java类生成到src/mai ...

  6. 微众银行《Solidity智能合约库》区块链工程师的随身工具箱之初体验相当的nice

    文章目录 一.智能合约库简介 二.痛点及解决方式 痛点一:计算可能溢出 痛点二:转换不够便捷 痛点三:数组操作不够丰富 痛点四:不提供字符串内置操作 痛点五:高级数据结构不完备 总结 一.智能合约库简 ...

  7. 蚂蚁区块链第12课 如何使用命令行编译工具solcjs编译Solidity智能合约?

    1,摘要 蚂蚁区块链合约平台支持 Solidity 智能合约,针对合约源代码的编译,可以直接通过蚂蚁区块链 Cloud IDE 合约开发环境进行合约编译.部署.测试和调试. 本文介绍由蚂蚁区块链平台提 ...

  8. c++ eos智能合约开发_十分钟教你开发EOS智能合约

    EOS环境搭建和启动节点 下面从EOS入门的环境搭建.编译运行一个智能合约.发送一些Aigsen,给大家做一些展示,希望能让非技术人员也有一些收获. 首先下载EOS环境搭建和启动节点.这一步其实还是比 ...

  9. 十分钟教你开发EOS智能合约

    十分钟教你开发EOS智能合约 在CSDN.柏链道捷(PDJ Education).HelloEOS.中关村区块链产业联盟主办的「EOS入门及最新技术解读」专场沙龙上,柏链道捷(PDJ Educatio ...

  10. 以太坊solidity智能合约-生成随机数

    Solidity随机数生成 在以太坊的只能合约中,没有提供像其他面向对象编程一样的生成随机数的工具类或方法.其实,所谓的随机数也是伪随机的,没有哪一种语言能够真正的生成随机数. 对于solidity来 ...

最新文章

  1. 8分频verilog线_Verilog设计分频器(面试必看)
  2. 第十三章、创建接口和定义抽象类
  3. 怎样让友情链接更加有效果
  4. java多线程流式写入文件夹_java多线程写入同一文件
  5. h5大转盘 php,HTML5 canvas实现中奖转盘的实例代码
  6. 我有一个域名_一个域名可以绑定几个网站?域名解析多少子域名?
  7. 微型计算机中外储存器比内储存器,计算机笔试复习题集共23页.doc
  8. TextView滚动功能的实现
  9. 怎么用c语言解三元二次方程组,三元二次方程组解法
  10. Android Mvc 模式
  11. PPT/Word中英文单词换行问题 (取消了西文在单词中间换行的选项,但英文部分依然不连续) 的解决方法
  12. 锚点的使用(链接到网页中某个位置)
  13. 说说亲身经历的草根seo的辛酸
  14. elasticSearch Analysis Token Filters作用及相关样例
  15. java 凸包,确定凸包上的点—Graham扫描法—java实现
  16. 【算法基础】堆排序——O(nlogn)
  17. 换根dp(板子整理)
  18. 计算机毕业设计Node.js+Vue房地产销售系统(程序+源码+LW+部署)
  19. vue如何将图片压缩成webp格式并用webpack打包
  20. An Optimistic Perspective on Offline Reinforcement Learning(ICML2020)

热门文章

  1. 电影《海贼王:红发歌姬》观后感
  2. 修改android屏幕分辨率
  3. php 遍历文件夹并压成zip_将文件夹压缩成zip文件的php代码
  4. PCB电路板EN 45545-2:2020最新标准的测试要求
  5. mysql查询历史时刻数据_跨平台实时数据库查询历史数据的方法介绍
  6. c语言算摄氏温度和绝对温度,将华氏度转换为绝对温度C语言
  7. xss靶场练习之xss.haozi.me解析及答案
  8. 西湖大学教授怎么看AI制药革命?|量子位智库圆桌实录
  9. 0和1在计算机电路中,0和1
  10. cs系统如何上云服务器,cs架构程序怎么连接云服务器