Skip to main content

ERC777

Details of ERC777 can be found in ERC777.

Dependency#

metis_erc777 = { git = "https://github.com/patractlabs/metis", default-features = false }

Storage#

#[cfg_attr(feature = "std", derive(::ink_storage::traits::StorageLayout))]#[derive(Debug, SpreadLayout)]pub struct Data<E: Env> {    /// Total token supply.    pub total_supply: Lazy<E::Balance>,    /// Mapping from owner to number of owned token.    pub balances: StorageHashMap<E::AccountId, E::Balance>,    /// Mapping of the token amount which an account is allowed to withdraw    /// from another account.    pub allowances: StorageHashMap<(E::AccountId, E::AccountId), E::Balance>,    /// Metadatas Symbols of ERC777 Token, by (name, symbol)    pub metadatas: Lazy<(u8, String, String)>,
    /// This isn't ever read from - it's only used to respond to the defaultOperators query.    pub default_operators_array: Lazy<Vec<E::AccountId>>,
    /// Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators).    pub default_operators: StorageHashMap<E::AccountId, ()>,
    /// For each account, a mapping of its operators and revoked default operators.    pub operators: StorageHashMap<(E::AccountId, E::AccountId), ()>,    pub revoked_default_operators: StorageHashMap<(E::AccountId, E::AccountId), ()>,}

Mutable Functions#

send#

Moves amount tokens from the caller's account to recipient.

If send or receive hooks are registered for the caller and recipient, the corresponding functions will be called with data and empty operator_data. See erc777_sender and erc777_recipient.

Emits a Sent event.

Requirements

  • the caller must have at least amount tokens.
  • recipient cannot be the zero address.
  • if recipient is a contract, it must implement the erc777_recipient interface.
    fn send(        &mut self,        recipient: E::AccountId,        amount: E::Balance,        data: Vec<u8>,    ) -> Result<()> {        self._send(            Self::caller(),            recipient,            amount,            data,            Vec::default(),            true,        )    }

transfer#

Moves amount tokens from the caller's account to recipient.

Returns a boolean value indicating whether the operation succeeded.

Emits a Transfer event.

    fn transfer(&mut self, recipient: &E::AccountId, amount: E::Balance) -> Result<()> {        let null_account = &E::AccountId::default();        let from = &Self::caller();
        if recipient == null_account {            return Err(Error::AccountIsZero)        }
        let null_data = &Vec::<u8>::default();
        self._call_tokens_to_send(            &from,            &Some(&from),            &Some(&recipient),            &amount,            null_data,            null_data,        );
        self._move(&from, &from, &recipient, &amount, null_data, null_data)?;
        self._call_tokens_received(            &from,            &Some(&from),            &Some(&recipient),            &amount,            null_data,            null_data,            false,        );
        Ok(())    }

burn#

Destroys amount tokens from the caller's account, reducing the total supply.

If a send hook is registered for the caller, the corresponding function will be called with data and empty operator_data. See erc777_sender.

Emits a Burned event.

Requirements

  • the caller must have at least amount tokens.
    fn burn(&mut self, amount: E::Balance, data: Vec<u8>) -> Result<()> {        self._burn(Self::caller(), amount, data, Vec::default())    }

authorize_operator#

Make an account an operator of the caller.

See is_operator_for.

Emits an AuthorizedOperator event.

Requirements

  • operator cannot be calling address.
    fn authorize_operator(&mut self, operator: E::AccountId) {        let caller = Self::caller();        assert!(caller != operator, "ERC777: authorizing self as operator");
        let key = (caller.clone(), operator.clone());
        if self.get().is_default_operator(&operator) {            self.get_mut().revoked_default_operators.take(&key);        } else {            self.get_mut().operators.insert(key, ());        }
        self.emit_event_authorized_operator(operator, caller);    }

revoke_operator#

Revoke an account's operator status for the caller.

See is_operator_for and default_operators.

Emits a RevokedOperator event.

