Skip to main content

ERC1155

Details of ERC1155 can be found in ERC1155

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> {    /// Symbols of ERC1155 Token, by (name, symbol)    pub url: Lazy<String>,
    /// Mapping from token ID to account balances    pub balances: StorageHashMap<(TokenId, E::AccountId), E::Balance>,
    /// Mapping from account to operator approvals    pub operator_approvals: StorageHashMap<(E::AccountId, E::AccountId), bool>,}

Mutable Message#

set_approval_for_all#

Grants or revokes permission to operator to transfer the caller's tokens, according to approved,

Emits an ApprovalForAll event.

Requirements:

  • operator cannot be the caller.
    fn set_approval_for_all(&mut self, operator: E::AccountId, approved: bool) {        let caller = Self::caller();
        assert!(            caller != operator,            "ERC1155: setting approval status for self"        );
        self.get_mut()            .set_approval_for_all(caller.clone(), operator.clone(), approved);        self.emit_event_approval_for_all(caller, operator, approved);    }

safe_transfer_from#

Transfers amount tokens of token type id from from to to.

Emits a TransferSingle event.

Requirements:

  • to cannot be the zero address.
  • If the caller is not from, it must be have been approved to spend from's tokens via set_approval_for_all.
  • from must have a balance of tokens of type id of at least amount.
  • If to refers to a smart contract, it must implement on_erc1155_received and return the acceptance magic value.
    fn safe_transfer_from(        &mut self,        from: E::AccountId,        to: E::AccountId,        id: TokenId,        amount: E::Balance,        data: Vec<u8>,    ) -> Result<()> {        let caller = Self::caller();        assert!(            from == caller || self.is_approved_for_all(&from, &caller),            "ERC1155: caller is not owner nor approved"        );
        self._safe_transfer_from(from, to, id, amount, data)    }

safe_batch_transfer_from#

Batched version of the safe_transfer_from

Emits a TransferBatch event.

Requirements:

  • ids and amounts must have the same length.
  • If to refers to a smart contract, it must implement on_erc1155_batch_received and return the acceptance magic value.
    fn safe_batch_transfer_from(        &mut self,        from: E::AccountId,        to: E::AccountId,        id: Vec<TokenId>,        amount: Vec<E::Balance>,        data: Vec<u8>,    ) -> Result<()> {        let caller = Self::caller();        assert!(            from == caller || self.is_approved_for_all(&from, &caller),            "ERC1155: transfer caller is not owner nor approved"        );
        self._safe_batch_transfer_from(from, to, id, amount, data)    }

Immutable Message#

url#

Returns the URI for token type id.

This implementation returns the same URI for all token types. It relies on the token type ID substitution mechanism defined in the EIP.

Clients calling this function must replace the \{id\} substring with the actual token type ID.

    fn url(&self, _id: TokenId) -> String {        self.get().get_url()    }

balance_of#

Returns the amount of tokens of token type id owned by account.

Requirements:

  • account cannot be the zero address.
    fn balance_of(&self, account: &E::AccountId, id: &TokenId) -> E::Balance {        assert!(            *account != E::AccountId::default(),            "ERC1155: balance query for the zero address"        );
        self.get().balance_of(id, account)    }

balance_of_batch#

Batched version of balance_of

Requirements:

  • accounts and ids must have the same length.
    fn balance_of_batch(        &self,        accounts: Vec<E::AccountId>,        ids: Vec<TokenId>,    ) -> Vec<E::Balance> {        assert!(            accounts.len() == ids.len(),            "ERC1155: accounts and ids length mismatch"        );
        (0..accounts.len())            .collect::<Vec<_>>()            .iter()            .map(|idx| {                self.balance_of(accounts.get(*idx).unwrap(), ids.get(*idx).unwrap())            })            .collect()    }

is_approved_for_all#

Returns true if operator is approved to transfer account's tokens.

See set_approval_for_all.

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

Internal Functions#

_mint#

Creates amount tokens of token type id, and assigns them to account.

Emits a TransferSingle event.

Requirements:

  • account cannot be the zero address.
  • If account refers to a smart contract, it must implement on_erc1155_received and return the acceptance magic value.
    fn _mint(        &mut self,        account: E::AccountId,        id: TokenId,        amount: E::Balance,        data: Vec<u8>,    ) -> Result<()> {        if account == E::AccountId::default() {            return Err(Error::AccountIsZero)        }
        let operator = Self::caller();
        self._before_token_transfer(            &operator,            &None,            &Some(&account),            &vec![id],            &vec![amount],            &data,        )?;
        self.get_mut().add_balance(&account, &id, amount);        self._do_safe_transfer_acceptance_check(            &operator, &None, &account, &id, &amount, &data,        );
        self.emit_event_transfer_single(operator, None, Some(account), id, amount);
        Ok(())    }

_mint_batch#

Batched version of _mint.

Requirements:

  • ids and amounts must have the same length.
  • If to refers to a smart contract, it must implement on_erc1155_batch_received and return the acceptance magic value.
    fn _mint_batch(        &mut self,        to: E::AccountId,        ids: Vec<TokenId>,        amounts: Vec<E::Balance>,        data: Vec<u8>,    ) -> Result<()> {        assert!(            ids.len() == amounts.len(),            "ERC1155: ids and amounts length mismatch"        );
        if to == E::AccountId::default() {            return Err(Error::AccountIsZero)        }
        let operator = Self::caller();
        self._before_token_transfer(&operator, &None, &Some(&to), &ids, &amounts, &data)?;
        for i in 0..ids.len() {            let id = ids[i];            let amount = amounts[i];
            self.get_mut().add_balance(&to, &id, amount);        }
        self._do_safe_batch_transfer_acceptance_check(            &operator, &None, &to, &ids, &amounts, &data,        );        self.emit_event_transfer_batch(operator, None, Some(to), ids, amounts);
        Ok(())    }

_burn#

Destroys amount tokens of token type id from account

Requirements:

  • account cannot be the zero address.
  • account must have at least amount tokens of token type id.
    fn _burn(        &mut self,        account: E::AccountId,        id: TokenId,        amount: E::Balance,    ) -> Result<()> {        if account == E::AccountId::default() {            return Err(Error::AccountIsZero)        }
        let operator = Self::caller();
        self._before_token_transfer(            &operator,            &Some(&account),            &None,            &vec![id],            &vec![amount],            &Vec::<u8>::default(),        )?;
        let account_balance = self.get().balance_of(&id, &account);        assert!(            account_balance >= amount,            "ERC1155: burn amount exceeds balance"        );        self.get_mut()            .set_balance(&account, &id, account_balance - amount);
        self.emit_event_transfer_single(operator, Some(account), None, id, amount);
        Ok(())    }

_burn_batch#

Batched version of _burn.

Requirements:

  • ids and amounts must have the same length.
    fn _burn_batch(        &mut self,        account: E::AccountId,        ids: Vec<TokenId>,        amounts: Vec<E::Balance>,    ) -> Result<()> {        assert!(            ids.len() == amounts.len(),            "ERC1155: ids and amounts length mismatch"        );
        if account == E::AccountId::default() {            return Err(Error::AccountIsZero)        }
        let operator = Self::caller();
        self._before_token_transfer(            &operator,            &Some(&account),            &None,            &ids,            &amounts,            &Vec::<u8>::default(),        )?;
        for i in 0..ids.len() {            let id = ids[i];            let amount = amounts[i];
            let account_balance = self.get().balance_of(&id, &account);            assert!(                account_balance >= amount,                "ERC1155: burn amount exceeds balance"            );            self.get_mut()                .set_balance(&account, &id, account_balance - amount);        }
        self.emit_event_transfer_batch(operator, Some(account), None, ids, amounts);
        Ok(())    }

Hooks#

_before_token_transfer#

Hook that is called before any token transfer. This includes minting and burning, as well as batched variants.

The same hook is called on both single and batched variants. For single transfers, the length of the id and amount arrays will be 1.

Calling conditions (for each id and amount pair):

  • When from and to are both non-zero, amount of from's tokens of token type id will be transferred to to.
  • When from is zero, amount tokens of token type id will be minted for to.
  • when to is zero, amount of from's tokens of token type id will be burned.
  • from and to are never both zero.
  • ids and amounts have the same, non-zero length.
    fn _before_token_transfer(        &mut self,        _operator: &E::AccountId,        _from: &Option<&E::AccountId>,        _to: &Option<&E::AccountId>,        _ids: &Vec<TokenId>,        _amounts: &Vec<E::Balance>,        _data: &Vec<u8>,    ) -> Result<()> {        Ok(())    }

Events#

TransferSingle#

Emitted when value tokens of token type id are transferred from from to to by operator.

    #[ink(event)]    #[metis(erc1155)]    pub struct TransferSingle {        #[ink(topic)]        pub operator: AccountId,        #[ink(topic)]        pub from: Option<AccountId>,        #[ink(topic)]        pub to: Option<AccountId>,        pub id: TokenId,        pub value: Balance,    }

TransferBatch#

Equivalent to multiple TransferSingle events, where operator, from and to are the same for all transfers.

    #[ink(event)]    #[metis(erc1155)]    pub struct TransferBatch {        #[ink(topic)]        pub operator: AccountId,        #[ink(topic)]        pub from: Option<AccountId>,        #[ink(topic)]        pub to: Option<AccountId>,        pub id: Vec<TokenId>,        pub value: Vec<Balance>,    }

ApprovalForAll#

Emitted when owner enables or disables (approved) operator to manage all of its assets.

    #[ink(event)]    #[metis(erc1155)]    pub struct ApprovalForAll {        #[ink(topic)]        pub owner: AccountId,        #[ink(topic)]        pub operator: AccountId,        pub approved: bool,    }

Url#

Emitted when the URI for token type id changes to value, if it is a non-programmatic URI.

If an URI event was emitted for id, the standard eip-1155#metadata-extensions that value will equal the value returned by uri.

    #[ink(event)]    #[metis(erc1155)]    pub struct Url {        pub value: String,        #[ink(topic)]        pub id: TokenId,    }

Extensions#

ERC1155Pausable#

ERC1155 token with pausable token transfers, minting and burning.

Useful for scenarios such as preventing trades until the end of an evaluation period, or having an emergency switch for freezing all token transfers in the event of a large bug.

FUNCTIONS

_beforeTokenTransfer(operator, from, to, ids, amounts, data)

For details, please refer ERC20Pausable for source code.

Usage examples can be found here

ERC1155Burnable#

Extension of ERC1155 that allows token holders to destroy both their own tokens and those that they have been approved to use.

FUNCTIONS

FUNCTIONS

burn(account, id, value)

burnBatch(account, ids, values)

For details, please refer ERC20Burnable for source code.

Usage examples can be found here

Usage Example#

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

#![cfg_attr(not(feature = "std"), no_std)]
#[metis_lang::contract]pub mod contract {    pub use erc1155::{        Error,        Result,        TokenId,    };    use metis_erc1155 as erc1155;    use metis_lang::{        import,        metis,    };
    #[ink(storage)]    #[import(erc1155)]    pub struct Erc1155 {        erc1155: erc1155::Data<Erc1155>,    }
    impl erc1155::Impl<Erc1155> for Erc1155 {}}

