ERC-721 Enumerable

The OpenZeppelin ERC-721 Enumerable extension is used to provide additional functionality to the standard ERC-721 token. Specifically, it allows for enumeration of all the token IDs in the contract as well as all the token IDs owned by each account. This is useful for applications that need to list or iterate over tokens, such as marketplaces or wallets.

Usage

In order to make an ERC-721 token with Enumerable flavour, you need to create a specified contract as follows:

use openzeppelin_stylus::
    token::erc721::{
        self,
        extensions::{enumerable, Erc721Enumerable, IErc721Burnable,
        Erc721, IErc721,
    },
    utils::introspection::erc165::IErc165,
};

#[derive(SolidityError, Debug)]
enum Error
    OutOfBoundsIndex(enumerable::ERC721OutOfBoundsIndex),
    EnumerableForbiddenBatchMint(
        enumerable::ERC721EnumerableForbiddenBatchMint,
    ),
    InvalidOwner(erc721::ERC721InvalidOwner),
    NonexistentToken(erc721::ERC721NonexistentToken),
    IncorrectOwner(erc721::ERC721IncorrectOwner),
    InvalidSender(erc721::ERC721InvalidSender),
    InvalidReceiver(erc721::ERC721InvalidReceiver),
    InvalidReceiverWithReason(erc721::InvalidReceiverWithReason),
    InsufficientApproval(erc721::ERC721InsufficientApproval),
    InvalidApprover(erc721::ERC721InvalidApprover),
    InvalidOperator(erc721::ERC721InvalidOperator),


impl From<enumerable::Error> for Error
    fn from(value: enumerable::Error) -> Self {
        match value {
            enumerable::Error::OutOfBoundsIndex(e) => {
                Error::OutOfBoundsIndex(e)

            enumerable::Error::EnumerableForbiddenBatchMint(e) =>
                Error::EnumerableForbiddenBatchMint(e)

        }
    }
}

impl From<erc721::Error> for Error
    fn from(value: erc721::Error) -> Self {
        match value {
            erc721::Error::InvalidOwner(e) => Error::InvalidOwner(e),
            erc721::Error::NonexistentToken(e) => Error::NonexistentToken(e),
            erc721::Error::IncorrectOwner(e) => Error::IncorrectOwner(e),
            erc721::Error::InvalidSender(e) => Error::InvalidSender(e),
            erc721::Error::InvalidReceiver(e) => Error::InvalidReceiver(e),
            erc721::Error::InvalidReceiverWithReason(e) => {
                Error::InvalidReceiverWithReason(e)

            erc721::Error::InsufficientApproval(e) =>
                Error::InsufficientApproval(e)

            erc721::Error::InvalidApprover(e) => Error::InvalidApprover(e),
            erc721::Error::InvalidOperator(e) => Error::InvalidOperator(e),
        }
    }
}

#[entrypoint]
#[storage]
struct Erc721Example
    erc721: Erc721,
    enumerable: Erc721Enumerable,


#[public]
#[implements(IErc721<Error = Error>, IErc721Enumerable<Error = Error>, IErc165)]
impl Erc721Example
    fn mint(&mut self, to: Address, token_id: U256) -> Result<(), Error> {
        self.erc721._mint(to, token_id)?;

        // Update the extension's state.
        self.enumerable._add_token_to_all_tokens_enumeration(token_id);
        self.enumerable._add_token_to_owner_enumeration(
            to,
            token_id,
            &self.erc721,
        )?;

        Ok(())

}

#[public]
impl IErc721 for Erc721Example
    type Error = Error;

    fn balance_of(&self, owner: Address) -> Result<U256, Self::Error> {
        Ok(self.erc721.balance_of(owner)?)


    fn owner_of(&self, token_id: U256) -> Result<Address, Self::Error>
        Ok(self.erc721.owner_of(token_id)?)


    fn safe_transfer_from(
        &mut self,
        from: Address,
        to: Address,
        token_id: U256,
    ) -> Result<(), Self::Error>
        // Retrieve the previous owner.
        let previous_owner = self.erc721.owner_of(token_id)?;

        self.erc721.safe_transfer_from(from, to, token_id)?;

        // Update the extension's state.
        self.enumerable._remove_token_from_owner_enumeration(
            previous_owner,
            token_id,
            &self.erc721,
        )?;
        self.enumerable._add_token_to_owner_enumeration(
            to,
            token_id,
            &self.erc721,
        )?;

        Ok(())


    #[selector(name = "safeTransferFrom")]
    fn safe_transfer_from_with_data(
        &mut self,
        from: Address,
        to: Address,
        token_id: U256,
        data: Bytes,
    ) -> Result<(), Self::Error>
        // Retrieve the previous owner.
        let previous_owner = self.erc721.owner_of(token_id)?;

        self.erc721.safe_transfer_from_with_data(from, to, token_id, data)?;

        // Update the extension's state.
        self.enumerable._remove_token_from_owner_enumeration(
            previous_owner,
            token_id,
            &self.erc721,
        )?;
        self.enumerable._add_token_to_owner_enumeration(
            to,
            token_id,
            &self.erc721,
        )?;

        Ok(())


    fn transfer_from(
        &mut self,
        from: Address,
        to: Address,
        token_id: U256,
    ) -> Result<(), Self::Error>
        // Retrieve the previous owner.
        let previous_owner = self.erc721.owner_of(token_id)?;

        self.erc721.transfer_from(from, to, token_id)?;

        // Update the extension's state.
        self.enumerable._remove_token_from_owner_enumeration(
            previous_owner,
            token_id,
            &self.erc721,
        )?;
        self.enumerable._add_token_to_owner_enumeration(
            to,
            token_id,
            &self.erc721,
        )?;

        Ok(())


    fn approve(
        &mut self,
        to: Address,
        token_id: U256,
    ) -> Result<(), Self::Error>
        Ok(self.erc721.approve(to, token_id)?)


    fn set_approval_for_all(
        &mut self,
        to: Address,
        approved: bool,
    ) -> Result<(), Self::Error>
        Ok(self.erc721.set_approval_for_all(to, approved)?)


    fn get_approved(&self, token_id: U256) -> Result<Address, Self::Error>
        Ok(self.erc721.get_approved(token_id)?)


    fn is_approved_for_all(&self, owner: Address, operator: Address) -> bool
        self.erc721.is_approved_for_all(owner, operator)

}

#[public]
impl IErc721Burnable for Erc721Example
    type Error = Error;

    fn burn(&mut self, token_id: U256) -> Result<(), Self::Error> {
        // Retrieve the owner.
        let owner = self.erc721.owner_of(token_id)?;

        self.erc721.burn(token_id)?;

        // Update the extension's state.
        self.enumerable._remove_token_from_owner_enumeration(
            owner,
            token_id,
            &self.erc721,
        )?;
        self.enumerable._remove_token_from_all_tokens_enumeration(token_id);

        Ok(())

}

#[public]
impl IErc721Enumerable for Erc721Example
    type Error = Error;

    fn total_supply(&self) -> U256 {
        self.enumerable.total_supply()


    fn token_by_index(&self, index: U256) -> Result<U256, Self::Error>
        Ok(self.enumerable.token_by_index(index)?)


    fn token_of_owner_by_index(
        &self,
        owner: Address,
        index: U256,
    ) -> Result<U256, Self::Error>
        Ok(self.enumerable.token_of_owner_by_index(owner, index)?)

}

#[public]
impl IErc165 for Erc721Example
    fn supports_interface(&self, interface_id: B32) -> bool {
        self.erc721.supports_interface(interface_id)
            || self.enumerable.supports_interface(interface_id)

}

On this page