Requirements

  • operator cannot be calling address.
    fn revoke_operator(&mut self, operator: E::AccountId) {        let caller = Self::caller();        assert!(caller != operator, "ERC777: revoke self as operator");
        let key = (caller.clone(), operator.clone());
        if self.get().is_default_operator(&operator) {            self.get_mut().revoked_default_operators.insert(key, ());        } else {            self.get_mut().operators.take(&key);        }
        self.emit_event_revoked_operator(operator, caller);    }

operator_send#

Moves amount tokens from sender to recipient. The caller must be an operator of sender.

If send or receive hooks are registered for sender and recipient, the corresponding functions will be called with data and operator_data. See erc777_sender and erc777_recipient.

Emits a Sent event.

Requirements

  • sender cannot be the zero address.
  • sender must have at least amount tokens.
  • the caller must be an operator for sender.
  • recipient cannot be the zero address.
  • if recipient is a contract, it must implement the erc777_recipient interface.
    fn operator_send(        &mut self,        sender: E::AccountId,        recipient: E::AccountId,        amount: E::Balance,        data: Vec<u8>,        operator_data: Vec<u8>,    ) -> Result<()> {        assert!(            self.get().is_operator_for(&Self::caller(), &sender),            "ERC777: caller is not an operator for holder"        );
        self._send(sender, recipient, amount, data, operator_data, true)    }

operator_burn#

Destroys amount tokens from account, reducing the total supply. The caller must be an operator of account.

If a send hook is registered for account, the corresponding function will be called with data and operator_data. See erc777_sender.

Emits a Burned event.

Requirements

  • account cannot be the zero address.
  • account must have at least amount tokens.
  • the caller must be an operator for account.
    fn operator_burn(        &mut self,        account: E::AccountId,        amount: E::Balance,        data: Vec<u8>,        operator_data: Vec<u8>,    ) -> Result<()> {        assert!(            self.get().is_operator_for(&Self::caller(), &account),            "ERC777: caller is not an operator for holder"        );
        self._burn(account, amount, data, operator_data)    }

approve#

Sets amount as the allowance of spender over the caller's tokens.

Returns a boolean value indicating whether the operation succeeded.

IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729

Emits an Approval event.

    fn approve(&mut self, spender: &E::AccountId, amount: E::Balance) -> Result<()> {        self._approve(&Self::caller(), spender, amount)    }

transfer_from#

Moves amount tokens from sender to recipient using the allowance mechanism. amount is then deducted from the caller's allowance.

Returns a boolean value indicating whether the operation succeeded.

Emits a Transfer event.

    fn transfer_from(        &mut self,        holder: &E::AccountId,        recipient: &E::AccountId,        amount: E::Balance,    ) -> Result<()> {        let caller = &Self::caller();        let null_account = &E::AccountId::default();
        if recipient == null_account {            return Err(Error::AccountIsZero)        }
        let current_allowance = self.get().allowance(holder, caller);        if current_allowance < amount {            return Err(Error::InsufficientAllowance)        }
        let spender = Self::caller();
        self._call_tokens_to_send(            &spender,            &Some(&holder),            &Some(&recipient),            &amount,            &Vec::default(),            &Vec::default(),        );
        self._approve(holder, caller, current_allowance - amount)?;
        self._move(            &spender,            &holder,            &recipient,            &amount,            &Vec::default(),            &Vec::default(),        )?;
        self._call_tokens_received(            &spender,            &Some(&holder),            &Some(&recipient),            &amount,            &Vec::default(),            &Vec::default(),            false,        );
        Ok(())    }

Immutable Functions#

name#

Returns the name of the token.

    fn name(&self) -> String {        self.get().name().clone()    }

symbol#

Returns the symbol of the token, usually a shorter version of the name.

    fn symbol(&self) -> String {        self.get().symbol().clone()    }

decimals#

Returns the number of decimals used to get its user representation. For example, if decimals equals 2, a balance of 505 tokens should be displayed to a user as 5,05 (505 / 10 ** 2).

Tokens usually opt for a value of 18, imitating the relationship between Ether and Wei in ETH. This is the value ERC20 uses, unless this function is overridden;

NOTE: This information is only used for display purposes: it in no way affects any of the arithmetic of the contract

    fn decimals(&self) -> u8 {        self.get().decimals().clone()    }

granularity#