Then add the event for erc1155:

    /// Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.    #[ink(event)]    #[metis(erc1155)]    pub struct TransferSingle {        #[ink(topic)]        pub operator: AccountId,        #[ink(topic)]        pub from: Option<AccountId>,        #[ink(topic)]        pub to: Option<AccountId>,        pub id: TokenId,        pub value: Balance,    }
    /// Equivalent to multiple `TransferSingle` events, where `operator`, `from` and `to` are the same for all    /// transfers.    #[ink(event)]    #[metis(erc1155)]    pub struct TransferBatch {        #[ink(topic)]        pub operator: AccountId,        #[ink(topic)]        pub from: Option<AccountId>,        #[ink(topic)]        pub to: Option<AccountId>,        pub id: Vec<TokenId>,        pub value: Vec<Balance>,    }
    /// Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.    #[ink(event)]    #[metis(erc1155)]    pub struct ApprovalForAll {        #[ink(topic)]        pub owner: AccountId,        #[ink(topic)]        pub operator: AccountId,        pub approved: bool,    }
    /// Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.    ///    /// If an `URI` event was emitted for `id`, the standard    /// https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value    /// returned by `uri`.    #[ink(event)]    #[metis(erc1155)]    pub struct Url {        pub value: String,        #[ink(topic)]        pub id: TokenId,    }

