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
2. We create an ERC20 smart contract, here, for example, we use the import of one of the standard ERC20 libraries.
npm install @openzeppelin/contracts
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] } } };
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
3) Expectations until the ERC20 contract is deployed
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
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.
npx hardhat compile
npx hardhat test
npx hardhat run scripts/deploy.js --network dev
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".
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
We check from the second wallet that the tokens have arrived:
We send the remaining created tokens to the address of the smart contract
10. Finally, we check the function of buying tokens in the ICO smart contract. For this we use the Remix development environment:
Create 2 files "ICO.sol" and "MyTokenERC20.sol". We copy our contracts to the created files, respectively:
Let's move on to compiling the ICO.sol and MyTokenERC20.sol files that we created:
Now we are trying to buy our tokens using an ICO smart contract deployed in the remix development environment:
https://razumv.tech/smart-contract_Moonbeam
https://docs.moonbeam.network/builders/get-started/moonbeam-dev/
https://docs.moonbeam.network/tokens/connect/metamask/#connect-metamask-to-moonbeam
https://docs.moonbeam.network/ru/integrations/wallets/metamask/