November 24, 2021

Deploy and test ICO and ERC20 of Moonbeam smart contract using hardhat

This document is a continuation of the documentation for working with the Moonbeam, check out the first article for installing the local Moonbeam Development Node and hardhat components. In this article, we will tell you how to deploy and test ICO and ERC20 smart contracts Moonbeam using a local node, using a hardhat

1. Go to the contacts folder (which should be located on the hardhatdev/contracts path) and check what is there:

cd contracts
Checking what is in the folder
| ll 
If there are unnecessary files there, delete them so that they do not interfere with you.
In our case, the file "Greeter.sol" is located there, let's delete it
| rm -rf Greeter.sol
And once again check what is in the contract folder:
| ll
Checking the hardhat-dev/contract folder

2. We create an ERC20 smart contract, here, for example, we use the import of one of the standard ERC20 libraries.

install the openzeppelin library :

npm install @openzeppelin/contracts
Output of successful openzeppelin installation

Create an ERC20 contract script

nano contracts/MyTokenERC20.sol

An example of the script we used:

pragma solidity ^0.8.1;

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

contract MyTokenERC20 is ERC20 {
  /**
   * @dev Constructor that gives msg.sender all of exis>
   */
  constructor(
      string memory name,
      string memory symbol,
      uint256 initialSupply
  ) ERC20(name, symbol) {
      _mint(msg.sender, initialSupply);
  }
}

!help We check that the version of solidity used in the contract matches the version used in the config file:

nano hardhat.config.js
require("@nomiclabs/hardhat-waffle");

// This is a sample Hardhat task. To learn how to creat>
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (>
  const accounts = await hre.ethers.getSigners();

  for (const account of accounts) {
    console.log(account.address);
  }
});

// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
const { privateKey } = require('./secrets.json');



module.exports = {
  solidity: "0.8.1",

  networks: {
    dev: {
      url: "http://127.0.0.1:9933",
      chainId: 1281,
      accounts: [privateKey]
    }
  }
};

3. We create an ICO smart contract:

nano contracts/ICO.sol

An example of the script we used:

pragma solidity ^0.8.1;


import "./MyTokenERC20.sol";

contract ICO {

  uint public buyPrice;
  MyTokenERC20 public token;

  constructor(MyTokenERC20 _token, uint _buyPrice) {
    token = _token;
    buyPrice = _buyPrice;
  }

  function buy() external payable returns (uint) {
    uint tokens = _buy(msg.sender , msg.value);

    return tokens;
  }



  function _buy(address _sender , uint256 _amount) inte>
    uint tokens = _amount / buyPrice;
    token.transfer(_sender , tokens);
    return tokens;
  }
}

!help This contract has a buy function - we will use the buy function to buy tokens. Also, the ICO smart contract has a constructor that accepts the address of the ERC20 token and the token price, the buy function sends tokens to the one who sent the ether in accordance with the specified price.

4. Now we need to change the deploy script that we used in the previous dock:

nano scripts/deploy.js

This is how our deployment script looks like for the current project:

async function main() {
    // Weget the contract to deploy
    const MyTokenERC20 = await ethers.getContractFactory('MyTokenERC20');
    console.log('Deploying MyTokenERC20...');
    const totalSupply = '1000000';

    // Instantiating a new ERC20 smart contract
    const myTokenERC20 = await MyTokenERC20.deploy('DoubleTop', 'DoubleTop', totalSupply + '0000000000000000000');

    // Waiting for the deployent to resolve 
    await myTokenERC20.deployed();
    console.log('MyTokenERC20 deployed to:', myTokenERC20.address);

    // We get the contract to deploy
    const ICO = await ethers.getContractFactory('ICO');
    console.log('Deploying ICO...');

    // Instantiating a new ICO smart contract
    const ico = await ICO.deploy(myTokenERC20.address, 1000);

    // Waiting for the deployment to resolve
    await ico.deployed();
    console.log('ICO deployed to:', ico.address);
}


main()
   .then(() => process.exit(0))
   .catch((error) => {
      console.error(error);
      process.exit(1);
   });

!Replace the name "DoubleTop" on line 8 with the name of your token!

!help Script designations. For your convenience, we will analyze what each paragraph in the script means (the number is the paragraph number):

1) Initialization of ERC20 contract

2) Deploy ERC20 contract

3) Expectations until the ERC20 contract is deployed