Returns the smallest part of the token that is not divisible. This means all token operations (creation, movement and destruction) must have amounts that are a multiple of this number.

For most token contracts, this value will equal 1.

    fn granularity(&self) -> E::Balance {        E::Balance::from(1_u8)    }

total_supply#

Returns the amount of tokens in existence.

    fn total_supply(&self) -> E::Balance {        self.get().total_supply()    }

balance_of#

Returns the amount of tokens owned by account.

    fn balance_of(&self, account: &E::AccountId) -> E::Balance {        self.get().balance_of(account)    }

is_operator_for#

Returns true if an account is an operator of token_holder. Operators can send and burn tokens on behalf of their owners. All accounts are their own operator.

See operator_send and operator_burn.

    fn is_operator_for(        &self,        operator: E::AccountId,        token_holder: E::AccountId,    ) -> bool {        self.get().is_operator_for(&operator, &token_holder)    }

default_operators#

Returns the list of default operators. These accounts are operators for all token holders, even if authorize_operator was never called on them.

This list is immutable, but individual holders may revoke these via revoke_operator, in which case is_operator_for will return false.

    fn default_operators(&self) -> Vec<E::AccountId> {        self.get().default_operators_array.clone()    }

allowance#

Returns the remaining number of tokens that spender will be allowed to spend on behalf of owner through transfer_from. This is zero by default.

This value changes when approve or transfer_from are called.

    fn allowance(&self, owner: &E::AccountId, spender: &E::AccountId) -> E::Balance {        self.get().allowance(owner, spender)    }

Internal Functions#

There are some internal functions can help developer to add self-defined messages:

  • _mint
  • _mint_required_reception_ack
  • _burn

_mint#

Creates amount tokens and assigns them to account, increasing the total supply.

If a send hook is registered for account, the corresponding function will be called with operator, data and operator_data.

See erc777_sender and erc777_recipient.

Emits Minted and Transfer events.

Requirements

  • account cannot be the zero address.
  • if account is a contract, it must implement the erc777_recipient interface.
    fn _mint(        &mut self,        account: E::AccountId,        amount: E::Balance,        user_data: Vec<u8>,        operator_data: Vec<u8>,    ) -> Result<()> {        self._mint_required_reception_ack(account, amount, user_data, operator_data, true)    }

_mint_required_reception_ack#

Creates amount tokens and assigns them to account, increasing the total supply.

If requireReceptionAck is set to true, and if a send hook is registered for account, the corresponding function will be called with operator, data and operator_data.

See erc777_sender and erc777_recipient.

Emits Minted and Transfer events.

Requirements

  • account cannot be the zero address.
  • if account is a contract, it must implement the erc777_recipient interface.
    fn _mint_required_reception_ack(        &mut self,        account: E::AccountId,        amount: E::Balance,        user_data: Vec<u8>,        operator_data: Vec<u8>,        required_reception_ack: bool,    ) -> Result<()> {        let null_account = E::AccountId::default();
        if account == null_account {            return Err(Error::AccountIsZero)        }
        let operator = Self::caller();
        self._before_token_transfer(&operator, &None, &Some(&account), &amount)?;
        // Update state variables        let current_total = self.get().total_supply();        let current_balance = self.get().balance_of(&account);
        self.get_mut().set_total_supply(current_total + amount);        self.get_mut()            .set_balance(&account, current_balance + amount);
        self._call_tokens_received(            &operator,            &None,            &Some(&account),            &amount,            &user_data,            &operator_data,            required_reception_ack,        );
        self.emit_event_minted(            operator,            account.clone(),            amount,            user_data,            operator_data,        );        self.emit_event_transfer(None, Some(account), amount);
        Ok(())    }

_burn#

Burn tokens

