Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于hardhat-deploy插件使用 #2

Open
tangminjie opened this issue Jun 15, 2022 · 0 comments
Open

关于hardhat-deploy插件使用 #2

tangminjie opened this issue Jun 15, 2022 · 0 comments

Comments

@tangminjie
Copy link
Member

hardhat-deploy可以将合约部署到任何网络,并且跟踪并且复制相同的环境进行测试。

  1. hardhat-deploy scripts 允许在deploy文件夹中写入部署脚本表且导出。
  2. 通过调用deployments.fixture(['MyContract']) 保证多个测试使用相同的合约并且只部署一次。
  3. hardhat-deploy 支持多链设置,可以方便的部署到任何网络。
hardhat-deploy源码:https://github.com/wighawag/tutorial-hardhat-deploy

使用hardhat-deploy插件

环境要求:

  1. node版本>= 12.0 ,
  2. 安装hardhat开发环境
  3. 安装hardhat-deploy插件:yarn add -D hardhat-deploy hardhat-deploy-ethers

配置文件

  1. hardhat-deploy 提供了hardhat-deploy-ethers插件来代替"@nomiclabs/hardhat-ethers"来和以太坊进行交互, 但是在hardhat工程中很多库都强依赖于"@nomiclabs/hardhat-ethers" 所以在package中我们可以修改下载源来解决兼容问题:
    "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers",
  2. 在hardhat.config.js中添加配置:
    ``` // Named accounts for plugin hardhat-deploy
    namedAccounts: {
    deployer: 0, // deployer 将是用于部署合约的账户
    tokenOwner: 1, //tokenOwner,这可能是传递给合约构造函数的另一个账户,
    play1: 2,
    },

// Rewrite the ./test folder to ./tests
paths: {
tests: './tests', //测试脚本路径
sources: 'contracts', //合约路径
},```

部署合约脚本

部署脚本默认路径在deploy目录下,例:

import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {DeployFunction} from 'hardhat-deploy/types';

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
  const {deployments, getNamedAccounts} = hre;
  const {deploy} = deployments;

  const {deployer, tokenOwner} = await getNamedAccounts();

  await deploy('Token', {
    from: deployer,
    args: [tokenOwner],
    log: true,
  });
};
export default func;
func.tags = ['Token'];

hardhat-deploy可以将hardhat运行环境作为参数传入:hre: HardhatRuntimeEnvironment),并且提供了deploymegetNamedAccounts

  1. deployment 作为部署工厂包含了 deploy方法,用来实现合约的部署。
  2. getNamedAccounts 可以获取到hardhat.config中配置的用户地址。 const {deployer, tokenOwner} = await getNamedAccounts();
  3. from:部署者的地址 args:合约构造函数参数,以JSON数组形式传入。log开始gas等信息的打印。

当在终端运行 hardhat deploy时,应该有一下输出:

Nothing to compile
deploying "Token" (tx: 0x259d19f33819ec8d3bd994f82912aec6af1a18ec5d74303cfb28d793a10ff683)...: deployed at 0x5FbDB2315678afecb367f032d93F642f64180aa3 with 592983 gas
Done in 3.66s.

测试合约

测试脚本默认在test目录下,例:

import {expect} from 'chai';
import {ethers, deployments, getNamedAccounts} from 'hardhat';

describe("Token contract", function() {
  it("Deployment should assign the total supply of tokens to the owner", async function() {
    await deployments.fixture(["Token"]);
    const {deployer,tokenOwner} = await getNamedAccounts();
    const accounts = await getUnnamedAccounts();
    const Token = await ethers.getContract("Token");
    const ownerBalance = await Token.balanceOf(tokenOwner);
    const supply = await Token.totalSupply();
    expect(ownerBalance).to.equal(supply);
    const accountsBalance = await Token.connct(await ethers.getSigner(accounts[0])).balanceOf(tokenOwner);

  });
});
  1. await deployments.fixture(["Token"]); 还记得你之前写的deploy脚本,这一行允许在测试前执行。 它还会自动生成一个evm_snapshot,所以如果你写了很多测试,并且它们都指向那个fixture,那么在背后就不会一次次重复部署。 而是恢复到以前的状态,自动加快你的测试速度。
  2. const {deployer,tokenOwner} = await getNamedAccounts(); 这样你就可以访问tokenOwner的地址,也就是部署脚本中使用的地址。
  3. const accounts = await getUnnamedAccounts(); 得到测试用的签名者的地址。
  4. const Token = await ethers.getContract("Token");获得可调用的合约,hardhat-deploy会通过名称自动匹配编译合约,前提是deploy中需要有部署脚本,
  5. const accountsBalance = await Token.connct(await ethers.getSigner(accounts[0])).balanceOf(tokenOwner); 将合约关联到一个签名者,可以改变合约的调用者地址。或者我们可以在getcontract时候传入调用者的地址,来使用不同账户进行测试, const TokenAsOwner = await ethers.getContract("Token", tokenOwner);

