Skip to main content

Creating the ERC20 Template

We are going to start another ink! project to build an ERC20 token contract.

Back in your working directory, run:

cargo contract new erc20

Again, we will replace the lib.rs file content with the template provided on this page.

You will notice that the template for the ERC20 token is VERY similar to the Incrementer contract. (Coincidence? ¯_()_)

The storage (so far) consists of:

  • A storage Value: representing the total supply of tokens in our contract.
  • A storage HashMap: representing the individual balance of each account.

ERC20 Deployment

The most basic ERC20 token contract is a fixed supply token. During contract deployment, all the tokens will be automatically given to the contract creator. It is then up to that user to distribute those tokens to other users as they see fit.

Of course, this is not the only way to mint and distribute tokens, but the most simple one, and what we will be doing here.

So remember to set the total balance and insert the balance of the Self::env().caller()

Your Turn!

This chapter should be nothing more than a quick refresher of the content you already learned.

You need to:

  • Set up a constructor function which initializes the two storage items
  • Create getters for both storage items
  • Create a balance_of_or_zero function to handle reading values from the HashMap

Remember to run cargo +nightly test to test your work.

#![cfg_attr(not(feature = "std"), no_std)]

use ink_lang as ink;

#[ink::contract]
mod erc20 {
#[cfg(not(feature = "ink-as-dependency"))]
#[ink(storage)]
pub struct Erc20 {
/// The total supply.
total_supply: Balance,
/// The balance of each user.
balances: ink_storage::collections::HashMap<AccountId, Balance>,
}

impl Erc20 {
#[ink(constructor)]
pub fn new(initial_supply: Balance) -> Self {
// ACTION: `set` the total supply to `init_value`
// ACTION: `insert` the `init_value` as the `caller` balance
}

#[ink(message)]
pub fn total_supply(&self) -> Balance {
// ACTION: Return the total supply
}

#[ink(message)]
pub fn balance_of(&self, owner: AccountId) -> Balance {
// ACTION: Return the balance of `owner`
// HINT: Use `balance_of_or_zero` to get the `owner` balance
}

fn balance_of_or_zero(&self, owner: &AccountId) -> Balance {
// ACTION: `get` the balance of `owner`, then `unwrap_or` fallback to 0
// ACTION: Return the balance
}
}

#[cfg(test)]
mod tests {
use super::*;

use ink_lang as ink;

#[ink::test]
fn new_works() {
let contract = Erc20::new(777);
assert_eq!(contract.total_supply(), 777);
}

#[ink::test]
fn balance_works() {
let contract = Erc20::new(100);
assert_eq!(contract.total_supply(), 100);
assert_eq!(contract.balance_of(AccountId::from([0x1; 32])), 100);
assert_eq!(contract.balance_of(AccountId::from([0x0; 32])), 0);
}
}
}