Params:

  • from address token holder address
  • amount uint256 amount of tokens to burn
  • data bytes extra information provided by the token holder
  • operator_data bytes extra information provided by the operator (if any)
    fn _burn(        &mut self,        from: E::AccountId,        amount: E::Balance,        data: Vec<u8>,        operator_data: Vec<u8>,    ) -> Result<()> {        let null_account = E::AccountId::default();
        assert!(from != null_account, "ERC777: burn from the zero address");
        let operator = Self::caller();
        self._call_tokens_to_send(            &operator,            &Some(&from),            &None,            &amount,            &data,            &operator_data,        );
        self._before_token_transfer(&operator, &Some(&from), &None, &amount)?;
        // Update state variables        let from_balance = self.get().balance_of(&from);        assert!(            from_balance >= amount,            "ERC777: burn amount exceeds balance"        );        self.get_mut().set_balance(&from, from_balance - amount);
        let current_total = self.get().total_supply();        self.get_mut().set_total_supply(current_total - amount);
        self.emit_event_burned(operator, from.clone(), amount, data, operator_data);        self.emit_event_transfer(Some(from), None, amount);
        Ok(())    }

Hooks#

_before_token_transfer#

Hook that is called before any token transfer. This includes calls to send, transfer, operator_send, minting and burning.

Calling conditions:

  • when from and to are both non-zero, amount of from's tokens will be to transferred to to.
  • when from is zero, amount tokens will be minted for to.
  • when to is zero, amount of from's tokens will be burned.
  • from and to are never both zero.
    fn _before_token_transfer(        &mut self,        _operator: &E::AccountId,        _from: &Option<&E::AccountId>,        _to: &Option<&E::AccountId>,        _amount: &E::Balance,    ) -> Result<()> {        Ok(())    }

Events#

Transfer#

Event emitted when a token transfer occurs.

    #[ink(event)]    #[metis(erc777)]    pub struct Transfer {        #[ink(topic)]        pub from: Option<AccountId>,        #[ink(topic)]        pub to: Option<AccountId>,        pub value: Balance,    }

Approval#

Event emitted when an approval occurs that spender is allowed to withdraw up to the amount of value tokens from owner.

    #[ink(event)]    #[metis(erc777)]    pub struct Approval {        #[ink(topic)]        pub owner: AccountId,        #[ink(topic)]        pub spender: AccountId,        pub value: Balance,    }

Sent#

Indicate a send of amount of tokens from the from address to the to address by the operator address.

NOTE: This event MUST NOT be emitted outside of a send or an ERC-20 transfer process.

    #[ink(event)]    #[metis(erc777)]    pub struct Sent {        #[ink(topic)]        pub operator: AccountId,        #[ink(topic)]        pub from: AccountId,        #[ink(topic)]        pub to: AccountId,        pub amount: Balance,        pub data: Vec<u8>,        pub operator_data: Vec<u8>,    }

Minted#

Indicate the minting of amount of tokens to the to address by the operator address.

NOTE: This event MUST NOT be emitted outside of a mint process.

    #[ink(event)]    #[metis(erc777)]    pub struct Minted {        #[ink(topic)]        pub operator: AccountId,        #[ink(topic)]        pub to: AccountId,        pub amount: Balance,        pub data: Vec<u8>,        pub operator_data: Vec<u8>,    }

Burned#

Indicate the burning of amount of tokens from the from address by the operator address.

NOTE: This event MUST NOT be emitted outside of a burn process.

    #[ink(event)]    #[metis(erc777)]    pub struct Burned {        #[ink(topic)]        pub operator: AccountId,        #[ink(topic)]        pub from: AccountId,        pub amount: Balance,        pub data: Vec<u8>,        pub operator_data: Vec<u8>,    }

AuthorizedOperator#

Indicates the authorization of operator as an operator for holder.

NOTE: This event MUST NOT be emitted outside of an operator authorization process.

    #[ink(event)]    #[metis(erc777)]    pub struct AuthorizedOperator {        #[ink(topic)]        pub operator: AccountId,        #[ink(topic)]        pub token_holder: AccountId,    }

RevokedOperator#

Indicates the revocation of operator as an operator for holder.

NOTE: This event MUST NOT be emitted outside of an operator revocation process.

    #[ink(event)]    #[metis(erc777)]    pub struct RevokedOperator {        #[ink(topic)]        pub operator: AccountId,        #[ink(topic)]        pub token_holder: AccountId,    }

Usage Example#

To make a new erc777-like token, we should import erc777 at first:

