当前位置:   article > 正文

了解区块链技术(Patrick Collins)(六)_your project is an esm project (you have "type": "

your project is an esm project (you have "type": "module" set in your packag

我们必须建立属于自己的测试基础设施,使用hardhat框架

hardhat是最容易的,也是目前智能合约开发中最受欢迎的框架之一,被价值数十亿美元的大规模协议使用,

是一种开发环境,允许基于JavaScript的开发,提供了很多工具,扩展性强,可调试

文件夹:hardhat-simple-storage-fcc

官网地址:hardhat.org

根据官网的教程操作:

https://hardhat.org/tutorial/testing-contracts

大概步骤:
yarn init
yarn add --dev hardhat
yarn add --dev @nomicfoundation/hardhat-toolbox @nomicfoundation/hardhat-network-helpers @nomicfoundation/hardhat-chai-matchers @nomicfoundation/hardhat-ethers @nomicfoundation/hardhat-verify chai ethers hardhat-gas-reporter solidity-coverage @typechain/hardhat typechain @typechain/ethers-v6
yarn hardhat 
选择:Create a JavaScript project

1、粘贴simpleStorage.sol,编译:yarn hardhat compile

错误:

 Your project is an ESM project (you have "type": "module" set in your package.json) but your Hardhat config file uses the .js extension.

解决方式:

将chai降级为:"chai": "^4.3.7" 依赖项依赖于ES模块
yarn add chai@^4.3.7

node_moudules有些是以@开始的,有些不是:

清楚的明白哪些包是官方的,哪些不是

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.19; //稳定的版本,MIT是限制最少的License
​
    contract SimpleStorage {
        uint256 public favoriteNum;
        People[] public people; //部署后的输入框表示想要获取到的数组中元素的索引
        struct People {
            uint256 favoriteNum;
            string name;
        }
​
        function addPerson1(string memory _name, uint256 _favoriteNum) public {
            people.push(People(_favoriteNum, _name));
        }
​
        function addPerson2(string memory _name, uint256 _favoriteNum) public {
            People memory newPerson = People({
                favoriteNum: _favoriteNum,
                name: _name
            });
            people.push(newPerson);
        }
​
        function store(uint256 _favoriteNum) public virtual {
            favoriteNum = _favoriteNum;
        }
​
        function retrieve() public view returns (uint256) {
            return favoriteNum;
        }
    }
​

yarn hardhat compile

// 之前
// const {ethers} = require("ethers") hardhat无法知道不同的代码中有不同的合约工厂,还没动,等我多用几次
// hardhat-ethers是一个将ethers内置进了hardhat的一个封装包
// 允许在不同脚本和其他情况下追踪不同的部署
const { ethers } = require("hardhat");
async function main() {
   // 如果使用ethers,无法知道SimpleStorage.sol已经编译好放在artifacts里面
   // hardhat知道contracts文件夹的存在的,也知道他已经被编译好了 
  const SimpleStorageFactory = await ethers.getContractFactory(
    "SimpleStorage"
  );
  console.log("deploy ....");
  // 会直接部署到本地的hardhat节点上
  const simpleStorage = await SimpleStorageFactory.deploy();
  await simpleStorage.waitForDeployment();
  console.log(simpleStorage.runner.address);
  console.log(simpleStorage);
}
​
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.log(error);
    process.exit(1);
  });
yarn hardhat run scripts/deploy.js 

其中target表示合约的部署地址

HardhatEthersSigner 是一个对象,表示与以太坊网络交互的签名者。它包含以下属性:
_gasLimit: 一个整数,表示交易的 gas 限制。在这个例子中,它的值为 30000000。
address: 一个字符串,表示签名者的地址。在这个例子中,它的值为 '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'。
provider: 一个 HardhatEthersProvider 对象,表示与以太坊网络交互的提供者。它包含以下属性:
_hardhatProvider: 一个 LazyInitializationProviderAdapter 对象,表示与 Hardhat 框架交互的适配器。
_networkName: 一个字符串,表示网络的名称。在这个例子中,它的值为 'hardhat'。
_blockListeners: 一个空数组,表示监听区块事件的监听器。
_transactionHashListeners: 一个空 Map 对象,表示监听交易哈希事件的监听器。
_eventListeners: 一个空数组,表示监听事件的监听器。

小技巧:

使用ctrl + P调出搜索框,输入>调出命令面板,不输入>搜索项目中文件

在写js的时候分号很麻烦,我们可以添加prettiersolidity prettier插件

yarn add prettier preitter-plugin-solidity