4) Initialization of ICO smart contract

5) Deploy ICO smart contract

6) Waiting for the ICO of a smart contract to be completed

5. Now let's create a script file for the test:

cd test 
Checking what is in the folder
| ll 
If there are unnecessary files there, delete them so that they do not interfere with you.
In our case, the file "sample-test.js" is located there, let's delete it
| rm -rf sample-test.js
And once again check what is in the contract folder:
| ll

create a new test file ICO.js

nano ICO.js

This is how our test script looks like :

const { expect } = require('chai');

describe('ICO contract', () => {
  let TokenContract, tokenContract, ICO, ico, owner, addr1, addr2, addr3
  const totalSupply = '1000000';
  const price = '10';

  beforeEach(async () => {
    TokenContract = await ethers.getContractFactory('MyTokenERC20');
    tokenContract = await TokenContract.deploy('MyTokenERC20', 'MTE', totalSupply + '00000000000000000');
    await tokenContract.deployed();

   ICO = await ethers.getContractFactory('ICO');
   ico = await ICO.deploy(tokenContract.address, price);
   await ico.deployed();

   [owner, addr1, addr2, addr3] = await ethers.getSigners();
   await tokenContract.transfer(ico.address, '2000000' + '00000000000000000');
 });


  describe('Transfer token', () => {

    it('Owner balance', async () => {
     const ownerBalance = await tokenContract.balanceOf(owner.address);
     expect(ownerBalance.toString()).to.equal('800000' + '000000000000000000');
   });


   it('Buy token', async () => {
     await ico.connect(addr1).buy({ value: ethers.utills.parseEther('1') });

     const adde1Balance = await tokenContract.balanceOf(addr1.address);
     expect(addr1Balance.toString()).to.equal(ethers.utils.parseEther('0.1').toString());
   });

  });
});

!help In the first test (lines 24-26), we take the owner's address and check that the balance is correct. In the second test (lines 30-34), we call the token buy function and check that the buy function worked correctly.

6. Let's compile a smart contract:

npx hardhat compile
Successful compilation output

7. Let's run the test and deploy smart contracts:

npx hardhat test 
npx hardhat run scripts/deploy.js --network dev
Conclusion of a successful deployment

8. Now let's connect to the metamask and test and verify the functionality of our contracts. To do this, we need the metamask extension, the developer account that we used
(you can check the developer accounts here).

We used Geralt's account for our projects :

Public Address: 0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b
Private Key: 0x99b3c12287537e38c90a9219d4cb074a89a16e9cdb20bf85728ebd97c343e342

We import the private key, connect to the Moonbeam Dev network (how to connect metamask to the moonbeam networks) and make sure that we have Dev tokens on our balance and that you created in the smart contract. In our case, "DoubleTop".

Balances are displayed and everything works correctly

9. Let's make a couple of test transfers between metamask wallets in order to make sure that everything works correctly:

Let's send the Dev tokens and the tokens we created to the second wallet

Sending DoubleTop Tokens

We check from the second wallet that the tokens have arrived:

Tokens came to another wallet

We send the remaining created tokens to the address of the smart contract

We sent tokens to the smart contract address

10. Finally, we check the function of buying tokens in the ICO smart contract. For this we use the Remix development environment:

Connecting to the metamask on the remix platform

Connected metamask to the remix platform

Go to the Workspace tab and create a new workspace:

Created a working environment Moonbeam ICO contract test

Create 2 files "ICO.sol" and "MyTokenERC20.sol". We copy our contracts to the created files, respectively:

file ICO.sol
file MyTokenERC20.sol

Let's move on to compiling the ICO.sol and MyTokenERC20.sol files that we created:

compile the file MyTokenERC20.sol
compile the ICO.sol file

Now we are trying to buy our tokens using an ICO smart contract deployed in the remix development environment:

Initializing the purchase of tokens
We look in the history of the token purchase transaction
Our tokens are bought and on the balance

Useful links :

https://razumv.tech/smart-contract_Moonbeam

https://docs.moonbeam.network/builders/get-started/moonbeam-dev/

https://hardhat.org/tutorial/

https://remix.ethereum.org/

https://docs.moonbeam.network/tokens/connect/metamask/#connect-metamask-to-moonbeam

https://docs.moonbeam.network/ru/integrations/wallets/metamask/

https://docs.moonbeam.network/builders/interact/remix/