#[metis_lang::contract]pub mod erc777_contract {    use super::super::behavior;    pub use erc777::{        Error,        Result,    };    use metis_erc777 as erc777;    use metis_lang::{        import,        metis,    };
    #[ink(storage)]    #[import(erc777)]    pub struct Erc777 {        erc777: erc777::Data<Erc777>,    }
    // Note: Now version of metis not support auto impl for erc777    #[cfg(not(feature = "ink-as-dependency"))]    impl erc777::Impl<Erc777> for Erc777 {}}

Note: Now version of metis not support auto impl for erc777.

Should add:

   #[cfg(not(feature = "ink-as-dependency"))]   impl erc777::Impl<Erc777> for Erc777 {}

Then add the event for erc777:

     /// Event emitted when a token transfer occurs.    #[ink(event)]    #[metis(erc777)]    pub struct Transfer {        #[ink(topic)]        pub from: Option<AccountId>,        #[ink(topic)]        pub to: Option<AccountId>,        pub value: Balance,    }
    /// Event emitted when an approval occurs that `spender` is allowed to withdraw    /// up to the amount of `value` tokens from `owner`.    #[ink(event)]    #[metis(erc777)]    pub struct Approval {        #[ink(topic)]        pub owner: AccountId,        #[ink(topic)]        pub spender: AccountId,        pub value: Balance,    }
    /// Indicate a send of `amount` of tokens from the `from` address to the `to`    /// address by the `operator` address.    ///     /// NOTE: This event MUST NOT be emitted outside of    /// a send or an ERC-20 transfer process.    #[ink(event)]    #[metis(erc777)]    pub struct Sent {        #[ink(topic)]        pub operator: AccountId,        #[ink(topic)]        pub from: AccountId,        #[ink(topic)]        pub to: AccountId,        pub amount: Balance,        pub data: Vec<u8>,        pub operator_data: Vec<u8>,    }
    /// Indicate the minting of `amount` of tokens to the `to` address by    /// the `operator` address.    ///    /// NOTE: This event MUST NOT be emitted outside of a mint process.    #[ink(event)]    #[metis(erc777)]    pub struct Minted {        #[ink(topic)]        pub operator: AccountId,        #[ink(topic)]        pub to: AccountId,        pub amount: Balance,        pub data: Vec<u8>,        pub operator_data: Vec<u8>,    }
    /// Indicate the burning of `amount` of tokens from the `from` address    /// by the `operator` address.    ///     /// NOTE: This event MUST NOT be emitted outside of a burn process.    #[ink(event)]    #[metis(erc777)]    pub struct Burned {        #[ink(topic)]        pub operator: AccountId,        #[ink(topic)]        pub from: AccountId,        pub amount: Balance,        pub data: Vec<u8>,        pub operator_data: Vec<u8>,    }
    /// Indicates the authorization of `operator` as an operator for `holder`.    ///     /// NOTE: This event MUST NOT be emitted outside    /// of an operator authorization process.    #[ink(event)]    #[metis(erc777)]    pub struct AuthorizedOperator {        #[ink(topic)]        pub operator: AccountId,        #[ink(topic)]        pub token_holder: AccountId,    }
    /// Indicates the revocation of `operator` as an operator for `holder`.    ///     /// NOTE: This event MUST NOT be emitted outside    /// of an operator revocation process.    #[ink(event)]    #[metis(erc777)]    pub struct RevokedOperator {        #[ink(topic)]        pub operator: AccountId,        #[ink(topic)]        pub token_holder: AccountId,    }