创建.prettierrc

{
    "tabWidth": 4 ,
    "useTabs": false,
    "semi": false,
    "singleQuote": false    
}

hardhat内置了一个hardhat network,是一个专门为开发设计的以太坊节点

打开hardhat.config.js,添加有关默认网络的信息,虚拟的hardhat network会自动提供RPC URL和私钥,

require("@nomicfoundation/hardhat-toolbox");
​
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  defaultNetwork: "hardhat",
    
  solidity: "0.8.19", 
};
// 有了网络标识后,在不同链,不同私钥之间的切换就会非常的容易

创建.env添加环境变量

输入我们的SEPOLIA_RPC_URLPRIVATE_KEY

我们将从环境变量中获取:

调用dotenv yarn add --dev dotenv,添加require("dotenv").config()启动配置

const SEPOLIA_RPC_URL = process.env.SEPOLIA_RPC_URL;
const PRIVATE_KEY = process.env.PRIVATE_KEY;

每一个EVM基础网络都有一个新的chainIDchainlist.org Sepolia 58008

修改后:

require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config()
/** @type import('hardhat/config').HardhatUserConfig */
​
const SEPOLIA_RPC_URL = process.env.SEPOLIA_RPC_URL;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
module.exports = {
  defaultNetwork: "hardhat",
  networks: {
    sepolia: {
      /**需要告诉hardhhat如何连接到Sepolia网络 */
      url: SEPOLIA_RPC_URL,
      account: [PRIVATE_KEY],
      chainId: 11155111,
    },
  },
  solidity: "0.8.19",
};

结果:

结果:https://sepolia.otterscan.io/address/0x6ee82245c4a9D9Ad4A673Aa42aa4bb1e5224E4cC

编程方式验证合约:

VAWNN-MJAVB-QTBUT-A4SHJ

https://sepolia.etherscan.io/address/0x6ee82245c4a9D9Ad4A673Aa42aa4bb1e5224E4cC

合约没有被验证,回到部署脚本改进使得合约在不受后可以自动验证:

etherscan.io和大多数区块链浏览器,它们的网站上有一个API文档,是我们通过编程和etherscan进行交互和操作的方式,通过API验证我们的合约

yarn add --dev @nomicfoundation/hardhat-verify

API key:相当于允许我们使用etherscan API的密码

注册登录etherscan.io,点击API KEYS,选择添加,APP name设置成HH-FCC,复制key

require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();
require("@nomicfoundation/hardhat-verify")
/** @type import('hardhat/config').HardhatUserConfig */
​
const SEPOLIA_RPC_URL = process.env.SEPOLIA_RPC_URL;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY;
module.exports = {
  defaultNetwork: "hardhat",
  networks: {
    sepolia: {
      url: SEPOLIA_RPC_URL,
      accounts: [PRIVATE_KEY],
      chainId: 11155111,
    },
  },
  solidity: "0.8.19",
  etherscan: {
    apiKey: ETHERSCAN_API_KEY,
  }
};
// 之前
// const {ethers} = require("ethers")
// hardhat-ethers是一个将ethers内置进了hardhat的一个封装包
const { ethers, run } = require("hardhat");
async function main() {
  const SimpleStorageFactory = await ethers.getContractFactory(
    "SimpleStorage"
  );
  console.log("deploy ....");
  const simpleStorage = await SimpleStorageFactory.deploy();
  await simpleStorage.waitForDeployment();
  console.log(simpleStorage.runner.address);
  console.log(simpleStorage);
}
// 合约地址以及合约需要的一些参数,构造函数
async function vertify(contractAddress, args) {
  console.log("Verify contract ....");
  await run("verity");
}
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.log(error);
    process.exit(1);
  });
const { ethers, run, network } = require("hardhat");
console.log(network.config);
执行: yarn hardhat run scripts/deploy.js

结果:显示了我们所在网络hardhat的大量信息

通过chainID确定测试网络和实时网络

  console.log(network.config);
  if (network.config.chainId === 11155111 && process.env.ETHERSCAN_API_KEY) {
    //等待
    await simpleStorage.deploymentTransaction().wait(6);
    await vertify(simpleStorage.target, [])
  }
}

部署到测试网络验证报错:

https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify

使用命令行也是:

自定义hardhat任务

创建一个Tasks文件夹,创建block-numbers.js

const { task } = require("hardhat/config");
//添加进config
task("block-number", "print the current block number").setAction(
    // 设置我们这个函数想要做的事情
    async (taskArgs, hre) => {
        const blockNumber = await hre.ethers.provider.getBlockNumber();
        console.log(`current block number:${blockNumber}`);
    }
)
module.exports = {};

