User Balances, MintingFungible Token Contract
3/11 tutorials
27%

Implementing User Balances and Token Minting

Next, extend the fungible token contract by adding user balances and a method to mint new tokens.

The addition of the following capabilities will enhance the contract:

  • Managing user balances
  • Minting new tokens

Сode explanation

  1. Storage Structure: The State structure is expanded to include a balances field for managing user balances.
  2. Storage Implementation:
    • get: Returns a reference to the storage for read-only operations.
    • get_mut: Returns a mutable reference to the storage for operations that modify the state.
  3. Token Service Implementation:
    • Initialization (init method): Initializes the static storage with the token name and an empty balance map.
    • Minting (mint method): Increases the balance of a specified user by a specified amount. This method is mutable (&mut self) as it changes the state.
    • Name Retrieval (name method): Returns the token name stored in the static storage. This method is non-mutable (&self) as it only queries the state.
    • Balance Query (balance_of method): Returns the balance of a specified user. This method is non-mutable (&self) as it only queries the state.
#![no_std]
use sails_rs::{collections::HashMap, prelude::*};

pub struct State {
    name: String,
    balances: HashMap<ActorId, U256>,
}

static mut STATE: Option<State> = None;

impl State {
    pub fn get() -> &'static Self {
        unsafe { STATE.as_ref().expect("State is not initialized") }
    }

    pub fn get_mut() -> &'static mut Self {
        unsafe { STATE.as_mut().expect("State is not initialized") }
    }
}

#[derive(Default)]
pub struct Token;

#[service]
impl Token {
    pub fn init(name: String) {
        unsafe {
            STATE = Some(State {
                name,
                balances: HashMap::new(),
            });
        }
    }

    pub fn mint(&mut self, to: ActorId, value: U256) {
        let state = State::get_mut();
        let balance = state.balances.entry(to).or_insert(U256::zero());
        *balance += value;
    }

    pub fn name(&self) -> &'static str {
        let state = State::get();
        &state.name
    }

    pub fn balance_of(&self, account: ActorId) -> U256 {
        let state = State::get();
        *state.balances.get(&account).unwrap_or(&U256::zero())
    }
}

pub struct MyProgram;

#[program]
impl MyProgram {
    pub fn new(name: String) -> Self {
        Token::init(name);
        Self
    }

    pub fn token(&self) -> Token {
        Token::default()
    }
}