impl the constructor for contract:

    impl Erc777 {        #[ink(constructor)]        pub fn new(            name: String,            symbol: String,            decimals: u8,            initial_supply: Balance,        ) -> Self {            let mut instance = Self {                erc777: erc777::Data::new(),            };
            erc777::Impl::init(&mut instance,                 name, symbol, decimals,                initial_supply);
            // other logics            instance        }    }
Then implement the messages for contract:
```rust    impl Erc777 {        /// Returns the name of the token.        #[ink(message)]        pub fn name(&self) -> String {            erc777::Impl::name(self)        }
        /// Returns the symbol of the token, usually a shorter version of the name.        #[ink(message)]        pub fn symbol(&self) -> String {            erc777::Impl::symbol(self)        }
        /// Returns the number of decimals used to get its user representation.        /// For example, if `decimals` equals `2`, a balance of `505` tokens should        /// be displayed to a user as `5,05` (`505 / 10 ** 2`).        ///        /// Tokens usually opt for a value of 18, imitating the relationship between        /// Ether and Wei in ETH. This is the value `ERC20` uses, unless this function is        /// overridden;        ///        /// NOTE: This information is only used for _display_ purposes: it in        /// no way affects any of the arithmetic of the contract        #[ink(message)]        pub fn decimals(&self) -> u8 {            erc777::Impl::decimals(self)        }
        /// Returns the smallest part of the token that is not divisible. This        /// means all token operations (creation, movement and destruction) must have        /// amounts that are a multiple of this number.        ///        /// For most token contracts, this value will equal 1.        #[ink(message)]        pub fn granularity(&self) -> Balance {            erc777::Impl::granularity(self)        }
        /// Returns the amount of tokens in existence.        #[ink(message)]        pub fn total_supply(&self) -> Balance {            erc777::Impl::total_supply(self)        }
        /// Returns the amount of tokens owned by `account`.        #[ink(message)]        pub fn balance_of(&self, owner: AccountId) -> Balance {            erc777::Impl::balance_of(self, &owner)        }
        /// Moves `amount` tokens from the caller's account to `recipient`.        ///        /// If send or receive hooks are registered for the caller and `recipient`,        /// the corresponding functions will be called with `data` and empty        /// `operator_data`. See `erc777_sender` and `erc777_recipient`.        ///        /// Emits a `Sent` event.        ///        /// Requirements        ///        /// - the caller must have at least `amount` tokens.        /// - `recipient` cannot be the zero address.        /// - if `recipient` is a contract, it must implement the `erc777_recipient` interface.        #[ink(message)]        pub fn send(            &mut self,            recipient: AccountId,            amount: Balance,            data: Vec<u8>,        ) -> Result<()> {            erc777::Impl::send(self, recipient, amount, data)        }
        /// Moves `amount` tokens from the caller's account to `recipient`.        ///        /// Returns a boolean value indicating whether the operation succeeded.        ///        /// Emits a `Transfer` event.        #[ink(message)]        pub fn transfer(&mut self, to: AccountId, value: Balance) -> Result<()> {            erc777::Impl::transfer(self, &to, value)        }
        /// Destroys `amount` tokens from the caller's account, reducing the        /// total supply.        ///        /// If a send hook is registered for the caller, the corresponding function        /// will be called with `data` and empty `operator_data`. See `erc777_sender`.        ///        /// Emits a `Burned` event.        ///        /// Requirements        ///        /// - the caller must have at least `amount` tokens.        #[ink(message)]        pub fn burn(&mut self, amount: Balance, data: Vec<u8>) -> Result<()> {            erc777::Impl::burn(self, amount, data)        }
        /// Returns true if an account is an operator of `token_holder`.        /// Operators can send and burn tokens on behalf of their owners. All        /// accounts are their own operator.        ///        /// See `operator_send` and `operator_burn`.        #[ink(message)]        pub fn is_operator_for(            &self,            operator: AccountId,            token_holder: AccountId,        ) -> bool {            erc777::Impl::is_operator_for(self, operator, token_holder)        }
        /// Make an account an operator of the caller.        ///        /// See `is_operator_for`.        ///        /// Emits an `AuthorizedOperator` event.        ///        /// Requirements        ///        /// - `operator` cannot be calling address.        #[ink(message)]        pub fn authorize_operator(&mut self, operator: AccountId) {            erc777::Impl::authorize_operator(self, operator)        }
        /// Revoke an account's operator status for the caller.        ///        /// See `is_operator_for` and `default_operators`.        ///        /// Emits a `RevokedOperator` event.        ///        /// Requirements        ///        /// - `operator` cannot be calling address.        #[ink(message)]        pub fn revoke_operator(&mut self, operator: AccountId) {            erc777::Impl::revoke_operator(self, operator)        }
        /// Returns the list of default operators. These accounts are operators        /// for all token holders, even if `authorize_operator` was never called on        /// them.        ///        /// This list is immutable, but individual holders may revoke these via        /// `revoke_operator`, in which case `is_operator_for` will return false.        #[ink(message)]        pub fn default_operators(&self) -> Vec<AccountId> {            erc777::Impl::default_operators(self)        }
        /// Moves `amount` tokens from `sender` to `recipient`. The caller must        /// be an operator of `sender`.        ///        /// If send or receive hooks are registered for `sender` and `recipient`,        /// the corresponding functions will be called with `data` and        /// `operator_data`. See `erc777_sender` and `erc777_recipient`.        ///        /// Emits a `Sent` event.        ///        /// Requirements        ///        /// - `sender` cannot be the zero address.        /// - `sender` must have at least `amount` tokens.        /// - the caller must be an operator for `sender`.        /// - `recipient` cannot be the zero address.        /// - if `recipient` is a contract, it must implement the `erc777_recipient` interface.        #[ink(message)]        pub fn operator_send(            &mut self,            sender: AccountId,            recipient: AccountId,            amount: Balance,            data: Vec<u8>,            operator_data: Vec<u8>,        ) -> Result<()> {            erc777::Impl::operator_send(                self,                sender,                recipient,                amount,                data,                operator_data,            )        }
        /// Destroys `amount` tokens from `account`, reducing the total supply.        /// The caller must be an operator of `account`.        ///        /// If a send hook is registered for `account`, the corresponding function        /// will be called with `data` and `operator_data`. See `erc777_sender`.        ///        /// Emits a `Burned` event.        ///        /// Requirements        ///        /// - `account` cannot be the zero address.        /// - `account` must have at least `amount` tokens.        /// - the caller must be an operator for `account`.        #[ink(message)]        pub fn operator_burn(            &mut self,            account: AccountId,            amount: Balance,            data: Vec<u8>,            operator_data: Vec<u8>,        ) -> Result<()> {            erc777::Impl::operator_burn(self, account, amount, data, operator_data)        }
        /// Returns the remaining number of tokens that `spender` will be        /// allowed to spend on behalf of `owner` through `transfer_from`. This is        /// zero by default.        ///        /// This value changes when `approve` or `transfer_from` are called.        #[ink(message)]        pub fn allowance(&self, owner: AccountId, spender: AccountId) -> Balance {            erc777::Impl::allowance(self, &owner, &spender)        }
        /// Sets `amount` as the allowance of `spender` over the caller's tokens.        ///        /// Returns a boolean value indicating whether the operation succeeded.        ///        /// IMPORTANT: Beware that changing an allowance with this method brings the risk        /// that someone may use both the old and the new allowance by unfortunate        /// transaction ordering. One possible solution to mitigate this race        /// condition is to first reduce the spender's allowance to 0 and set the        /// desired value afterwards:        /// <https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729>        ///        /// Emits an `Approval` event.        #[ink(message)]        pub fn approve(&mut self, spender: AccountId, value: Balance) -> Result<()> {            erc777::Impl::approve(self, &spender, value)        }
        /// Moves `amount` tokens from `sender` to `recipient` using the        /// allowance mechanism. `amount` is then deducted from the caller's        /// allowance.        ///        /// Returns a boolean value indicating whether the operation succeeded.        ///        /// Emits a `Transfer` event.        #[ink(message)]        pub fn transfer_from(            &mut self,            from: AccountId,            to: AccountId,            value: Balance,        ) -> Result<()> {            erc777::Impl::transfer_from(self, &from, &to, value)        }
        /// Creates `amount` tokens and assigns them to `account`, increasing        /// the total supply.        ///        /// If a send hook is registered for `account`, the corresponding function        /// will be called with `operator`, `data` and `operator_data`.        ///        /// See `erc777_sender` and `erc777_recipient`.        ///        /// Emits `Minted` and `Transfer` events.        ///        /// Requirements        ///        /// - `account` cannot be the zero address.        /// - if `account` is a contract, it must implement the `erc777_recipient` interface.        #[ink(message)]        pub fn mint(&mut self, to: AccountId, value: Balance) -> Result<()> {            erc777::Impl::_mint(self, to, value, Vec::default(), Vec::default())        }    }

In the end, we can add some other messages.