添加进config

require("./tasks/block-numbers");

每次脚本运行都会重置blockNumber

使用测试网络:

hardhat本地节点

yarn hardhat node

在本地网络上启动一个节点,和Ganache一样

Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/

节点并没有运行于hardhat network

可以在开一个终端,运行yarn hardhat run scripts/deploy.js,本地节点并没有发生变化

仍然在使用hardhat 运行环境,只不过不是默认的hardhat network,这个节点会在脚本执行结束之后仍然存在

 networks: {
    sepolia: {
      url: SEPOLIA_RPC_URL,
      accounts: [PRIVATE_KEY],
      chainId: 11155111,
    },
    locahost: {
      url: "http://127.0.0.1:8545/",
      chainId: 31337,
    }
  },

添加后,新开的终端输入yarn hardhat run scripts/deploy.js --network locahost,就会发现另一个终端有日志输出了

hardhat控制台:

hardhat控制台是一个JavaScript环境,用于运行JavaScript命令和区块链网络进行交互

yarn hardhat console --network localhost

不需要导入任何东西,因为所有的东西都已经在控制台中自动导入了

在控制台上直接输入deploy.js上的js命令

按两下ctrl + c退出

运行测试:

hardhat的最大优点是非常适用于运行测试,我们所有的代码都将对所有人开源供其交互,并有可能被利用

rekt.news介绍了大量黑客攻击事件以及他们是如何攻击的,以及智能合约中发生了什么才会导致这些攻击的发生

重命名为test-deploy.js

hardhat测试使用的是Mocha框架,是一个基于JavaScript的框架用于运行我们的测试

支持现代编程语言进行测试的观点是:会有更大的灵活性对智能合约进行更多的测试和交互(大多数项目)

支持使用solidity进行测试的观点是:测试用的代码和编写智能合约用的代码相同

describe是一个关键字,hardhat的Mocha框架会识别,有两个参数,第一个接收的是字符串,第二个接收的是函数(通用的是匿名函数)

// 编写一个基本的测试
const { ethers } = require("hardhat");
const { assert, expect } = require("chai");
describe("SimpleStorage", () => {
  let simpleStorageFactory, simpleStorage;
  //告诉我们在每一个each之前要做什么,一个,每个测试之前,都会部署beforeEach
  beforeEach(async function () {
    simpleStorageFactory = await hre.ethers.getContractFactory("SimpleStorage");
    simpleStorage = await simpleStorageFactory.deploy();
  });
  // 多个,运行测试代码的地方
  it("Should start a favorite number of 0", async function () {
    const currentValue = await simpleStorage.retrieve();
    const expextValue = "0";
    // assert
    // expect,它们都会从chai包中导进来
    assert.equal(currentValue.toString(),expextValue);
  });
 // 调用store的时候,我们希望favoriteNum更新
  it("Should update when we call store", async function () {
    const expextValue = "7";
    const transactionResponse = await simpleStorage.store(expextValue);
    await transactionResponse.wait(1);
​
    const currentValue = await simpleStorage.retrieve();
    assert.equal(expextValue, currentValue.toString());
  })
})

yarn hardhat test

函数实际上要消耗的Gas:

hardhat-gas-report

yarn add hardhat-gas-reporter --dev

在config中添加新的分支:

require("hardhat-gas-reporter")
gasReporter: {
    //测试时运行
    enabled: true
  },

运行yarn hardhat test,会自动运行gasReporter

结果:

改进:

登录coinmarketcap.com/api/,复制我们的密钥

  gasReporter: {
    //测试时运行
    enabled: true,
    outputFile: "gas-report.txt",
    noColors: true,
    currency: "CNY",//每个函数的成本
    // 为了获取current,需要获取一个私钥
    coinmarketcap: COINMARKETCAP_API_KEY
  },

环境变量的改进:

使变量始终处于被填充的状态

const SEPOLIA_RPC_URL = process.env.SEPOLIA_RPC_URL || "https://eth-rinkeby";
const PRIVATE_KEY = process.env.PRIVATE_KEY || "0xkey";
const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY || "key";
const COINMARKETCAP_API_KEY = process.env.COINMARKETCAP_API_KEY || "key";

Solidity-coverage通过遍历我们的测试,计算我们有多少代码被测试实际覆盖

yarn add --dev solidity-coverage,添加到config中

运行yarn hardhat coverage

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/69085
推荐阅读
相关标签
  

闽ICP备14008679号