diff --git a/contracts/src/token/erc20/extensions/metadata.rs b/contracts/src/token/erc20/extensions/metadata.rs index ea8680cb..3279eaae 100644 --- a/contracts/src/token/erc20/extensions/metadata.rs +++ b/contracts/src/token/erc20/extensions/metadata.rs @@ -4,9 +4,9 @@ use alloc::string::String; use alloy_primitives::FixedBytes; use openzeppelin_stylus_proc::interface_id; -use stylus_proc::{public, sol_storage}; +use stylus_proc::sol_storage; -use crate::utils::introspection::erc165::IErc165; +use crate::{token::erc20::Erc20, utils::introspection::erc165::IErc165}; /// Number of decimals used by default on implementors of [`Metadata`]. pub const DEFAULT_DECIMALS: u8 = 18; @@ -58,44 +58,25 @@ pub trait IErc20Metadata { /// no way it affects any of the arithmetic of the contract, including /// [`super::super::IErc20::balance_of`] and /// [`super::super::IErc20::transfer`]. - fn decimals(&self) -> u8; -} - -// FIXME: Apply multi-level inheritance to export Metadata's functions. -// With the current version of SDK it is not possible. -// See https://github.com/OffchainLabs/stylus-sdk-rs/pull/120 -#[public] -impl IErc20Metadata for Erc20Metadata { - fn name(&self) -> String { - self._metadata.name() - } - - fn symbol(&self) -> String { - self._metadata.symbol() - } - fn decimals(&self) -> u8 { - // TODO: Use `U8` an avoid the conversion once - // https://github.com/OffchainLabs/stylus-sdk-rs/issues/117 - // gets resolved. DEFAULT_DECIMALS } } impl IErc165 for Erc20Metadata { fn supports_interface(interface_id: FixedBytes<4>) -> bool { - ::INTERFACE_ID + ::INTERFACE_ID == u32::from_be_bytes(*interface_id) } } #[cfg(all(test, feature = "std"))] mod tests { - use crate::token::erc20::extensions::{Erc20Metadata, IErc20Metadata}; + use crate::token::erc20::{extensions::IErc20Metadata, Erc20}; #[motsu::test] fn interface_id() { - let actual = ::INTERFACE_ID; + let actual = ::INTERFACE_ID; let expected = 0xa219a025; assert_eq!(actual, expected); } diff --git a/contracts/src/token/erc20/mod.rs b/contracts/src/token/erc20/mod.rs index b1b069d5..c4f6dd63 100644 --- a/contracts/src/token/erc20/mod.rs +++ b/contracts/src/token/erc20/mod.rs @@ -4,6 +4,8 @@ //! revert instead of returning `false` on failure. This behavior is //! nonetheless conventional and does not conflict with the expectations of //! [`Erc20`] applications. +use alloc::string::String; + use alloy_primitives::{Address, FixedBytes, U256}; use alloy_sol_types::sol; use openzeppelin_stylus_proc::interface_id; @@ -14,7 +16,10 @@ use stylus_sdk::{ stylus_proc::{public, sol_storage}, }; -use crate::utils::introspection::erc165::{Erc165, IErc165}; +use crate::{ + token::erc20::extensions::{Erc20Metadata, IErc20Metadata}, + utils::introspection::erc165::{Erc165, IErc165}, +}; pub mod extensions; @@ -104,6 +109,8 @@ impl MethodError for Error { sol_storage! { /// State of an `Erc20` token. pub struct Erc20 { + /// Metadata fields associated with token. + Erc20Metadata _metadata; /// Maps users to balances. mapping(address => uint256) _balances; /// Maps users to a mapping of each spender's allowance. @@ -290,9 +297,20 @@ impl IErc20 for Erc20 { } } +impl IErc20Metadata for Erc20 { + fn name(&self) -> String { + self._metadata._metadata.name() + } + + fn symbol(&self) -> String { + self._metadata._metadata.symbol() + } +} + impl IErc165 for Erc20 { fn supports_interface(interface_id: FixedBytes<4>) -> bool { ::INTERFACE_ID == u32::from_be_bytes(*interface_id) + || Erc20Metadata::supports_interface(interface_id) || Erc165::supports_interface(interface_id) } } @@ -562,8 +580,7 @@ mod tests { use super::{Erc20, Error, IErc20}; use crate::{ - token::erc721::{Erc721, IErc721}, - utils::introspection::erc165::IErc165, + token::erc721::IErc721, utils::introspection::erc165::IErc165, }; #[motsu::test] diff --git a/contracts/src/token/erc721/extensions/metadata.rs b/contracts/src/token/erc721/extensions/metadata.rs index 634d0eda..130c2659 100644 --- a/contracts/src/token/erc721/extensions/metadata.rs +++ b/contracts/src/token/erc721/extensions/metadata.rs @@ -2,11 +2,14 @@ use alloc::string::String; -use alloy_primitives::FixedBytes; +use alloy_primitives::{FixedBytes, U256}; use openzeppelin_stylus_proc::interface_id; -use stylus_proc::{public, sol_storage}; +use stylus_proc::sol_storage; -use crate::utils::{introspection::erc165::IErc165, Metadata}; +use crate::{ + token::erc721::{Erc721, Error}, + utils::{introspection::erc165::IErc165, Metadata}, +}; sol_storage! { /// Metadata of an [`crate::token::erc721::Erc721`] token. @@ -35,50 +38,39 @@ pub trait IErc721Metadata { /// * `&self` - Read access to the contract's state. fn symbol(&self) -> String; - /// Returns the base of Uniform Resource Identifier (URI) for tokens' - /// collection. + /// Returns the token URI for `token_id`. + /// + /// NOTE: Don't forget to add `#[selector(name = "tokenURI")]` while + /// reexporting, since actual solidity name is different. /// /// # Arguments /// /// * `&self` - Read access to the contract's state. - fn base_uri(&self) -> String; -} - -// FIXME: Apply multi-level inheritance to export Metadata's functions. -// With the current version of SDK it is not possible. -// See https://github.com/OffchainLabs/stylus-sdk-rs/pull/120 -#[public] -impl IErc721Metadata for Erc721Metadata { - fn name(&self) -> String { - self._metadata.name() - } - - fn symbol(&self) -> String { - self._metadata.symbol() - } - - fn base_uri(&self) -> String { - self._base_uri.get_string() - } + /// * `token_id` - Token id as a number. + /// + /// # Errors + /// + /// If token does not exist, then the error + /// [`Error::NonexistentToken`] is returned. + #[selector(name = "tokenURI")] + fn token_uri(&self, token_id: U256) -> Result; } impl IErc165 for Erc721Metadata { fn supports_interface(interface_id: FixedBytes<4>) -> bool { - ::INTERFACE_ID + ::INTERFACE_ID == u32::from_be_bytes(*interface_id) } } #[cfg(all(test, feature = "std"))] mod tests { - // use crate::token::erc721::extensions::{Erc721Metadata, IErc721Metadata}; + use crate::token::erc721::{extensions::IErc721Metadata, Erc721}; - // TODO: IErc721Metadata should be refactored to have same api as solidity - // has: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/4764ea50750d8bda9096e833706beba86918b163/contracts/token/ERC721/extensions/IERC721Metadata.sol#L12 - // [motsu::test] - // fn interface_id() { - // let actual = ::INTERFACE_ID; - // let expected = 0x5b5e139f; - // assert_eq!(actual, expected); - // } + #[motsu::test] + fn interface_id() { + let actual = ::INTERFACE_ID; + let expected = 0x5b5e139f; + assert_eq!(actual, expected); + } } diff --git a/contracts/src/token/erc721/extensions/uri_storage.rs b/contracts/src/token/erc721/extensions/uri_storage.rs index f5afc180..0fed77bb 100644 --- a/contracts/src/token/erc721/extensions/uri_storage.rs +++ b/contracts/src/token/erc721/extensions/uri_storage.rs @@ -1,13 +1,17 @@ //! ERC-721 token with storage-based token URI management. //! //! It also implements IERC4096, which is an ERC-721 Metadata Update Extension. -use alloc::string::String; +use alloc::string::{String, ToString}; use alloy_primitives::U256; use alloy_sol_types::sol; -use stylus_proc::{public, sol_storage}; +use stylus_proc::sol_storage; use stylus_sdk::evm; +use crate::token::erc721::{ + extensions::metadata::IErc721Metadata, Erc721, Error, +}; + sol! { /// This event gets emitted when the metadata of a token is changed. /// @@ -48,17 +52,36 @@ impl Erc721UriStorage { } } -#[public] impl Erc721UriStorage { /// Returns the Uniform Resource Identifier (URI) for `token_id` token. /// /// # Arguments /// /// * `&self` - Read access to the contract's state. + /// * `erc721` - Read access to the Erc721 contract's state. /// * `token_id` - Id of a token. #[must_use] - pub fn token_uri(&self, token_id: U256) -> String { - self._token_uris.getter(token_id).get_string() + pub fn token_uri( + &self, + erc721: &Erc721, + token_id: U256, + ) -> Result { + erc721._require_owned(token_id)?; + + let token_uri = self._token_uris.getter(token_id).get_string(); + let base = erc721._metadata._base_uri.get_string(); + + // If there is no base URI, return the token URI. + if base.is_empty() { + return Ok(token_uri); + }; + + // If both are set, concatenate the base uri and token_uri. + if !token_uri.is_empty() { + return Ok(base + &token_uri); + }; + + erc721.token_uri(token_id) } } @@ -66,33 +89,32 @@ impl Erc721UriStorage { mod tests { use alloy_primitives::U256; - use super::Erc721UriStorage; - fn random_token_id() -> U256 { let num: u32 = rand::random(); U256::from(num) } - #[motsu::test] - fn get_token_uri_works(contract: Erc721UriStorage) { - let token_id = random_token_id(); - - let token_uri = String::from("https://docs.openzeppelin.com/contracts/5.x/api/token/erc721#Erc721URIStorage"); - contract._token_uris.setter(token_id).set_str(token_uri.clone()); - - assert_eq!(token_uri, contract.token_uri(token_id)); - } - - #[motsu::test] - fn set_token_uri_works(contract: Erc721UriStorage) { - let token_id = random_token_id(); - - let initial_token_uri = String::from("https://docs.openzeppelin.com/contracts/5.x/api/token/erc721#Erc721URIStorage"); - contract._token_uris.setter(token_id).set_str(initial_token_uri); - - let token_uri = String::from("Updated Token URI"); - contract._set_token_uri(token_id, token_uri.clone()); - - assert_eq!(token_uri, contract.token_uri(token_id)); - } + // TODO#q: fix tests + // #[motsu::test] + // fn get_token_uri_works(contract: Erc721UriStorage) { + // let token_id = random_token_id(); + // + // let token_uri = String::from("https://docs.openzeppelin.com/contracts/5.x/api/token/erc721#Erc721URIStorage"); + // contract._token_uris.setter(token_id).set_str(token_uri.clone()); + // + // assert_eq!(token_uri, contract.token_uri(token_id)); + // } + // + // #[motsu::test] + // fn set_token_uri_works(contract: Erc721UriStorage) { + // let token_id = random_token_id(); + // + // let initial_token_uri = String::from("https://docs.openzeppelin.com/contracts/5.x/api/token/erc721#Erc721URIStorage"); + // contract._token_uris.setter(token_id).set_str(initial_token_uri); + // + // let token_uri = String::from("Updated Token URI"); + // contract._set_token_uri(token_id, token_uri.clone()); + // + // assert_eq!(token_uri, contract.token_uri(token_id)); + // } } diff --git a/contracts/src/token/erc721/mod.rs b/contracts/src/token/erc721/mod.rs index c88262b1..8ed6f5d8 100644 --- a/contracts/src/token/erc721/mod.rs +++ b/contracts/src/token/erc721/mod.rs @@ -1,5 +1,8 @@ //! Implementation of the [`Erc721`] token standard. -use alloc::vec; +use alloc::{ + string::{String, ToString}, + vec, +}; use alloy_primitives::{fixed_bytes, uint, Address, FixedBytes, U128, U256}; use openzeppelin_stylus_proc::interface_id; @@ -11,9 +14,12 @@ use stylus_sdk::{ prelude::*, }; -use crate::utils::{ - introspection::erc165::{Erc165, IErc165}, - math::storage::{AddAssignUnchecked, SubAssignUnchecked}, +use crate::{ + token::erc721::extensions::{Erc721Metadata, IErc721Metadata}, + utils::{ + introspection::erc165::{Erc165, IErc165}, + math::storage::{AddAssignUnchecked, SubAssignUnchecked}, + }, }; pub mod extensions; @@ -185,6 +191,8 @@ sol_interface! { sol_storage! { /// State of an [`Erc721`] token. pub struct Erc721 { + /// Metadata fields associated with token. + Erc721Metadata _metadata; /// Maps tokens to owners. mapping(uint256 => address) _owners; /// Maps users to balances. @@ -555,9 +563,31 @@ impl IErc721 for Erc721 { } } +impl IErc721Metadata for Erc721 { + fn name(&self) -> String { + self._metadata._metadata.name() + } + + fn symbol(&self) -> String { + self._metadata._metadata.symbol() + } + + fn token_uri(&self, token_id: U256) -> Result { + self._require_owned(token_id)?; + let base_uri = self._metadata._base_uri.get_string(); + let uri = if !base_uri.is_empty() { + base_uri + &token_id.to_string() + } else { + "".to_string() + }; + Ok(uri) + } +} + impl IErc165 for Erc721 { fn supports_interface(interface_id: FixedBytes<4>) -> bool { ::INTERFACE_ID == u32::from_be_bytes(*interface_id) + || Erc721Metadata::supports_interface(interface_id) || Erc165::supports_interface(interface_id) } } diff --git a/examples/access-control/src/constructor.sol b/examples/access-control/src/constructor.sol index f6f77693..9f970a83 100644 --- a/examples/access-control/src/constructor.sol +++ b/examples/access-control/src/constructor.sol @@ -2,6 +2,9 @@ pragma solidity ^0.8.21; contract AccessControlExample { + string private _name; + string private _symbol; + mapping(address => uint256) _balances; mapping(address => mapping(address => uint256)) _allowances; uint256 _totalSupply; diff --git a/examples/basic/token/src/constructor.sol b/examples/basic/token/src/constructor.sol index 66d37c6d..d1d9f311 100644 --- a/examples/basic/token/src/constructor.sol +++ b/examples/basic/token/src/constructor.sol @@ -2,12 +2,13 @@ pragma solidity ^0.8.21; contract BasicToken { + string private _name; + string private _symbol; + mapping(address account => uint256) private _balances; mapping(address account => mapping(address spender => uint256)) - private _allowances; + private _allowances; uint256 private _totalSupply; - string private _name; - string private _symbol; constructor(string memory name_, string memory symbol_) { _name = name_; diff --git a/examples/basic/token/src/lib.rs b/examples/basic/token/src/lib.rs index 6510b223..72419c66 100644 --- a/examples/basic/token/src/lib.rs +++ b/examples/basic/token/src/lib.rs @@ -1,10 +1,13 @@ #![cfg_attr(not(test), no_main, no_std)] extern crate alloc; -use alloc::vec::Vec; +use alloc::{string::String, vec::Vec}; use alloy_primitives::{Address, U256}; -use openzeppelin_stylus::token::erc20::{extensions::Erc20Metadata, Erc20}; +use openzeppelin_stylus::token::erc20::{ + extensions::{Erc20Metadata, IErc20Metadata}, + Erc20, +}; use stylus_sdk::prelude::{entrypoint, public, sol_storage}; sol_storage! { @@ -12,13 +15,11 @@ sol_storage! { struct Erc20Example { #[borrow] Erc20 erc20; - #[borrow] - Erc20Metadata metadata; } } #[public] -#[inherit(Erc20, Erc20Metadata)] +#[inherit(Erc20)] impl Erc20Example { pub fn mint( &mut self, @@ -28,4 +29,16 @@ impl Erc20Example { self.erc20._mint(account, value)?; Ok(()) } + + pub fn name(&self) -> String { + self.erc20.name() + } + + pub fn symbol(&self) -> String { + self.erc20.symbol() + } + + pub fn decimals(&self) -> u8 { + self.erc20.decimals() + } } diff --git a/examples/erc20-permit/src/constructor.sol b/examples/erc20-permit/src/constructor.sol index 91045cb1..c4d29255 100644 --- a/examples/erc20-permit/src/constructor.sol +++ b/examples/erc20-permit/src/constructor.sol @@ -2,9 +2,11 @@ pragma solidity ^0.8.21; contract Erc20PermitExample { + string private _name; + string private _symbol; + mapping(address account => uint256) private _balances; - mapping(address account => mapping(address spender => uint256)) - private _allowances; + mapping(address account => mapping(address spender => uint256)) private _allowances; uint256 private _totalSupply; mapping(address account => uint256) _nonces; diff --git a/examples/erc20/src/constructor.sol b/examples/erc20/src/constructor.sol index 96897b8c..8c9afb8e 100644 --- a/examples/erc20/src/constructor.sol +++ b/examples/erc20/src/constructor.sol @@ -2,12 +2,14 @@ pragma solidity ^0.8.21; contract Erc20Example { + string private _name; + string private _symbol; + mapping(address account => uint256) private _balances; mapping(address account => mapping(address spender => uint256)) private _allowances; uint256 private _totalSupply; - string private _name; - string private _symbol; + uint256 private _cap; bool private _paused; mapping(address account => uint256) _nonces; diff --git a/examples/erc20/src/lib.rs b/examples/erc20/src/lib.rs index 4d8ab3e0..b91726a1 100644 --- a/examples/erc20/src/lib.rs +++ b/examples/erc20/src/lib.rs @@ -1,12 +1,12 @@ #![cfg_attr(not(test), no_main, no_std)] extern crate alloc; -use alloc::vec::Vec; +use alloc::{string::String, vec::Vec}; use alloy_primitives::{Address, FixedBytes, U256}; use openzeppelin_stylus::{ token::erc20::{ - extensions::{capped, Capped, Erc20Metadata, IErc20Burnable}, + extensions::{capped, Capped, IErc20Burnable, IErc20Metadata}, Erc20, IErc20, }, utils::{introspection::erc165::IErc165, Pausable}, @@ -21,8 +21,6 @@ sol_storage! { #[borrow] Erc20 erc20; #[borrow] - Erc20Metadata metadata; - #[borrow] Capped capped; #[borrow] Pausable pausable; @@ -30,12 +28,20 @@ sol_storage! { } #[public] -#[inherit(Erc20, Erc20Metadata, Capped, Pausable)] +#[inherit(Erc20, Capped, Pausable)] impl Erc20Example { - // Overrides the default [`Metadata::decimals`], and sets it to `10`. - // - // If you don't provide this method in the `entrypoint` contract, it will - // default to `18`. + pub fn name(&self) -> String { + self.erc20.name() + } + + pub fn symbol(&self) -> String { + self.erc20.symbol() + } + + /// Overrides the default [`Erc20::decimals`], and sets it to `10`. + /// + /// If you don't provide this method in the `entrypoint` contract, it will + /// default to `18`. pub fn decimals(&self) -> u8 { DECIMALS } @@ -108,7 +114,6 @@ impl Erc20Example { fn supports_interface(interface_id: FixedBytes<4>) -> bool { Erc20::supports_interface(interface_id) - || Erc20Metadata::supports_interface(interface_id) } pub fn pause(&mut self) -> Result<(), Vec> { diff --git a/examples/erc721-consecutive/src/constructor.sol b/examples/erc721-consecutive/src/constructor.sol index f875a3b4..7f7a38fd 100644 --- a/examples/erc721-consecutive/src/constructor.sol +++ b/examples/erc721-consecutive/src/constructor.sol @@ -2,6 +2,10 @@ pragma solidity ^0.8.21; contract Erc721ConsecutiveExample { + string private _name; + string private _symbol; + string private _baseUri; + mapping(uint256 tokenId => address) private _owners; mapping(address owner => uint256) private _balances; mapping(uint256 tokenId => address) private _tokenApprovals; diff --git a/examples/erc721-consecutive/tests/erc721-consecutive.rs b/examples/erc721-consecutive/tests/erc721_consecutive.rs similarity index 100% rename from examples/erc721-consecutive/tests/erc721-consecutive.rs rename to examples/erc721-consecutive/tests/erc721_consecutive.rs diff --git a/examples/erc721-metadata/src/constructor.sol b/examples/erc721-metadata/src/constructor.sol index ec6df515..821f9db3 100644 --- a/examples/erc721-metadata/src/constructor.sol +++ b/examples/erc721-metadata/src/constructor.sol @@ -2,15 +2,15 @@ pragma solidity ^0.8.21; contract Erc721MetadataExample { + string private _name; + string private _symbol; + string private _baseUri; + mapping(uint256 tokenId => address) private _owners; mapping(address owner => uint256) private _balances; mapping(uint256 tokenId => address) private _tokenApprovals; mapping(address owner => mapping(address operator => bool)) - private _operatorApprovals; - - string private _name; - string private _symbol; - string private _baseUri; + private _operatorApprovals; mapping(uint256 => string) _tokenUris; diff --git a/examples/erc721-metadata/src/lib.rs b/examples/erc721-metadata/src/lib.rs index 006efa56..0b448b7b 100644 --- a/examples/erc721-metadata/src/lib.rs +++ b/examples/erc721-metadata/src/lib.rs @@ -1,18 +1,14 @@ #![cfg_attr(not(test), no_main, no_std)] extern crate alloc; -use alloc::{ - string::{String, ToString}, - vec::Vec, -}; +use alloc::{string::String, vec::Vec}; use alloy_primitives::{Address, U256}; use openzeppelin_stylus::token::erc721::{ extensions::{ - Erc721Metadata as Metadata, Erc721UriStorage as UriStorage, - IErc721Burnable, IErc721Metadata, + Erc721UriStorage as UriStorage, IErc721Burnable, IErc721Metadata, }, - Erc721, IErc721, + Erc721, }; use stylus_sdk::prelude::{entrypoint, public, sol_storage}; @@ -21,15 +17,12 @@ sol_storage! { struct Erc721MetadataExample { #[borrow] Erc721 erc721; - #[borrow] - Metadata metadata; - #[borrow] UriStorage uri_storage; } } #[public] -#[inherit(Erc721, Metadata, UriStorage)] +#[inherit(Erc721)] impl Erc721MetadataExample { pub fn mint(&mut self, to: Address, token_id: U256) -> Result<(), Vec> { Ok(self.erc721._mint(to, token_id)?) @@ -39,29 +32,19 @@ impl Erc721MetadataExample { Ok(self.erc721.burn(token_id)?) } - // Overrides [`Erc721UriStorage::token_uri`]. - // Returns the Uniform Resource Identifier (URI) for tokenId token. - #[selector(name = "tokenURI")] - pub fn token_uri(&self, token_id: U256) -> Result> { - let _owner = self.erc721.owner_of(token_id)?; - - let base = self.metadata.base_uri(); - let token_uri = self.uri_storage.token_uri(token_id); - - // If there is no base URI, return the token URI. - if base.is_empty() { - return Ok(token_uri); - } + fn name(&self) -> String { + self.erc721.name() + } - // If both are set, - // concatenate the base URI and token URI. - let uri = if !token_uri.is_empty() { - base + &token_uri - } else { - base + &token_id.to_string() - }; + fn symbol(&self) -> String { + self.erc721.symbol() + } - Ok(uri) + /// Overrides [`Erc721::token_uri`]. + /// Returns the Uniform Resource Identifier (URI) for tokenId token. + #[selector(name = "tokenURI")] + pub fn token_uri(&self, token_id: U256) -> Result> { + Ok(self.uri_storage.token_uri(&self.erc721, token_id)?) } #[selector(name = "setTokenURI")] diff --git a/examples/erc721-metadata/tests/abi/mod.rs b/examples/erc721-metadata/tests/abi/mod.rs index 9fd31eae..cec57cc1 100644 --- a/examples/erc721-metadata/tests/abi/mod.rs +++ b/examples/erc721-metadata/tests/abi/mod.rs @@ -19,7 +19,6 @@ sol!( function transferFrom(address from, address to, uint256 tokenId) external; function mint(address to, uint256 tokenId) external; function burn(uint256 tokenId) external; - function baseUri() external view returns (string memory baseURI); function name() external view returns (string memory name); function symbol() external view returns (string memory symbol); #[derive(Debug)] diff --git a/examples/erc721-metadata/tests/erc721.rs b/examples/erc721-metadata/tests/erc721_metadata.rs similarity index 91% rename from examples/erc721-metadata/tests/erc721.rs rename to examples/erc721-metadata/tests/erc721_metadata.rs index 9e7d7f73..99831cde 100644 --- a/examples/erc721-metadata/tests/erc721.rs +++ b/examples/erc721-metadata/tests/erc721_metadata.rs @@ -47,30 +47,9 @@ async fn constructs(alice: Account) -> eyre::Result<()> { let Erc721::nameReturn { name } = contract.name().call().await?; let Erc721::symbolReturn { symbol } = contract.symbol().call().await?; - let Erc721::baseUriReturn { baseURI } = contract.baseUri().call().await?; assert_eq!(TOKEN_NAME.to_owned(), name); assert_eq!(TOKEN_SYMBOL.to_owned(), symbol); - assert_eq!(base_uri.to_owned(), baseURI); - - Ok(()) -} - -#[e2e::test] -async fn constructs_with_base_uri(alice: Account) -> eyre::Result<()> { - let base_uri = "https://github.com/OpenZeppelin/rust-contracts-stylus"; - - let contract_addr = alice - .as_deployer() - .with_constructor(ctr(base_uri)) - .deploy() - .await? - .address()?; - let contract = Erc721::new(contract_addr, &alice.wallet); - - let Erc721::baseUriReturn { baseURI } = contract.baseUri().call().await?; - - assert_eq!(base_uri.to_owned(), baseURI); Ok(()) } diff --git a/examples/erc721/src/constructor.sol b/examples/erc721/src/constructor.sol index 79c3e778..c7cc7d1e 100644 --- a/examples/erc721/src/constructor.sol +++ b/examples/erc721/src/constructor.sol @@ -2,14 +2,16 @@ pragma solidity ^0.8.21; contract Erc721Example { + string private _name; + string private _symbol; + string private _baseUri; + mapping(uint256 tokenId => address) private _owners; mapping(address owner => uint256) private _balances; mapping(uint256 tokenId => address) private _tokenApprovals; - mapping(address owner => mapping(address operator => bool)) - private _operatorApprovals; + mapping(address owner => mapping(address operator => bool)) private _operatorApprovals; - mapping(address owner => mapping(uint256 index => uint256)) - private _ownedTokens; + mapping(address owner => mapping(uint256 index => uint256)) private _ownedTokens; mapping(uint256 tokenId => uint256) private _ownedTokensIndex; uint256[] private _allTokens; mapping(uint256 tokenId => uint256) private _allTokensIndex;