Através da macro #[ink::trait_definition], agora é possível definir suas próprias definições de trait que podem ser implementadas por contratos inteligentes ink!.
Isso permite definir interfaces compartilhadas de contratos inteligentes para diferentes implementações concretas. Observe que essa definição de trait ink! pode ser definida em qualquer lugar, até mesmo em outra crate!
#[ink::trait_definition]
pub trait BaseErc20 {
/// Creates a new ERC-20 contract and initializes it with the initial supply for the instantiator.
#[ink(constructor)]
fn new(initial_supply: Balance) -> Self;
/// Returns the total supply.
#[ink(message)]
fn total_supply(&self) -> Balance;
/// Transfers `amount` from caller to `to`.
#[ink(message, payable)]
fn transfer(&mut self, to: AccountId, amount: Balance);
}
Uma definição de contrato inteligente ink! pode implementar essa definição de trait da seguinte forma:
Chamar a implementação do trait acima do Erc20 explicitamente pode ser feito da mesma forma que o código Rust normal:
// --- Instantiating the ERC-20 contract:
//
let mut erc20 = <Erc20 as BaseErc20>::new(1000);
// --- Is just the same as:
use base_erc20::BaseErc20;
let mut erc20 = Erc20::new(1000);
// --- Retrieving the total supply:
//
assert_eq!(<Erc20 as BaseErc20>::total_supply(&erc20), 1000);
// --- Is just the same as:
use base_erc20::BaseErc20;
assert_eq!(erc20.total_supply(), 1000);
Ainda existem várias limitações nas definições de trait e implementações de trait do ink!. Por exemplo, não é possível definir constantes ou tipos associados ou ter métodos implementados por padrão. Essas limitações existem devido a complexidades técnicas, no entanto, espere que muitas delas sejam abordadas em futuras versões do ink!.
Marcas especiais para definições de trait ink! como definições de trait do ink!.
Existem algumas restrições que se aplicam às definições de trait do ink! que essa macro verifica. Além disso, as definições de trait do ink! devem ter uma estrutura especializada para que a macro #[ink::contract] possa gerar corretamente o código para suas implementações.
Exemplo: Definição
type Balance = <ink::env::DefaultEnvironment as ink::env::Environment>::Balance;
#[ink::trait_definition]
pub trait Erc20 {
/// Constructs a new ERC-20 compliant smart contract using the initial supply.
#[ink(constructor)]
fn new(initial_supply: Balance) -> Self;
/// Returns the total supply of the ERC-20 smart contract.
#[ink(message)]
fn total_supply(&self) -> Balance;
// etc.
}
Exemplo: Implementação
Dada a definição de trait acima, você pode implementá-la da seguinte forma:
#[ink::contract]
mod base_erc20 {
/// We somehow cannot put the trait in the doc-test crate root due to bugs.
#[ink_lang::trait_definition]
pub trait Erc20 {
/// Constructs a new ERC-20 compliant smart contract using the initial supply.
#[ink(constructor)]
fn new(initial_supply: Balance) -> Self;
/// Returns the total supply of the ERC-20 smart contract.
#[ink(message)]
fn total_supply(&self) -> Balance;
}
#[ink(storage)]
pub struct BaseErc20 {
total_supply: Balance,
// etc ..
}
impl Erc20 for BaseErc20 {
#[ink(constructor)]
fn new(initial_supply: Balance) -> Self {
Self { total_supply: initial_supply }
}
/// Returns the total supply of the ERC-20 smart contract.
#[ink(message)]
fn total_supply(&self) -> Balance {
self.total_supply
}
// etc ..
}
}