用Hardhat开发智能合约,并发布ERC20 Token
January 31st, 2023

1. 安装Hardhat

npm install --save-dev hardhat

2. 初始化Hardhat工程

npx hardhat

3. 安装官方contract库

npm install @openzeppelin/contracts

4. 完成Token智能合约代码

目录contracts/DrawToken.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract DrawToken is ERC20, Ownable {
    constructor() ERC20("DrawToken", "DTK") {}

    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
}

5. 完成合约部署代码

目录scripts/deploy.ts

import { ethers } from "hardhat";

async function main() {
  // DrawToken
  const DrawToken = await ethers.getContractFactory("DrawToken");
  const drawToken = await DrawToken.deploy();
  await drawToken.deployed();
  console.log(`drawToken deployed to ${drawToken.address}`);
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

6. 启动本地私有链

npx hardhat node

7. 部署合约

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

得到合约地址

drawToken deployed to 0x5FbDB2315678afecb367f032d93F642f64180aa3

8. 启动nodejs,并初始化环境

# node
Welcome to Node.js v16.15.1.
Type ".help" for more information.
> const { ethers } = require("ethers");
undefined
> const provider = new ethers.providers.JsonRpcProvider();
undefined
> account1 = new ethers.Wallet('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',provider)
Wallet {
  _isSigner: true,
  _signingKey: [Function (anonymous)],
  _mnemonic: [Function (anonymous)],
  address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  provider: JsonRpcProvider {
    _isProvider: true,
    _events: [],
    _emitted: { block: -2 },
    disableCcipRead: false,
    formatter: Formatter { formats: [Object] },
    anyNetwork: false,
    _networkPromise: Promise {
      [Object],
      [Symbol(async_id_symbol)]: 47,
      [Symbol(trigger_async_id_symbol)]: 5,
      [Symbol(destroyed)]: [Object]
    },
    _maxInternalBlockNumber: -1024,
    _lastBlockNumber: -2,
    _maxFilterBlockRange: 10,
    _pollingInterval: 4000,
    _fastQueryDate: 0,
    connection: { url: 'http://localhost:8545' },
    _nextId: 43,
    _eventLoopCache: { detectNetwork: null, eth_chainId: null },
    _network: { chainId: 31337, name: 'unknown' }
  }
}

9. 初始化contract

> abi = [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]
...
> contract = new ethers.Contract('0x5FbDB2315678afecb367f032d93F642f64180aa3',abi,account1)
...

调用合约基本方法

> await contract.name()
'DrawToken'
> await contract.symbol()
'DTK'

10. mint出token

> contractWithSigner = contract.connect(account1)
...
> contractWithSigner.mint(await account1.getAddress(),100)
{
  type: 2,
  chainId: 31337,
  nonce: 2,
  maxPriorityFeePerGas: BigNumber { _hex: '0x59682f00', _isBigNumber: true },
  maxFeePerGas: BigNumber { _hex: '0xb5f20e1e', _isBigNumber: true },
  gasPrice: null,
  gasLimit: BigNumber { _hex: '0x0116e2', _isBigNumber: true },
  to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
  value: BigNumber { _hex: '0x00', _isBigNumber: true },
  data: '0x40c10f19000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000064',
  accessList: [],
  hash: '0x3e0a661dc1656fc7d3be8033f2f6fd4ceec8b05ac328c37fb1769d813a1c8524',
  v: 1,
  r: '0xff8f32375fcc5fb3d31634a5b604c8bc4d975b1b694611a4323609d990f944b0',
  s: '0x14a5b08e8afd9686c1c89962f490a82836dd8aa95cd6c9adcb611596cbc46c91',
  from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  confirmations: 0,
  wait: [Function (anonymous)]
}

查询是否mint成功

> balance = await contractWithSigner.balanceOf(await account1.getAddress())
BigNumber { _hex: '0x64', _isBigNumber: true }
> balance.toString()
'100'

11. 发送Token

> account2 = new ethers.Wallet('0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d',provider)
...
> contractWithSigner.transfer(await account2.getAddress(), 50)
{
  type: 2,
  chainId: 31337,
  nonce: 3,
  maxPriorityFeePerGas: BigNumber { _hex: '0x59682f00', _isBigNumber: true },
  maxFeePerGas: BigNumber { _hex: '0xac5e7c24', _isBigNumber: true },
  gasPrice: null,
  gasLimit: BigNumber { _hex: '0xcc41', _isBigNumber: true },
  to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
  value: BigNumber { _hex: '0x00', _isBigNumber: true },
  data: '0xa9059cbb00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000000000000000000032',
  accessList: [],
  hash: '0xe0d69fe8dbffbb507090d6bc62c4f9127d88b9f747c466fd5a8df8cd5e60690c',
  v: 0,
  r: '0xed8ebd334cf1996a9e3021866aca03c50bf98ffbbbd4dccb15352baabc0c0b02',
  s: '0x706acb4a6f768c095d3ac56f51dd2c4502da8236f52c39137891250ddab5de8c',
  from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  confirmations: 0,
  wait: [Function (anonymous)]
}

查看Token是否发送成功

> (await contractWithSigner.balanceOf(await account2.getAddress())).toString()
'50'
> (await contractWithSigner.balanceOf(await account1.getAddress())).toString()
'50'
Subscribe to zhongxuqi
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.
More from zhongxuqi

Skeleton

Skeleton

Skeleton