ERC-721 Uri Storage

OpenZeppelin ERC-721 URI Storage 扩展是管理和存储单个 token 的 URI 所必需的。此扩展允许每个 token 拥有其自己唯一的 URI, 该 URI 可以指向有关 token 的元数据,例如图像、描述和其他属性。 这对于非同质化 token (NFT) 尤其有用,因为每个 token 都是唯一的并且可能具有不同的元数据。

用法

为了使用具有 URI Storage 特性的 ERC-721 token, 您的 token 还应该使用 ERC-721 Metadata 扩展来为每个 token 提供额外的元数据。 您需要创建一个如下所示的指定合约:

use openzeppelin_stylus::{
    token::erc721::{
        self,
        extensions::{
            Erc721Metadata, Erc721UriStorage, IErc721Metadata,
            IErc721UriStorage,
        },
        Erc721, IErc721,
    },
    utils::introspection::erc165::IErc165,
};

#[entrypoint]
#[storage]
struct Erc721MetadataExample {
    erc721: Erc721,
    metadata: Erc721Metadata,
    uri_storage: Erc721UriStorage,
}

#[public]
#[implements(IErc721<Error = erc721::Error>, IErc721Metadata<Error = erc721::Error>, IErc165)]
impl Erc721MetadataExample {
    #[constructor]
    fn constructor(&mut self, name: String, symbol: String, base_uri: String) {
        self.metadata.constructor(name, symbol);
        self.metadata.base_uri.set_str(base_uri);
    }

    fn mint(
        &mut self,
        to: Address,
        token_id: U256,
    ) -> Result<(), erc721::Error> {
        self.erc721._mint(to, token_id)
    }

    #[selector(name = "setTokenURI")]
    fn set_token_uri(&mut self, token_id: U256, token_uri: String) {
        self.uri_storage._set_token_uri(token_id, token_uri)
    }
}

#[public]
impl IErc721 for Erc721MetadataExample {
    type Error = erc721::Error;

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

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

    fn safe_transfer_from(
        &mut self,
        from: Address,
        to: Address,
        token_id: U256,
    ) -> Result<(), Self::Error> {
        self.erc721.safe_transfer_from(from, to, token_id)
    }

    fn safe_transfer_from_with_data(
        &mut self,
        from: Address,
        to: Address,
        token_id: U256,
        data: Bytes,
    ) -> Result<(), Self::Error> {
        self.erc721.safe_transfer_from_with_data(from, to, token_id, data)
    }

    fn transfer_from(
        &mut self,
        from: Address,
        to: Address,
        token_id: U256,
    ) -> Result<(), Self::Error> {
        self.erc721.transfer_from(from, to, token_id)
    }

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

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

    fn get_approved(&self, token_id: U256) -> Result<Address, Self::Error> {
        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 IErc721Metadata for Erc721MetadataExample {
    type Error = erc721::Error;

    fn name(&self) -> String {
        self.metadata.name()
    }

    fn symbol(&self) -> String {
        self.metadata.symbol()
    }

    #[selector(name = "tokenURI")]
    fn token_uri(&self, token_id: U256) -> Result<String, Self::Error> {
        self.uri_storage.token_uri(token_id, &self.erc721, &self.metadata)
    }
}

// We implement this trait only to respect Rust's trait requirements.
// There's nothing to expose, as all the necessary functions were exposed by
// implementing `IErc721Metadata`.
// 我们实现这个 trait 只是为了尊重 Rust 的 trait 要求。
// 没有什么可公开的,因为所有必要的功能都通过实现 `IErc721Metadata` 公开了。
impl IErc721UriStorage for Erc721MetadataExample {}

#[public]
impl IErc165 for Erc721MetadataExample {
    fn supports_interface(&self, interface_id: B32) -> bool {
        self.erc721.supports_interface(interface_id)
            || <Self as IErc721Metadata>::interface_id() == interface_id
    }
}