ERC1155
Details of ERC1155 can be found in ERC1155
#
Dependencymetis_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_allGrants 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_fromTransfers 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 spendfrom
's tokens viaset_approval_for_all
. from
must have a balance of tokens of typeid
of at leastamount
.- If
to
refers to a smart contract, it must implementon_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_fromBatched version of the safe_transfer_from
Emits a TransferBatch
event.
Requirements:
ids
andamounts
must have the same length.- If
to
refers to a smart contract, it must implementon_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#
urlReturns 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_ofReturns 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_batchBatched version of balance_of
Requirements:
accounts
andids
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_allReturns 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#
_mintCreates 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 implementon_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_batchBatched version of _mint
.
Requirements:
ids
andamounts
must have the same length.- If
to
refers to a smart contract, it must implementon_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(()) }
#
_burnDestroys amount
tokens of token type id
from account
Requirements:
account
cannot be the zero address.account
must have at leastamount
tokens 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_batchBatched version of _burn
.
Requirements:
ids
andamounts
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_transferHook 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
andto
are both non-zero,amount
offrom
's tokens of token typeid
will be transferred toto
. - When
from
is zero,amount
tokens of token typeid
will be minted forto
. - when
to
is zero,amount
offrom
's tokens of token typeid
will be burned. from
andto
are never both zero.ids
andamounts
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#
TransferSingleEmitted 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, }
#
TransferBatchEquivalent 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>, }
#
ApprovalForAllEmitted 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, }
#
UrlEmitted 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#
ERC1155PausableERC1155 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
#
ERC1155BurnableExtension 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 ExampleTo 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.