Skip to main content

PSP22

This example shows how you can reuse the implementation of PSP22 token. Also, this example shows how you can customize the logic, for example, to reject transferring tokens to hated_account.

Step 1: Include dependencies#

Include brush as dependency in the cargo file or you can use default Cargo.toml template. After you need to enable default implementation of PSP22 via brush features.

brush = { tag = "v1.6.1", git = "https://github.com/Supercolony-net/openbrush-contracts", default-features = false, features = ["psp22"] }

Step 2: Add imports and enable unstable feature#

Use brush::contract macro instead of ink::contract. Import everything from brush::contracts::psp22.

#![cfg_attr(not(feature = "std"), no_std)]#![feature(min_specialization)]
#[brush::contract]pub mod my_psp22 {    use brush::contracts::psp22::*;    use ink_prelude::string::String;    use ink_storage::traits::SpreadAllocate;...

Step 3: Define storage#

Declare the storage struct and declare the field related to the PSP22Storage trait. Then you need to derive the PSP22Storage trait and mark the corresponding field with the #[PSP22StorageField] attribute. Deriving this trait allows you to reuse the default implementation of PSP22.

#[ink(storage)]#[derive(Default, SpreadAllocate, PSP22Storage)]pub struct MyPSP22 {    #[PSP22StorageField]    psp22: PSP22Data,}

Step 4: Inherit logic#

Inherit the implementation of PSP22 trait. You can customize (override) methods in the impl block.

impl PSP22 for MyPSP22 {}

Step 5: Define constructor#

Define constructor. Your basic version of PSP22 contract is ready!

impl MyPSP22 {   #[ink(constructor)]    pub fn new(total_supply: Balance) -> Self {        ink_lang::codegen::initialize_contract(|instance: &mut MyPSP22| {            instance                ._mint(instance.env().caller(), total_supply)                .expect("Should mint");        })    }}

Step 6: Customize your contract#

Customize it by adding hated account logic. It will contain two public methods set_hated_account and get_hated_account. Also we will override _before_token_transfer method in the PSP22 implementation(that methods defined in PSP22Transfer trait), and we will add the hated_account: AccountId field to the structure.

#[ink(storage)]#[derive(Default, SpreadAllocate, PSP22Storage)]pub struct MyPSP22 {    #[PSP22StorageField]    psp22: PSP22Data,    // fields for hater logic    hated_account: AccountId,}
impl PSP22Transfer for MyPSP22 {    // Let's override method to reject transactions to bad account    fn _before_token_transfer(        &mut self,        _from: Option<&AccountId>,        to: Option<&AccountId>,        _amount: &Balance,    ) -> Result<(), PSP22Error> {        if to == Some(&self.hated_account) {            return Err(PSP22Error::Custom(String::from("I hate this account!")))        }        Ok(())    }}
impl PSP22 for MyPSP22 {}
impl MyPSP22 {    #[ink(constructor)]    pub fn new(total_supply: Balance) -> Self {        ink_lang::codegen::initialize_contract(|instance: &mut MyPSP22| {            instance                ._mint(instance.env().caller(), total_supply)                .expect("Should mint");        })    }
    #[ink(message)]    pub fn set_hated_account(&mut self, hated: AccountId) {        self.hated_account = hated;    }
    #[ink(message)]    pub fn get_hated_account(&self) -> AccountId {        self.hated_account.clone()    }}

You can check an example of the usage of PSP22.

Also you can use extensions for PSP22 token:

PSP22Metadata: metadata for PSP22.

PSP22Mintable: creation of new tokens.

PSP22Burnable: destruction of own tokens.

PSP22Wrapper: token wrapper for PSP22.

PSP22FlashMint: extension which allows the user to perform flashloans on the token by minting and burning the token.

PSP22Capped: extension which adds a cap for total supply of PSP22 tokens.

Check out the utilities for PSP22 token:

PSP22TokenTimelock: utility for locking PSP22 tokens for a specified time.