• 十分钟学会dapp开发之hardhat搭建


    blockchain-hardhat-demo
    使用hardhat进行智能合约开发

    首先安装nodejs开发环境(已安装的可直接略过这个环节)
    如下使用是linux版本安装,window系统自行网上查找,很简单。

    wget https://nodejs.org/dist/v16.13.0/node-v16.13.0-linux-x64.tar.xz

    1.解压源码

    xz -d node-v16.13.0-linux-x64.tar.xz 
    tar xvf node-v16.13.0-linux-x64.tar
    
    • 1
    • 2

    2.重命名

    mv node-v16.13.0-linux-x64 nodejs
    
    • 1

    3.移动到/usr/local目录下

    mv nodejs /usr/local
    
    • 1

    4.加入到环境变量

    vim /etc/profile
    export NODE_HOME=/usr/local/nodejs
    export PATH=$PATH:$NODE_HOME/bin
    export NODE_PATH=$NODE_HOME/lib/node_modules
    
    • 1
    • 2
    • 3
    • 4

    5.重载资源

    source /etc/profile
    
    • 1

    6.检查npm版本

    npm -v
    
    • 1

    7.检查npx版本

    npx -v
    
    • 1

    8.检查node版本

    node -v
    
    • 1

    9.全局安装nrm包(镜像源管理器)

    npm install -g nrm
    
    • 1

    10.查看已有镜像源

    nrm ls
    
    • 1

    11.切换到淘宝镜像源

    nrm use taobao
    
    • 1

    12.全局安装yarn包管理器

    npm install -g yarn
    
    • 1

    13.npm全局安装包所在位置

    ls /usr/local/nodejs/lib/node_modules
    
    • 1

    14.创建demo项目

    mkdir demo
    cd demo
    npm init #一路回车确认生成package.json文件
    
    • 1
    • 2
    • 3

    15.使用yarn命令安装hardhat项目包(这里使用局部安装)

    yarn add hardhat
    
    • 1

    16.使用yarn命令安装openzeppelin库(商用级别的智能合约开源库)有了这个库之后,通过import命令导入合约库,在合约库之上构建DAPP应用

    yarn add @openzeppelin/contracts 
    yarn add @openzeppelin/hardhat-upgrades
    
    • 1
    • 2

    17.使用yarn命令安装hardhat插件

    $ yarn add -D @nomiclabs/hardhat-ethers 
    $ yarn add -D @nomiclabs/hardhat-waffle
    
    • 1
    • 2

    18.安装用于测试的组件(用于编写智能合约测试用例)

    yarn add -D chai
    
    • 1

    20.安装ethers(与区块链交互的完整组件库、代码少、接口简单、和web3先比推荐使用ethers)

    yarn add -D ethers
    
    • 1

    21.安装ethereum-waffle(由于一些潜在的原因,如果yarn命令安装不成功使用可以使用npm命令安装)

    npm install --save-dev ethereum-waffle
    
    • 1

    22.以上都安装成功后,就可以编写智能合约了
    通过执行npx hardhat --help 查看hardhat操作命令

    npx hardhat --help
    
    • 1
    全局选项
    --config              一个安全帽配置文件.
    --emoji               在信息中使用表情符号.
    --help                显示此消息,如果提供了任务名称,则显示任务的帮助
    --max-memory          安全帽可以使用的最大内存量
    --network             要连接到的网络.
    --show-stack-traces   显示堆栈跟踪.
    --tsconfig            保留的安全帽参数.
    --verbose             支持安全帽详细日志记录
    --version             版本显示安全帽的版本
    可用任务
    check         检查你需要的任何东西
    clean         清除缓存并删除所有工件
    compile       编译编译整个项目,构建所有工件
    console       控制台打开安全帽控制台
    flatten       展平并打印合同及其依赖项
    help          帮助
    node          在Hardhat网络上启动JSON-RPC服务器
    run           在编译项目后运行用户定义的脚本
    test          运行摩卡测试
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    23.执行npx hardhat命令(选择Create an empty hardhat.config.js创建一个空白配置文件)

    npx hardhat
    Welcome to Hardhat v2.6.8
    
    ? What do you want to do? …
    Create a basic sample project
    Create an advanced sample project
    Create an advanced sample project that uses TypeScript
    ▸ Create an empty hardhat.config.js
    Quit
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    24.打开配置文件修改如下配置

    //hardhat项目依赖组件
    require("@nomiclabs/hardhat-waffle");
    require('@openzeppelin/hardhat-upgrades');
    
    //hardhat项目配置项
    module.exports = {
      solidity: "0.8.4", //使用的sodity库的版本
      networks: {
        local: {
          url: 'http://127.0.0.1:8545', //本地RPC地址
          //本地区块链账户地址(需要启动运行npx hardhat node命令开启本地开发环境的区块链)
          //这些账户地址和秘钥每次重启区块链都是相同的,并且数据会重置
          accounts: [
            // 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 (第一个账户地址及秘钥)
            '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
            // 0x70997970c51812dc3a010c7d01b50e0d17dc79c8 (第二个账户地址及秘钥)
            '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d',
            // 0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc (三个账户地址及秘钥)
            '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a',
            // 0x90f79bf6eb2c4f870365e785982e1f101e93b906 (第四个个账户地址及秘钥)
            '0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6',
            // 0x15d34aaf54267db7d7c367839aaf71a00a2c6a65 (第五个账户地址及秘钥)
            '0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a',
          ]
        }
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    25.在demo目录下创建合contracts约目录用于编写合约

    mkdir contracts
    
    • 1

    26.在demo目录下创建test目录用于编写测试用例

    mkdir test
    
    • 1

    27.在demo目录下创建scripts目录用于编写部署脚本

    mkdir scripts
    
    • 1

    28.在contracts目录编写创建Demo.sol合约文件

    touch Demo.sol
    $ vim Demo.sol
    
    • 1
    • 2

    29.编辑合约 Demo.sol 先编写一个最简单的合约内容

    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0 <0.9.0;
    
    contract Demo {
    uint256  public score = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    30.在scripts目录编写合约部署文件demo-deploy.js

    $ touch demo-deploy.js
    $ vim demo-deploy.js
    
    //hardhat库使用ethers组件与区块链进行交互
    const { ethers } = require("hardhat");
    
    //部署主函数
    async function main() {
    const Demo = await ethers.getContractFactory("Demo");
    const demo = await Demo.deploy()
    console.log(demo.address)
    }
    
    //执行部署
    main().then(() => process.exit(0)).catch(error => {
    console.error(error);
    process.exit(1);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    31.部署合约(开发模式1)
    始终返回合约地址 0x5FbDB2315678afecb367f032d93F642f64180aa3

    npx hardhat run scripts/demo-deploy.js
    
    • 1

    32.部署合约(开发模式2) 通过参数–network部署到指定的网络每次返回全新的合约地址 0x…部署本地网络前需要启本地节点

    npx hardhat run scripts/demo-deploy.js --network local
    
    • 1

    至此简单的合约已经通过scripts/demo-deploy.js进行部署

    如果是部署到主网 只需对hardhat.config.js进行主网的连接配置即可

    然后通过 npx hardhat run scripts/demo-deploy.js --network [主网配置]

    与Demo合约交互进行测试
    1.首先在test目录创建demo.test.js

    touch demo.test.js
    $ vim demo.test.js
    
    const { expect } = require('chai') //断言模块
    const { ethers} = require('hardhat') //安全帽模块
    
    describe('Demo合约测试', () => {
    
    /**
    
    * 测试执行前的钩子函数
      */
      before(async function () {
      //获取合约工厂对象
      this.Demo = await ethers.getContractFactory('Demo')
    
    
      //通过合约工厂部署合约
      this.demo = await this.Demo.deploy()
    
    })
    
    /**
    
    * 获取score状态测试
      */
      it('测试score等于0', async function () {
        const score = await this.demo.score()
        expect(score.toString()).to.be.equal('0')
      })
    
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    执行测试

    npx hardhat test test/demo.test.js --network local
    
    • 1

    至此一个简单的合约编写、部署、测试的流程就完成了,但这仅仅是个开始
    现在我们的合约 仅仅只有读取score的功能,现在我们要对合约进行功能的添加
    在未部署可升级合约之前 我们先用不可升级合约演示功能的添加

    接下来现在我们再来一个demo,就是添加一个setScore方法,这个方法可以改变score的值

    // contracts/D.sol
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0 <0.9.0;
    
    //Demo合约
    contract Demo {
      uint256  public score = 0;
      //修改score的状态
      function setScore(uint256 newScore) public returns(bool) {
        score = newScore;
        return true;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    继续编写测试用例 针对新添加的setScore合约方法添加测试用例

    const { expect } = require('chai') //断言模块
    const { ethers} = require('hardhat') //安全帽模块
    
    describe('Demo合约测试', () => {
    
    /**
    
    * 测试执行前的钩子函数
      */
      before(async () => {
      //获取合约工厂对象
      this.Demo = await ethers.getContractFactory('Demo')
    
      //通过合约工厂部署合约
      this.demo = await this.Demo.deploy()
    
    });
    
    /**
    
    * 获取score状态测试
      */
      it('测试score等于0', async () => {
        const score = await this.demo.score()
        expect(score.toString()).to.be.equal('0')
      })
    
    /**
    
    * 修改score状态测试
      */
      it('修改score等于100', async () => {
        await this.demo.setScore(100)
        const score = await this.demo.score()
        expect(score.toString()).to.be.equal('100')
      })
    
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    执行新的测试

    npx hardhat test test/demo.test.js --network local
    
    • 1

    现在我们可以通过编写Demo.sol文件进行合约的开发

    通过demo.test.js文件进行测试用例的编写进行合约功能验证 由于在测试的过程中会自动编译合约,因此也无需执行npx hardhat compile编译操作

    编写可升级的智能合约
    在scripts目录创建demo-upgradeable-deploy.js

    cd scripts
    $ touch demo-upgradeable-deploy.js
    $ vim demo-upgradeable-deploy.js
    
    • 1
    • 2
    • 3

    //安全帽模块
    const { ethers, upgrades } = require(“hardhat”)

    //主函数

    async function main() {
      const Demo = await ethers.getContractFactory('Demo')
      console.log('Deploying Demo...')
      const demo = await upgrades.deployProxy(Demo, [300], { initializer:  setScore' })
      await demo.deployed()
      console.log('Demo to:', demo.address)
    }
    
    //执行可升级合约部署
    main().then(() => process.exit(0)).catch(error => {
      console.error(error)
      process.exit(1)
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    执行可升级合约部署

    npx hardhat run scripts/demo-upgradeable-deploy.js --network local
    Deploying Demo...
    Demo to: 0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1
    
    • 1
    • 2
    • 3

    部署完成后 我们需要保存我们的合约地址,这个地址以后就是面向用户的合约交互地址
    每次升级合约都需要这个地址作为代理合约地址与新升级的合约进行绑定
    开始编写升级合约DemoV2.sol 对升级的合约增加了increment方法 每次调用score分值加1

    $ cd contracts
    $ touch DemoV2.sol
    $ vim DemoV2.sol
    
    // contracts/DemoV2.sol
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0 <0.9.0;
    
    //Demo合约
    contract DemoV2 {
      uint256  public score;
    
    //修改score的状态
    function setScore(uint256 newScore) public {
      score = newScore;
    }
    
    //分值加1
    function increment() public {
      score = score + 1;
    }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    开始编写部署升级合约的文件

    $ cd scripts
    $ touch demo-upgrade.js
    $ vim demo-upgrade.js
    
    //安全帽模块
    const { ethers, upgrades  } = require("hardhat");
    
    //主函数
    async function main() {
      const upgradeContractName = 'DemoV2' //升级合约的名称
      const proxyContractAddress = '0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1' //代理合约的名称
      const DemoUpgrade = await ethers.getContractFactory(upgradeContractName)
      console.log('Upgrading Demo...')
      await upgrades.upgradeProxy(proxyContractAddress, DemoUpgrade)
      console.log('Demo upgraded')
    }
    
    //升级合约
    main().then(() => process.exit(0)).catch(error => {
      console.error(error)
      process.exit(1)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    执行合约升级

    npx hardhat run scripts/demo-upgrade.js --network local
    
    • 1

    通过hardhat控制台验证升级合约

    npx hardhat console --network local
    
    • 1

    针对升级合约编写测试用例

    $ cd test
    $ touch demo-v2.test.js
    $ vim demo-v2.test.js
    
    //demo-v2.test.js
    const { expect } = require('chai') //断言模块 
    const { ethers, upgrades} = require('hardhat') //安全帽模块
    
    describe('DemoV2升级合约测试', () => {
    
      /**
       * 测试执行前的钩子函数
       */
      before(async () => { 
        //从工厂获取合约
        const DemoContract = await ethers.getContractFactory('Demo')
        //部署可升级代理合约
        const demoProxy = await upgrades.deployProxy(DemoContract, [300], { initializer: 'setScore' })
        //部署
        this.demo = await demoProxy.deployed()
      });
    
      /**
       * 获取score状态测试
       */
      it('测试score等于300', async () => {
        const score = await this.demo.score() //读取score 
        expect(score.toString()).to.be.equal('300') //断言结果为300
      })
    
      /**
       * 修改score状态测试
       */
      it('修改score等于100', async () => {
        await this.demo.setScore(100) //设置score为100
        const score = await this.demo.score() //读取score  
        expect(score.toString()).to.be.equal('100') //断言结果为100
      })
    
      /**
       * 计数器加1测试
       */
      it('increment计数器测试', async () => {
        const upgradeContractName = 'DemoV2' //升级合约的名称
        const proxyContractAddress = this.demo.address //代理合约的名称
        const DemoUpgrade = await ethers.getContractFactory(upgradeContractName) //工厂合约
        const demoV2 = await upgrades.upgradeProxy(proxyContractAddress, DemoUpgrade) //升级合约
        await this.demo.setScore(800) //设置score为800
        await demoV2.increment() //计数器+1
        const newScore = (await this.demo.score()).toString() //获取存储的新值
        expect(newScore).to.be.equal('801') //断言结果为801
      })
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    注意事项

    可升级合约 不能包含construct构造函数,构造函数的实现方式是通过调用普通函数实现的,因此针对普通函数 可以通过权限控制调用机制比如增加普通函数只允许合约部署者调用

  • 相关阅读:
    浏览器的选择建议,按照这些建议选,总能找到合适的
    #gStore-weekly | SPARQL 执行过程中高级查询函数的求值(上)
    十年诉讼,迈瑞真的赢了吗?
    leetcode-627. 变更性别
    Avue使用本地/网络数据字典
    数据结构-二叉树·堆(顺序结构的实现)
    【Linux C | 网络编程】入门知识:UDP协议、一个最简单的UDP客户端、一个最简单的UDP服务端
    【D3.js】1.17-给 D3 元素添加标签
    Nginx配置参数详解一篇到底
    cocos creater 鸿蒙 音频卡死 播放失败 不回调
  • 原文地址:https://blog.csdn.net/leaning_java/article/details/126341338