Module Overview
Module Name auction_nft_contract
Module Reference ba5d3e5871
Verification Status Verified
Age 267 days
Sender DjookyX
More Info
Deployed in block 18,056,423
Deployed in tx 6169
Methods 9
Contracts 1
Module Build Info
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.
Module Source Code
//! 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, &params.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,
                &parameter,
                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, &param.operator, builder),
            OperatorUpdate::Remove => state.remove_operator(&sender, &param.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(())
}
Module Schema
Initialization

Parameters
None
Errors
None
Event
{
  "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>"
      }
    }
  ]
}
Methods

Parameters
None
Errors
None
Return
{
  "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>"
        ]
      }
    ]
  ]
}

Parameters
{
  "owner": {
    "Enum": [
      {
        "Account": [
          "<AccountAddress>"
        ]
      },
      {
        "Contract": [
          {
            "index": "<UInt64>",
            "subindex": "<UInt64>"
          }
        ]
      }
    ]
  },
  "tokens": [
    "<String with lowercase hex>"
  ]
}
Errors
{
  "Enum": [
    {
      "InvalidTokenId": []
    },
    {
      "InsufficientFunds": []
    },
    {
      "Unauthorized": []
    },
    {
      "Custom": [
        {
          "Enum": [
            {
              "ParseParams": []
            },
            {
              "LogFull": []
            },
            {
              "LogMalformed": []
            },
            {
              "TokenIdAlreadyExists": []
            },
            {
              "InvokeContractError": []
            }
          ]
        }
      ]
    }
  ]
}
Return
None

Parameters
[
  {
    "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>"
  }
]
Errors
{
  "Enum": [
    {
      "InvalidTokenId": []
    },
    {
      "InsufficientFunds": []
    },
    {
      "Unauthorized": []
    },
    {
      "Custom": [
        {
          "Enum": [
            {
              "ParseParams": []
            },
            {
              "LogFull": []
            },
            {
              "LogMalformed": []
            },
            {
              "TokenIdAlreadyExists": []
            },
            {
              "InvokeContractError": []
            }
          ]
        }
      ]
    }
  ]
}
Return
None

Parameters
[
  {
    "operator": {
      "Enum": [
        {
          "Account": [
            "<AccountAddress>"
          ]
        },
        {
          "Contract": [
            {
              "index": "<UInt64>",
              "subindex": "<UInt64>"
            }
          ]
        }
      ]
    },
    "update": {
      "Enum": [
        {
          "Remove": []
        },
        {
          "Add": []
        }
      ]
    }
  }
]
Errors
{
  "Enum": [
    {
      "InvalidTokenId": []
    },
    {
      "InsufficientFunds": []
    },
    {
      "Unauthorized": []
    },
    {
      "Custom": [
        {
          "Enum": [
            {
              "ParseParams": []
            },
            {
              "LogFull": []
            },
            {
              "LogMalformed": []
            },
            {
              "TokenIdAlreadyExists": []
            },
            {
              "InvokeContractError": []
            }
          ]
        }
      ]
    }
  ]
}
Return
None

Parameters
[
  {
    "address": {
      "Enum": [
        {
          "Account": [
            "<AccountAddress>"
          ]
        },
        {
          "Contract": [
            {
              "index": "<UInt64>",
              "subindex": "<UInt64>"
            }
          ]
        }
      ]
    },
    "owner": {
      "Enum": [
        {
          "Account": [
            "<AccountAddress>"
          ]
        },
        {
          "Contract": [
            {
              "index": "<UInt64>",
              "subindex": "<UInt64>"
            }
          ]
        }
      ]
    }
  }
]
Errors
{
  "Enum": [
    {
      "InvalidTokenId": []
    },
    {
      "InsufficientFunds": []
    },
    {
      "Unauthorized": []
    },
    {
      "Custom": [
        {
          "Enum": [
            {
              "ParseParams": []
            },
            {
              "LogFull": []
            },
            {
              "LogMalformed": []
            },
            {
              "TokenIdAlreadyExists": []
            },
            {
              "InvokeContractError": []
            }
          ]
        }
      ]
    }
  ]
}
Return
[
  "<Bool>"
]

Parameters
[
  {
    "address": {
      "Enum": [
        {
          "Account": [
            "<AccountAddress>"
          ]
        },
        {
          "Contract": [
            {
              "index": "<UInt64>",
              "subindex": "<UInt64>"
            }
          ]
        }
      ]
    },
    "token_id": "<String with lowercase hex>"
  }
]
Errors
{
  "Enum": [
    {
      "InvalidTokenId": []
    },
    {
      "InsufficientFunds": []
    },
    {
      "Unauthorized": []
    },
    {
      "Custom": [
        {
          "Enum": [
            {
              "ParseParams": []
            },
            {
              "LogFull": []
            },
            {
              "LogMalformed": []
            },
            {
              "TokenIdAlreadyExists": []
            },
            {
              "InvokeContractError": []
            }
          ]
        }
      ]
    }
  ]
}
Return
[
  "<String of size at most 74 containing an unsigned integer.>"
]

Parameters
[
  "<String with lowercase hex>"
]
Errors
{
  "Enum": [
    {
      "InvalidTokenId": []
    },
    {
      "InsufficientFunds": []
    },
    {
      "Unauthorized": []
    },
    {
      "Custom": [
        {
          "Enum": [
            {
              "ParseParams": []
            },
            {
              "LogFull": []
            },
            {
              "LogMalformed": []
            },
            {
              "TokenIdAlreadyExists": []
            },
            {
              "InvokeContractError": []
            }
          ]
        }
      ]
    }
  ]
}
Return
[
  {
    "hash": {
      "Enum": [
        {
          "None": []
        },
        {
          "Some": [
            "<String of size 64 containing lowercase hex characters.>"
          ]
        }
      ]
    },
    "url": "<String>"
  }
]

Parameters
[
  "<String>"
]
Errors
{
  "Enum": [
    {
      "InvalidTokenId": []
    },
    {
      "InsufficientFunds": []
    },
    {
      "Unauthorized": []
    },
    {
      "Custom": [
        {
          "Enum": [
            {
              "ParseParams": []
            },
            {
              "LogFull": []
            },
            {
              "LogMalformed": []
            },
            {
              "TokenIdAlreadyExists": []
            },
            {
              "InvokeContractError": []
            }
          ]
        }
      ]
    }
  ]
}
Return
[
  {
    "Enum": [
      {
        "NoSupport": []
      },
      {
        "Support": []
      },
      {
        "SupportBy": [
          [
            {
              "index": "<UInt64>",
              "subindex": "<UInt64>"
            }
          ]
        ]
      }
    ]
  }
]

Parameters
{
  "id": "<String>",
  "implementors": [
    {
      "index": "<UInt64>",
      "subindex": "<UInt64>"
    }
  ]
}
Errors
{
  "Enum": [
    {
      "InvalidTokenId": []
    },
    {
      "InsufficientFunds": []
    },
    {
      "Unauthorized": []
    },
    {
      "Custom": [
        {
          "Enum": [
            {
              "ParseParams": []
            },
            {
              "LogFull": []
            },
            {
              "LogMalformed": []
            },
            {
              "TokenIdAlreadyExists": []
            },
            {
              "InvokeContractError": []
            }
          ]
        }
      ]
    }
  ]
}
Return
None
Module Usage