全面覆盖测试

在测试脚本中我们需要大量的测试账号,可以创建一个utils脚本来对账号进行管理:

import {Contract} from 'ethers';
import {ethers} from 'hardhat';

export async function setupUsers<T extends {[contractName: string]: Contract}>(
  addresses: string[],
  contracts: T
): Promise<({address: string} & T)[]> {
  const users: ({address: string} & T)[] = [];
  for (const address of addresses) {
    users.push(await setupUser(address, contracts));
  }
  return users;
}

export async function setupUser<T extends {[contractName: string]: Contract}>(
  address: string,
  contracts: T
): Promise<{address: string} & T> {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const user: any = {address};
  for (const key of Object.keys(contracts)) {
    user[key] = contracts[key].connect(await ethers.getSigner(address));
  }
  return user as {address: string} & T;
}

通过utils 可以方便的创建账号,让测试简洁和容易阅读。

不同网络部署

Hardhat内置了Hardhat网络,这是一个专为开发设计的本地以太坊网络。 它允许你部署合约,运行测试和调试代码。 这是Hardhat连接到的默认网络,所以你不需要设置任何东西就可以工作,只需要简单运行测试。
并且hardhat提供了console.log() 可以在合约中输出打印信息,方便测试。

真实的以太坊网络分为主网(mainnet),测试网(testnet),以太坊有多个测试网: Ropsten、Kovan、Rinkeby和Goerli。
在软件层面,部署到testnet和部署到mainnet是一样的。 唯一不同的是连接的网络。
由于我们使用了hardhat-deploy插件,并且我们已经编写了部署脚本,现在只需要对部署到的网络进行一些配置,就可以部署到真实网络中。

部署到真实网络:

要部署到远程网络,如mainnet或任何testnet,你需要在你的hardhat.config.js文件中添加一个network条目。并且在部署时指定网络:yarn hardhat --network <network-name> deploy

为了更方便地处理私钥和网络配置,在项目的根部创建了一个新的文件夹utils 来对网络进行管理:

  1. 创建 network.ts,包含以下内容:
import 'dotenv/config';
export function node_url(networkName: string): string {
  if (networkName) {
    const uri = process.env['ETH_NODE_URI_' + networkName.toUpperCase()];
    if (uri && uri !== '') {
      return uri;
    }
  }

  let uri = process.env.ETH_NODE_URI;
  if (uri) {
    uri = uri.replace('{{networkName}}', networkName);
  }
  if (!uri || uri === '') {
    if (networkName === 'localhost') {
      return 'http://localhost:8545';
    }
    return '';
  }
  if (uri.indexOf('{{') >= 0) {
    throw new Error(
      `invalid uri or network not supported by node provider : ${uri}`
    );
  }
  return uri;
}

export function getMnemonic(networkName?: string): string {
  if (networkName) {
    const mnemonic = process.env['MNEMONIC_' + networkName.toUpperCase()];
    if (mnemonic && mnemonic !== '') {
      return mnemonic;
    }
  }

  const mnemonic = process.env.MNEMONIC;
  if (!mnemonic || mnemonic === '') {
    return 'test test test test test test test test test test test junk';
  }
  return mnemonic;
}

export function accounts(networkName?: string): {mnemonic: string} {
  return {mnemonic: getMnemonic(networkName)};
}
  1. 在hardhat.config中增加网络配置:
  networks: {
   rinkeby: {
     url: node_url('rinkeby'),
     accounts: accounts('rinkeby'),
   },
 },
  1. 设置utils/networks.ts自动读取的环境变量.env 来保存密钥和节点私钥:
ETH_NODE_URI_RINKEBY=https://eth-rinkeby.alchemyapi.io/v2/<alchmey api key>
MNEMONIC_RINKEBY=<mnemonic for rinkeby>

也可以通过命令行来指定私钥:
yarn hardhat --network rinkeby etherscan-verify --api-key <api-key>

部署完成后会看到地址abi文件在artifacts文件夹中
Nothing to compile deploying "Token" (tx: 0xb40879c3162e6a924cfadfc1027c4629dd57ee4ba08a5f8af575be1c751cd515)...: deployed at 0x8bDFEf5f67685725BC0eD9f54f20A2A4d3FEDA98 with 475842 gas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant