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:
operatorcannot 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:
tocannot be the zero address.- If the caller is not
from, it must be have been approved to spendfrom's tokens viaset_approval_for_all. frommust have a balance of tokens of typeidof at leastamount.- If
torefers to a smart contract, it must implementon_erc1155_receivedand 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:
idsandamountsmust have the same length.- If
torefers to a smart contract, it must implementon_erc1155_batch_receivedand 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:
accountcannot 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:
accountsandidsmust 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:
accountcannot be the zero address.- If
accountrefers to a smart contract, it must implementon_erc1155_receivedand 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:
idsandamountsmust have the same length.- If
torefers to a smart contract, it must implementon_erc1155_batch_receivedand 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:
accountcannot be the zero address.accountmust have at leastamounttokens of token typeid.
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:
idsandamountsmust 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
fromandtoare both non-zero,amountoffrom's tokens of token typeidwill be transferred toto. - When
fromis zero,amounttokens of token typeidwill be minted forto. - when
tois zero,amountoffrom's tokens of token typeidwill be burned. fromandtoare never both zero.idsandamountshave 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.