Shares contract
Similarly, we will implement another PSP22 token which will represent the ownership of assets available by the smart contract to be lent. In this token, we will need PSP22Metadata and we will also need to mint and burn this token. We only want our contract(lending contract) to perform these actions, so we will also add the Ownable extension.
Definition of the Shares
trait
In the traits/shares.rs
, we will define a Shares
trait.
That trait contains the next super traits: PSP22
, PSP22Mintable
, PSP22Burnable
, PSP22Metadata
, and Ownable
, without any other method.
That shows that Shares
is PSP22
with mint and burn methods that can be called only by the owner.
In the implementation of the contract, we will implement that trait to be sure that all super traits are also implemented.
SharesRef
can be used by other developers to do a cross contract call to SharesContract
.
use openbrush::contracts::traits::{
ownable::*,
psp22::{
extensions::{
burnable::*,
metadata::*,
mintable::*,
},
*,
},
};
#[openbrush::wrapper]
pub type SharesRef = dyn PSP22 + PSP22Mintable + PSP22Burnable + PSP22Metadata + Ownable;
#[openbrush::trait_definition]
pub trait Shares: PSP22 + PSP22Mintable + PSP22Burnable + PSP22Metadata + Ownable {}
Add dependencies
In addition to the dependencies imported in the PSP22
documentation, we will also add the ownable
dependency the same way as in the
ownable documentation. We will be using SharesContract
as a dependency in our lending contract to instantiate it. So we need to also add
the "rlib"
crate type to have the ability to import the SharesContract
as a dependency.
Implement the contract
Implementing our shares contract will follow the same steps as implementing
the basic PSP22
contract in the previous step, but we will do some small
changes for the token to be mintable, burnable, and for these functions to
be restricted. Therefore, on top of the imports in the previous contract,
we also need these imports:
#![cfg_attr(not(feature = "std"), no_std, no_main)]
/// This contract will be used to represent the shares of a user
/// and other instance of this contract will be used to represent
/// the amount of borrowed tokens
#[openbrush::implementation(PSP22, PSP22Mintable, PSP22Burnable, PSP22Metadata, Ownable)]
#[openbrush::contract]
pub mod shares {
use openbrush::traits::String;
use lending_project::traits::shares::*;
use openbrush::{
modifiers,
traits::Storage,
};
Define the storage
In this storage, we will also derive the storage trait related to Ownable
and declare the field related to this trait.
/// Define the storage for PSP22 data, Metadata data and Ownable data
#[ink(storage)]
#[derive(Default, Storage)]
pub struct SharesContract {
#[storage_field]
psp22: psp22::Data,
#[storage_field]
ownable: ownable::Data,
#[storage_field]
metadata: metadata::Data,
}
Implement the extension traits
We will be using these extensions in our token, so we will implement them for our storage.
// It forces the compiler to check that you implemented all super traits
impl Shares for SharesContract {}
Implement the Burnable and Mintable traits
Now we will implement the PSP22Burnable
and PSP22Mintable
traits.
These are a little different so we are doing it in a separate section.
We don't want anybody to mint or burn the tokens, we only want the owner,
in this case, our lending contract, to do it. So we will add the PSP22Burnable
and PSP22Mintable
and mark the functions of these traits with the only_owner
restriction. Here we are using the #[default_impl]
macro to mark, that we want to use default implementation of the trait's
method but to use some modifiers or add other attributes to the method.
/// override the `mint` function to add the `only_owner` modifier
#[default_impl(PSP22Mintable)]
#[modifiers(only_owner)]
fn mint() {}
/// override the `burn` function to add the `only_owner` modifier
#[default_impl(PSP22Burnable)]
#[modifiers(only_owner)]
fn burn() {}
This will restrict accounts other than the owner of the token (which will be the lending contract) from calling these functions.
Define the constructor
Finally, we will define the constructor where we will set the name and the symbol of the token and then initialize the owner of the token (which then will be able to mint and burn the tokens).
impl SharesContract {
/// constructor with name and symbol
#[ink(constructor)]
pub fn new(name: Option<String>, symbol: Option<String>) -> Self {
let mut instance = Self::default();
let caller = Self::env().caller();
instance.metadata.name = name;
instance.metadata.symbol = symbol;
instance.metadata.decimals = 18;
instance._init_with_owner(caller);
instance
}
}