Skip to main content
Version: 3.2.0

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: Import default implementation

With default Cargo.toml, you need to enable psp22 feature, embed modules data structures and implement them via #[openbrush::implementation] macro as described in that section.

The main trait is PSP22.

Step 2: Define constructor

Define constructor where you mint tokens to caller.

impl Contract {
#[ink(constructor)]
pub fn new(total_supply: Balance) -> Self {
let mut instance = Self {
psp22: Default::default(),
hated_storage: HatedStorage {
hated_account: [255; 32].into(),
},
};

Internal::_mint_to(&mut instance, Self::env().caller(), total_supply).expect("Should mint");

instance
}
}

Step 3: 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 Transfer trait), and we will add the hated_account: AccountId field to the structure.

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

// pub use my_psp22::*;
pub use openbrush::traits::{
AccountId,
Storage,
};

// we need to expand this struct before the contract macro is expanded
// that is why we declare it here for this example
#[ink::storage_item]
#[openbrush::accessors(HatedStorageAccessors)]
#[derive(Debug)]
pub struct HatedStorage {
#[get]
#[set]
pub hated_account: AccountId,
}

#[openbrush::implementation(PSP22)]
#[openbrush::contract]
pub mod my_psp22 {
use crate::*;
use openbrush::traits::String;

#[ink(storage)]
#[derive(Storage)]
pub struct Contract {
#[storage_field]
psp22: psp22::Data,
#[storage_field]
hated_storage: HatedStorage,
}

#[overrider(psp22::Internal)]
fn _before_token_transfer(
&mut self,
_from: Option<&AccountId>,
to: Option<&AccountId>,
_amount: &Balance,
) -> Result<(), PSP22Error> {
if to == Some(&self.hated_storage.hated_account) {
return Err(PSP22Error::Custom(String::from("I hate this account!")))
}
Ok(())
}

impl HatedStorageAccessors for Contract {}

impl Contract {
#[ink(constructor)]
pub fn new(total_supply: Balance) -> Self {
let mut instance = Self {
psp22: Default::default(),
hated_storage: HatedStorage {
hated_account: [255; 32].into(),
},
};

Internal::_mint_to(&mut instance, Self::env().caller(), total_supply).expect("Should mint");

instance
}
}
}

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.

Check out the utilities for PSP22 token:

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