impl the constructor for contract:

    impl Erc1155 {        #[ink(constructor)]        pub fn new(url: String) -> Self {            let mut instance = Self {                erc1155: erc1155::Data::new(),            };
            erc1155::Impl::init(&mut instance, url);            instance        }    }

Then implement the messages for contract:

    impl Erc1155 {        /// Returns the URI for token type `id`.        ///        /// This implementation returns the same URI for *all* token types. It relies        /// on the token type ID substitution mechanism        /// https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].        ///        /// Clients calling this function must replace the `\{id\}` substring with the        /// actual token type ID.        #[ink(message)]        pub fn url(&self, id: TokenId) -> String {            erc1155::Impl::url(self, id)        }
        /// Returns the amount of tokens of token type `id` owned by `account`.        ///        /// Requirements:        ///        /// - `account` cannot be the zero address.        #[ink(message)]        pub fn balance_of(&self, account: &AccountId, id: &TokenId) -> Balance {            erc1155::Impl::balance_of(self, account, id)        }
        /// Batched version of balance_of        ///        /// Requirements:        ///        /// - `accounts` and `ids` must have the same length.        #[ink(message)]        pub fn balance_of_batch(            &self,            accounts: Vec<AccountId>,            ids: Vec<TokenId>,        ) -> Vec<Balance> {            erc1155::Impl::balance_of_batch(self, accounts, ids)        }
        /// Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,        ///         /// Emits an `ApprovalForAll` event.        ///         /// Requirements:        ///         /// - `operator` cannot be the caller.        #[ink(message)]        pub fn set_approval_for_all(&mut self, operator: AccountId, approved: bool) {            erc1155::Impl::set_approval_for_all(self, operator, approved)        }
        /// Returns true if `operator` is approved to transfer ``account``'s tokens.        ///         /// See `set_approval_for_all`.        #[ink(message)]        pub fn is_approved_for_all(            &self,            account: &AccountId,            operator: &AccountId,        ) -> bool {            erc1155::Impl::is_approved_for_all(self, account, operator)        }
        /// Transfers `amount` tokens of token type `id` from `from` to `to`.        ///        /// Emits a `TransferSingle` event.        ///        /// Requirements:        ///        /// - `to` cannot be the zero address.        /// - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via `set_approval_for_all`.        /// - `from` must have a balance of tokens of type `id` of at least `amount`.        /// - If `to` refers to a smart contract, it must implement `on_erc1155_received` and return the        ///   acceptance magic value.        #[ink(message)]        pub fn safe_transfer_from(            &mut self,            from: AccountId,            to: AccountId,            id: TokenId,            amount: Balance,            data: Vec<u8>,        ) -> Result<()> {            erc1155::Impl::safe_transfer_from(self, from, to, id, amount, data)        }
        /// Batched version of the `safe_transfer_from`        ///        /// Emits a `TransferBatch` event.        ///        /// Requirements:        ///        /// - `ids` and `amounts` must have the same length.        /// - If `to` refers to a smart contract, it must implement `on_erc1155_batch_received` and return the        ///   acceptance magic value.        #[ink(message)]        pub fn safe_batch_transfer_from(            &mut self,            from: AccountId,            to: AccountId,            ids: Vec<TokenId>,            amounts: Vec<Balance>,            data: Vec<u8>,        ) -> Result<()> {            erc1155::Impl::safe_batch_transfer_from(self, from, to, ids, amounts, data)        }    }

In the end, we can add some other messages.