Module Name | auction_nft_contract |
Module Reference | ba5d3e5871 |
Verification Status | Verified |
Age | 265 days |
Sender | DjookyX |
Deployed in block | 18,056,423 |
Deployed in tx | 6169 |
Methods | 9 |
Contracts | 1 |
Verification Status: | Verified |
Build Image Used: | docker.io/concordium/verifiable-sc:1.73.0 |
Build Command Used: | cargo --locked build --target wasm32-unknown-unknown --release --target-dir /b/t |
Archive Hash: | 75e9abf1149f68d13c22c56a8d2b01b31cfadab1a64bee5e466676557f5bd5f7 |
Link to Source Code: | Source Code |
Explanation: | Source and module match. |
//! A NFT smart contract example using the Concordium Token Standard CIS2.
//!
//! # Description
//! An instance of this smart contract can contain a number of different token
//! each identified by a token ID. A token is then globally identified by the
//! contract address together with the token ID.
//!
//! In this example the contract is initialized with no tokens, and tokens can
//! be minted through a `mint` contract function, which will only succeed for
//! the contract owner. No functionality to burn token is defined in this
//! example.
//!
//! Note: The word 'address' refers to either an account address or a
//! contract address.
//!
//! As follows from the CIS2 specification, the contract has a `transfer`
//! function for transferring an amount of a specific token type from one
//! address to another address. An address can enable and disable one or more
//! addresses as operators. An operator of some address is allowed to transfer
//! any tokens owned by this address.
//!
//! Tests are located in `./tests/tests.rs`.
#![cfg_attr(not(feature = "std"), no_std)]
use concordium_cis2::*;
use concordium_std::*;
/// The baseurl for the token metadata, gets appended with the token ID as hex
/// encoding before emitted in the TokenMetadata event.
pub const TOKEN_METADATA_BASE_URL: &str = "https://ipfs.io/ipfs/QmYGDWa3evqaqa2eMrtPnwN2SWFRvf8HC6u87fE4BJ1eUY/";
/// List of supported standards by this contract address.
pub const SUPPORTS_STANDARDS: [StandardIdentifier<'static>; 2] =
[CIS0_STANDARD_IDENTIFIER, CIS2_STANDARD_IDENTIFIER];
// Types
/// Contract token ID type.
/// To save bytes we use a token ID type limited to a `u32`.
pub type ContractTokenId = TokenIdU32;
/// Contract token amount.
/// Since the tokens are non-fungible the total supply of any token will be at
/// most 1 and it is fine to use a small type for representing token amounts.
pub type ContractTokenAmount = TokenAmountU8;
/// The parameter for the contract function `mint` which mints a number of
/// tokens to a given address.
#[derive(Serial, Deserial, SchemaType)]
pub struct MintParams {
/// Owner of the newly minted tokens.
pub owner: Address,
/// A collection of tokens to mint.
#[concordium(size_length = 1)]
pub tokens: collections::BTreeSet<ContractTokenId>,
}
/// The state for each address.
#[derive(Serial, DeserialWithState, Deletable)]
#[concordium(state_parameter = "S")]
pub struct AddressState<S = StateApi> {
/// The tokens owned by this address.
pub owned_tokens: StateSet<ContractTokenId, S>,
/// The address which are currently enabled as operators for this address.
pub operators: StateSet<Address, S>,
}
impl AddressState {
fn empty(state_builder: &mut StateBuilder) -> Self {
AddressState {
owned_tokens: state_builder.new_set(),
operators: state_builder.new_set(),
}
}
}
/// The contract state.
// Note: The specification does not specify how to structure the contract state
// and this could be structured in a more space efficient way depending on the use case.
#[derive(Serial, DeserialWithState)]
#[concordium(state_parameter = "S")]
pub struct State<S = StateApi> {
/// The state for each address.
pub state: StateMap<Address, AddressState<S>, S>,
/// All of the token IDs
pub all_tokens: StateSet<ContractTokenId, S>,
/// Map with contract addresses providing implementations of additional
/// standards.
pub implementors: StateMap<StandardIdentifierOwned, Vec<ContractAddress>, S>,
}
/// The parameter type for the contract function `setImplementors`.
/// Takes a standard identifier and list of contract addresses providing
/// implementations of this standard.
#[derive(Debug, Serialize, SchemaType)]
pub struct SetImplementorsParams {
/// The identifier for the standard.
pub id: StandardIdentifierOwned,
/// The addresses of the implementors of the standard.
pub implementors: Vec<ContractAddress>,
}
/// The custom errors the contract can produce.
#[derive(Serialize, Debug, PartialEq, Eq, Reject, SchemaType)]
pub enum CustomContractError {
/// Failed parsing the parameter.
#[from(ParseError)]
ParseParams,
/// Failed logging: Log is full.
LogFull,
/// Failed logging: Log is malformed.
LogMalformed,
/// Failing to mint new tokens because one of the token IDs already exists
/// in this contract.
TokenIdAlreadyExists,
/// Failed to invoke a contract.
InvokeContractError,
}
/// Wrapping the custom errors in a type with CIS2 errors.
pub type ContractError = Cis2Error<CustomContractError>;
pub type ContractResult<A> = Result<A, ContractError>;
/// Mapping the logging errors to CustomContractError.
impl From<LogError> for CustomContractError {
fn from(le: LogError) -> Self {
match le {
LogError::Full => Self::LogFull,
LogError::Malformed => Self::LogMalformed,
}
}
}
/// Mapping errors related to contract invocations to CustomContractError.
impl<T> From<CallContractError<T>> for CustomContractError {
fn from(_cce: CallContractError<T>) -> Self { Self::InvokeContractError }
}
/// Mapping CustomContractError to ContractError
impl From<CustomContractError> for ContractError {
fn from(c: CustomContractError) -> Self { Cis2Error::Custom(c) }
}
// Functions for creating, updating and querying the contract state.
impl State {
/// Creates a new state with no tokens.
fn empty(state_builder: &mut StateBuilder) -> Self {
State {
state: state_builder.new_map(),
all_tokens: state_builder.new_set(),
implementors: state_builder.new_map(),
}
}
/// Mint a new token with a given address as the owner
fn mint(
&mut self,
token: ContractTokenId,
owner: &Address,
state_builder: &mut StateBuilder,
) -> ContractResult<()> {
ensure!(self.all_tokens.insert(token), CustomContractError::TokenIdAlreadyExists.into());
let mut owner_state =
self.state.entry(*owner).or_insert_with(|| AddressState::empty(state_builder));
owner_state.owned_tokens.insert(token);
Ok(())
}
/// Check that the token ID currently exists in this contract.
#[inline(always)]
fn contains_token(&self, token_id: &ContractTokenId) -> bool {
self.all_tokens.contains(token_id)
}
/// Get the current balance of a given token ID for a given address.
/// Results in an error if the token ID does not exist in the state.
/// Since this contract only contains NFTs, the balance will always be
/// either 1 or 0.
fn balance(
&self,
token_id: &ContractTokenId,
address: &Address,
) -> ContractResult<ContractTokenAmount> {
ensure!(self.contains_token(token_id), ContractError::InvalidTokenId);
let balance = self
.state
.get(address)
.map(|address_state| u8::from(address_state.owned_tokens.contains(token_id)))
.unwrap_or(0);
Ok(balance.into())
}
/// Check if a given address is an operator of a given owner address.
fn is_operator(&self, address: &Address, owner: &Address) -> bool {
self.state
.get(owner)
.map(|address_state| address_state.operators.contains(address))
.unwrap_or(false)
}
/// Update the state with a transfer of some token.
/// Results in an error if the token ID does not exist in the state or if
/// the from address have insufficient tokens to do the transfer.
fn transfer(
&mut self,
token_id: &ContractTokenId,
amount: ContractTokenAmount,
from: &Address,
to: &Address,
state_builder: &mut StateBuilder,
) -> ContractResult<()> {
ensure!(self.contains_token(token_id), ContractError::InvalidTokenId);
// A zero transfer does not modify the state.
if amount == 0.into() {
return Ok(());
}
// Since this contract only contains NFTs, no one will have an amount greater
// than 1. And since the amount cannot be the zero at this point, the
// address must have insufficient funds for any amount other than 1.
ensure_eq!(amount, 1.into(), ContractError::InsufficientFunds);
{
let mut from_address_state =
self.state.get_mut(from).ok_or(ContractError::InsufficientFunds)?;
// Find and remove the token from the owner, if nothing is removed, we know the
// address did not own the token..
let from_had_the_token = from_address_state.owned_tokens.remove(token_id);
ensure!(from_had_the_token, ContractError::InsufficientFunds);
}
// Add the token to the new owner.
let mut to_address_state =
self.state.entry(*to).or_insert_with(|| AddressState::empty(state_builder));
to_address_state.owned_tokens.insert(*token_id);
Ok(())
}
/// Update the state adding a new operator for a given address.
/// Succeeds even if the `operator` is already an operator for the
/// `address`.
fn add_operator(
&mut self,
owner: &Address,
operator: &Address,
state_builder: &mut StateBuilder,
) {
let mut owner_state =
self.state.entry(*owner).or_insert_with(|| AddressState::empty(state_builder));
owner_state.operators.insert(*operator);
}
/// Update the state removing an operator for a given address.
/// Succeeds even if the `operator` is _not_ an operator for the `address`.
fn remove_operator(&mut self, owner: &Address, operator: &Address) {
self.state.entry(*owner).and_modify(|address_state| {
address_state.operators.remove(operator);
});
}
/// Check if state contains any implementors for a given standard.
fn have_implementors(&self, std_id: &StandardIdentifierOwned) -> SupportResult {
if let Some(addresses) = self.implementors.get(std_id) {
SupportResult::SupportBy(addresses.to_vec())
} else {
SupportResult::NoSupport
}
}
/// Set implementors for a given standard.
fn set_implementors(
&mut self,
std_id: StandardIdentifierOwned,
implementors: Vec<ContractAddress>,
) {
let _ = self.implementors.insert(std_id, implementors);
}
}
/// Build a string from TOKEN_METADATA_BASE_URL appended with the token ID
/// encoded as hex.
fn build_token_metadata_url(token_id: &ContractTokenId) -> String {
let mut token_metadata_url = String::from(TOKEN_METADATA_BASE_URL);
token_metadata_url
}
// Contract functions
/// Initialize contract instance with no token types initially.
#[init(contract = "auction_nft_contract", event = "Cis2Event<ContractTokenId, ContractTokenAmount>")]
fn contract_init(_ctx: &InitContext, state_builder: &mut StateBuilder) -> InitResult<State> {
// Construct the initial contract state.
Ok(State::empty(state_builder))
}
#[derive(Serialize, SchemaType, PartialEq, Eq, Debug)]
pub struct ViewAddressState {
pub owned_tokens: Vec<ContractTokenId>,
pub operators: Vec<Address>,
}
#[derive(Serialize, SchemaType, PartialEq, Eq, Debug)]
pub struct ViewState {
pub state: Vec<(Address, ViewAddressState)>,
pub all_tokens: Vec<ContractTokenId>,
}
/// View function that returns the entire contents of the state. Meant for
/// testing.
#[receive(contract = "auction_nft_contract", name = "view", return_value = "ViewState")]
fn contract_view(_ctx: &ReceiveContext, host: &Host<State>) -> ReceiveResult<ViewState> {
let state = host.state();
let mut inner_state = Vec::new();
for (k, a_state) in state.state.iter() {
let owned_tokens = a_state.owned_tokens.iter().map(|x| *x).collect();
let operators = a_state.operators.iter().map(|x| *x).collect();
inner_state.push((*k, ViewAddressState {
owned_tokens,
operators,
}));
}
let all_tokens = state.all_tokens.iter().map(|x| *x).collect();
Ok(ViewState {
state: inner_state,
all_tokens,
})
}
/// Mint new tokens with a given address as the owner of these tokens.
/// Can only be called by the contract owner.
/// Logs a `Mint` and a `TokenMetadata` event for each token.
/// The url for the token metadata is the token ID encoded in hex, appended on
/// the `TOKEN_METADATA_BASE_URL`.
///
/// It rejects if:
/// - The sender is not the contract instance owner.
/// - Fails to parse parameter.
/// - Any of the tokens fails to be minted, which could be if:
/// - The minted token ID already exists.
/// - Fails to log Mint event
/// - Fails to log TokenMetadata event
///
/// Note: Can at most mint 32 token types in one call due to the limit on the
/// number of logs a smart contract can produce on each function call.
#[receive(
contract = "auction_nft_contract",
name = "mint",
parameter = "MintParams",
error = "ContractError",
enable_logger,
mutable
)]
fn contract_mint(
ctx: &ReceiveContext,
host: &mut Host<State>,
logger: &mut impl HasLogger,
) -> ContractResult<()> {
// Get the contract owner
let owner = ctx.owner();
// Get the sender of the transaction
let sender = ctx.sender();
ensure!(sender.matches_account(&owner), ContractError::Unauthorized);
// Parse the parameter.
let params: MintParams = ctx.parameter_cursor().get()?;
let (state, builder) = host.state_and_builder();
for &token_id in params.tokens.iter() {
// Mint the token in the state.
state.mint(token_id, ¶ms.owner, builder)?;
// Event for minted NFT.
logger.log(&Cis2Event::Mint(MintEvent {
token_id,
amount: ContractTokenAmount::from(1),
owner: params.owner,
}))?;
// Metadata URL for the NFT.
logger.log(&Cis2Event::TokenMetadata::<_, ContractTokenAmount>(TokenMetadataEvent {
token_id,
metadata_url: MetadataUrl {
url: build_token_metadata_url(&token_id),
hash: None,
},
}))?;
}
Ok(())
}
type TransferParameter = TransferParams<ContractTokenId, ContractTokenAmount>;
/// Execute a list of token transfers, in the order of the list.
///
/// Logs a `Transfer` event and invokes a receive hook function for every
/// transfer in the list.
///
/// It rejects if:
/// - It fails to parse the parameter.
/// - Any of the transfers fail to be executed, which could be if:
/// - The `token_id` does not exist.
/// - The sender is not the owner of the token, or an operator for this
/// specific `token_id` and `from` address.
/// - The token is not owned by the `from`.
/// - Fails to log event.
/// - Any of the receive hook function calls rejects.
#[receive(
contract = "auction_nft_contract",
name = "transfer",
parameter = "TransferParameter",
error = "ContractError",
enable_logger,
mutable
)]
fn contract_transfer(
ctx: &ReceiveContext,
host: &mut Host<State>,
logger: &mut impl HasLogger,
) -> ContractResult<()> {
// Parse the parameter.
let TransferParams(transfers): TransferParameter = ctx.parameter_cursor().get()?;
// Get the sender who invoked this contract function.
let sender = ctx.sender();
for Transfer {
token_id,
amount,
from,
to,
data,
} in transfers
{
let (state, builder) = host.state_and_builder();
// Authenticate the sender for this transfer
ensure!(from == sender || state.is_operator(&sender, &from), ContractError::Unauthorized);
let to_address = to.address();
// Update the contract state
state.transfer(&token_id, amount, &from, &to_address, builder)?;
// Log transfer event
logger.log(&Cis2Event::Transfer(TransferEvent {
token_id,
amount,
from,
to: to_address,
}))?;
// If the receiver is a contract: invoke the receive hook function.
if let Receiver::Contract(address, function) = to {
let parameter = OnReceivingCis2Params {
token_id,
amount,
from,
data,
};
host.invoke_contract(
&address,
¶meter,
function.as_entrypoint_name(),
Amount::zero(),
)?;
}
}
Ok(())
}
/// Enable or disable addresses as operators of the sender address.
/// Logs an `UpdateOperator` event.
///
/// It rejects if:
/// - It fails to parse the parameter.
/// - Fails to log event.
#[receive(
contract = "auction_nft_contract",
name = "updateOperator",
parameter = "UpdateOperatorParams",
error = "ContractError",
enable_logger,
mutable
)]
fn contract_update_operator(
ctx: &ReceiveContext,
host: &mut Host<State>,
logger: &mut impl HasLogger,
) -> ContractResult<()> {
// Parse the parameter.
let UpdateOperatorParams(params) = ctx.parameter_cursor().get()?;
// Get the sender who invoked this contract function.
let sender = ctx.sender();
let (state, builder) = host.state_and_builder();
for param in params {
// Update the operator in the state.
match param.update {
OperatorUpdate::Add => state.add_operator(&sender, ¶m.operator, builder),
OperatorUpdate::Remove => state.remove_operator(&sender, ¶m.operator),
}
// Log the appropriate event
logger.log(&Cis2Event::<ContractTokenId, ContractTokenAmount>::UpdateOperator(
UpdateOperatorEvent {
owner: sender,
operator: param.operator,
update: param.update,
},
))?;
}
Ok(())
}
/// Takes a list of queries. Each query is an owner address and some address to
/// check as an operator of the owner address.
///
/// It rejects if:
/// - It fails to parse the parameter.
#[receive(
contract = "auction_nft_contract",
name = "operatorOf",
parameter = "OperatorOfQueryParams",
return_value = "OperatorOfQueryResponse",
error = "ContractError"
)]
fn contract_operator_of(
ctx: &ReceiveContext,
host: &Host<State>,
) -> ContractResult<OperatorOfQueryResponse> {
// Parse the parameter.
let params: OperatorOfQueryParams = ctx.parameter_cursor().get()?;
// Build the response.
let mut response = Vec::with_capacity(params.queries.len());
for query in params.queries {
// Query the state for address being an operator of owner.
let is_operator = host.state().is_operator(&query.address, &query.owner);
response.push(is_operator);
}
let result = OperatorOfQueryResponse::from(response);
Ok(result)
}
/// Parameter type for the CIS-2 function `balanceOf` specialized to the subset
/// of TokenIDs used by this contract.
type ContractBalanceOfQueryParams = BalanceOfQueryParams<ContractTokenId>;
/// Response type for the CIS-2 function `balanceOf` specialized to the subset
/// of TokenAmounts used by this contract.
type ContractBalanceOfQueryResponse = BalanceOfQueryResponse<ContractTokenAmount>;
/// Get the balance of given token IDs and addresses.
///
/// It rejects if:
/// - It fails to parse the parameter.
/// - Any of the queried `token_id` does not exist.
#[receive(
contract = "auction_nft_contract",
name = "balanceOf",
parameter = "ContractBalanceOfQueryParams",
return_value = "ContractBalanceOfQueryResponse",
error = "ContractError"
)]
fn contract_balance_of(
ctx: &ReceiveContext,
host: &Host<State>,
) -> ContractResult<ContractBalanceOfQueryResponse> {
// Parse the parameter.
let params: ContractBalanceOfQueryParams = ctx.parameter_cursor().get()?;
// Build the response.
let mut response = Vec::with_capacity(params.queries.len());
for query in params.queries {
// Query the state for balance.
let amount = host.state().balance(&query.token_id, &query.address)?;
response.push(amount);
}
let result = ContractBalanceOfQueryResponse::from(response);
Ok(result)
}
/// Parameter type for the CIS-2 function `tokenMetadata` specialized to the
/// subset of TokenIDs used by this contract.
type ContractTokenMetadataQueryParams = TokenMetadataQueryParams<ContractTokenId>;
/// Get the token metadata URLs and checksums given a list of token IDs.
///
/// It rejects if:
/// - It fails to parse the parameter.
/// - Any of the queried `token_id` does not exist.
#[receive(
contract = "auction_nft_contract",
name = "tokenMetadata",
parameter = "ContractTokenMetadataQueryParams",
return_value = "TokenMetadataQueryResponse",
error = "ContractError"
)]
fn contract_token_metadata(
ctx: &ReceiveContext,
host: &Host<State>,
) -> ContractResult<TokenMetadataQueryResponse> {
// Parse the parameter.
let params: ContractTokenMetadataQueryParams = ctx.parameter_cursor().get()?;
// Build the response.
let mut response = Vec::with_capacity(params.queries.len());
for token_id in params.queries {
// Check the token exists.
ensure!(host.state().contains_token(&token_id), ContractError::InvalidTokenId);
let metadata_url = MetadataUrl {
url: build_token_metadata_url(&token_id),
hash: None,
};
response.push(metadata_url);
}
let result = TokenMetadataQueryResponse::from(response);
Ok(result)
}
/// Get the supported standards or addresses for a implementation given list of
/// standard identifiers.
///
/// It rejects if:
/// - It fails to parse the parameter.
#[receive(
contract = "auction_nft_contract",
name = "supports",
parameter = "SupportsQueryParams",
return_value = "SupportsQueryResponse",
error = "ContractError"
)]
fn contract_supports(
ctx: &ReceiveContext,
host: &Host<State>,
) -> ContractResult<SupportsQueryResponse> {
// Parse the parameter.
let params: SupportsQueryParams = ctx.parameter_cursor().get()?;
// Build the response.
let mut response = Vec::with_capacity(params.queries.len());
for std_id in params.queries {
if SUPPORTS_STANDARDS.contains(&std_id.as_standard_identifier()) {
response.push(SupportResult::Support);
} else {
response.push(host.state().have_implementors(&std_id));
}
}
let result = SupportsQueryResponse::from(response);
Ok(result)
}
/// Set the addresses for an implementation given a standard identifier and a
/// list of contract addresses.
///
/// It rejects if:
/// - Sender is not the owner of the contract instance.
/// - It fails to parse the parameter.
#[receive(
contract = "auction_nft_contract",
name = "setImplementors",
parameter = "SetImplementorsParams",
error = "ContractError",
mutable
)]
fn contract_set_implementor(ctx: &ReceiveContext, host: &mut Host<State>) -> ContractResult<()> {
// Authorize the sender.
ensure!(ctx.sender().matches_account(&ctx.owner()), ContractError::Unauthorized);
// Parse the parameter.
let params: SetImplementorsParams = ctx.parameter_cursor().get()?;
// Update the implementors in the state
host.state_mut().set_implementors(params.id, params.implementors);
Ok(())
}
None
None
{
"Enum": [
{
"TokenMetadata": {
"metadata_url": {
"hash": {
"Enum": [
{
"None": []
},
{
"Some": [
"<String of size 64 containing lowercase hex characters.>"
]
}
]
},
"url": "<String>"
},
"token_id": "<String with lowercase hex>"
}
},
{
"UpdateOperator": {
"operator": {
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
]
},
"owner": {
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
]
},
"update": {
"Enum": [
{
"Remove": []
},
{
"Add": []
}
]
}
}
},
{
"Burn": {
"amount": "<String of size at most 74 containing an unsigned integer.>",
"owner": {
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
]
},
"token_id": "<String with lowercase hex>"
}
},
{
"Mint": {
"amount": "<String of size at most 74 containing an unsigned integer.>",
"owner": {
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
]
},
"token_id": "<String with lowercase hex>"
}
},
{
"Transfer": {
"amount": "<String of size at most 74 containing an unsigned integer.>",
"from": {
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
]
},
"to": {
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
]
},
"token_id": "<String with lowercase hex>"
}
}
]
}
None
None
{
"all_tokens": [
"<String with lowercase hex>"
],
"state": [
[
{
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
]
},
{
"operators": [
{
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
]
}
],
"owned_tokens": [
"<String with lowercase hex>"
]
}
]
]
}
{
"owner": {
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
]
},
"tokens": [
"<String with lowercase hex>"
]
}
{
"Enum": [
{
"InvalidTokenId": []
},
{
"InsufficientFunds": []
},
{
"Unauthorized": []
},
{
"Custom": [
{
"Enum": [
{
"ParseParams": []
},
{
"LogFull": []
},
{
"LogMalformed": []
},
{
"TokenIdAlreadyExists": []
},
{
"InvokeContractError": []
}
]
}
]
}
]
}
None
[
{
"amount": "<String of size at most 74 containing an unsigned integer.>",
"data": "<String with lowercase hex>",
"from": {
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
]
},
"to": {
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
},
"<String>"
]
}
]
},
"token_id": "<String with lowercase hex>"
}
]
{
"Enum": [
{
"InvalidTokenId": []
},
{
"InsufficientFunds": []
},
{
"Unauthorized": []
},
{
"Custom": [
{
"Enum": [
{
"ParseParams": []
},
{
"LogFull": []
},
{
"LogMalformed": []
},
{
"TokenIdAlreadyExists": []
},
{
"InvokeContractError": []
}
]
}
]
}
]
}
None
[
{
"operator": {
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
]
},
"update": {
"Enum": [
{
"Remove": []
},
{
"Add": []
}
]
}
}
]
{
"Enum": [
{
"InvalidTokenId": []
},
{
"InsufficientFunds": []
},
{
"Unauthorized": []
},
{
"Custom": [
{
"Enum": [
{
"ParseParams": []
},
{
"LogFull": []
},
{
"LogMalformed": []
},
{
"TokenIdAlreadyExists": []
},
{
"InvokeContractError": []
}
]
}
]
}
]
}
None
[
{
"address": {
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
]
},
"owner": {
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
]
}
}
]
{
"Enum": [
{
"InvalidTokenId": []
},
{
"InsufficientFunds": []
},
{
"Unauthorized": []
},
{
"Custom": [
{
"Enum": [
{
"ParseParams": []
},
{
"LogFull": []
},
{
"LogMalformed": []
},
{
"TokenIdAlreadyExists": []
},
{
"InvokeContractError": []
}
]
}
]
}
]
}
[
"<Bool>"
]
[
{
"address": {
"Enum": [
{
"Account": [
"<AccountAddress>"
]
},
{
"Contract": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
]
},
"token_id": "<String with lowercase hex>"
}
]
{
"Enum": [
{
"InvalidTokenId": []
},
{
"InsufficientFunds": []
},
{
"Unauthorized": []
},
{
"Custom": [
{
"Enum": [
{
"ParseParams": []
},
{
"LogFull": []
},
{
"LogMalformed": []
},
{
"TokenIdAlreadyExists": []
},
{
"InvokeContractError": []
}
]
}
]
}
]
}
[
"<String of size at most 74 containing an unsigned integer.>"
]
[
"<String with lowercase hex>"
]
{
"Enum": [
{
"InvalidTokenId": []
},
{
"InsufficientFunds": []
},
{
"Unauthorized": []
},
{
"Custom": [
{
"Enum": [
{
"ParseParams": []
},
{
"LogFull": []
},
{
"LogMalformed": []
},
{
"TokenIdAlreadyExists": []
},
{
"InvokeContractError": []
}
]
}
]
}
]
}
[
{
"hash": {
"Enum": [
{
"None": []
},
{
"Some": [
"<String of size 64 containing lowercase hex characters.>"
]
}
]
},
"url": "<String>"
}
]
[
"<String>"
]
{
"Enum": [
{
"InvalidTokenId": []
},
{
"InsufficientFunds": []
},
{
"Unauthorized": []
},
{
"Custom": [
{
"Enum": [
{
"ParseParams": []
},
{
"LogFull": []
},
{
"LogMalformed": []
},
{
"TokenIdAlreadyExists": []
},
{
"InvokeContractError": []
}
]
}
]
}
]
}
[
{
"Enum": [
{
"NoSupport": []
},
{
"Support": []
},
{
"SupportBy": [
[
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
]
}
]
}
]
{
"id": "<String>",
"implementors": [
{
"index": "<UInt64>",
"subindex": "<UInt64>"
}
]
}
{
"Enum": [
{
"InvalidTokenId": []
},
{
"InsufficientFunds": []
},
{
"Unauthorized": []
},
{
"Custom": [
{
"Enum": [
{
"ParseParams": []
},
{
"LogFull": []
},
{
"LogMalformed": []
},
{
"TokenIdAlreadyExists": []
},
{
"InvokeContractError": []
}
]
}
]
}
]
}
None