From 47895186aa4593385dbb0d45938af6502adf9dec Mon Sep 17 00:00:00 2001 From: gerceboss Date: Thu, 27 Jun 2024 20:43:37 +0530 Subject: [PATCH 01/22] updated with recent implementation --- packages/token/src/erc721/extensions.cairo | 3 + .../extensions/erc721_uri_storage.cairo | 98 +++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 packages/token/src/erc721/extensions.cairo create mode 100644 packages/token/src/erc721/extensions/erc721_uri_storage.cairo diff --git a/packages/token/src/erc721/extensions.cairo b/packages/token/src/erc721/extensions.cairo new file mode 100644 index 000000000..5c08a2fe3 --- /dev/null +++ b/packages/token/src/erc721/extensions.cairo @@ -0,0 +1,3 @@ +pub mod erc721_uri_storage; + +pub use erc721_uri_storage::ERC721URIstorageComponent; diff --git a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo new file mode 100644 index 000000000..db3b35191 --- /dev/null +++ b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc721/extensions/erc721_uri_storage.cairo) + +use starknet::ContractAddress; + +pub trait IERC721URIstorage{ + fn token_uri(self: @ComponentState,token_id:u256)->ByteArray; + fn set_token_uri(ref self: ComponentState,token_id:u256,_token_uri:ByteArray) +} + +#[starknet::component] +pub mod ERC721URIstorageComponent { + use openzeppelin::token::erc721::ERC721Component; + use openzeppelin::token::erc721::interface::{IERC721,IERC721Metadata}; + use starknet::ContractAddress; + + #[storage] + struct Storage { + token_uris:LegacyMap, + } + + #[event] + #[derive(Drop, PartialEq, starknet::Event)] + pub enum Event { + MetadataUpdate:MetadataUpdate, + } + + /// Emitted when `token_uri` is changed for `token_id` + #[derive(Drop, PartialEq, starknet::Event)] + pub struct MetadataUpdate{ + #[key] + pub token_id: u256, + } + + #[embeddable_as(ERC721URIstorageImpl)] + impl ERC721URIstorage< + TContractState, + +HasComponent, + +ERC721Component::HasComponent, + +ERC721Component::ERC721HooksTrait, + +Drop + > of super::IERC721URIstorage> { + + /// Returns the Uniform Resource Identifier (URI) for the `token_id` token. + /// If the URI is not set, the return value will be an empty ByteArray. + /// + /// Requirements: + /// + /// - `token_id` exists. + fn token_uri(self: @ComponentState,token_id:u256)->ByteArray{ + self._token_uri(token_id) + } + + /// Sets the Uniform Resource Identifier (URI) for the `token_id` token. + fn set_token_uri(ref self: ComponentState,token_id:u256,_token_uri:ByteArray){ + self._set_token_uri(token_id,_token_uri); + } + } + + // + // Internal + // + + #[generate_trait] + pub impl InternalImpl< + TContractState, + +HasComponent, + impl ERC721: ERC721Component::HasComponent, + +ERC721Component::ERC721HooksTrait, + +Drop + > of InternalTrait { + + /// Returns the `token_uri` for the `token_id` + /// if needed, returns the concatenated string + fn _token_uri(self: @ComponentState,token_id:u256)-> ByteArray{ + let mut erc721_component= get_dep_component!(self, ERC721); + let base_uri:ByteArray=erc721_component.base_uri(); + let token_uri:ByteArray= self.token_uris.read(token_id); + if base_uri.len()==0{ + return token_uri; + } + if token_uri.len()>0{ + return format!("{}{}",base_uri,token_uri); + } + return erc721_component.token_uri(token_id); + } + + /// Sets or updates the `token_uri` for the respective `token_uri` + /// + /// Emits `MetadataUpdate` event + fn _set_token_uri(ref self: ComponentState,token_id:u256,_token_uri:ByteArray){ + self.token_uris.write(token_id,_token_uri); + self.emit(MetadataUpdate{token_id:token_id}); + } + } +} + + From deaceb7d0fa8698cbb387edd1d4814e92ea557a8 Mon Sep 17 00:00:00 2001 From: gerceboss Date: Fri, 28 Jun 2024 03:37:32 +0530 Subject: [PATCH 02/22] added mock and tests ERC721URIstorage --- .../extensions/erc721_uri_storage.cairo | 3 +- .../erc721/test_erc721_uri_storage.cairo | 104 ++++++++++++++++++ .../mocks/erc721_uri_storage_mocks.cairo | 56 ++++++++++ 3 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 packages/token/src/tests/erc721/test_erc721_uri_storage.cairo create mode 100644 packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo diff --git a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo index db3b35191..7a2c1abb7 100644 --- a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo +++ b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo @@ -11,6 +11,7 @@ pub trait IERC721URIstorage{ #[starknet::component] pub mod ERC721URIstorageComponent { use openzeppelin::token::erc721::ERC721Component; + use openzeppelin::token::erc721:InternalImpl as ERC721Impl; use openzeppelin::token::erc721::interface::{IERC721,IERC721Metadata}; use starknet::ContractAddress; @@ -37,7 +38,6 @@ pub mod ERC721URIstorageComponent { TContractState, +HasComponent, +ERC721Component::HasComponent, - +ERC721Component::ERC721HooksTrait, +Drop > of super::IERC721URIstorage> { @@ -73,6 +73,7 @@ pub mod ERC721URIstorageComponent { /// Returns the `token_uri` for the `token_id` /// if needed, returns the concatenated string fn _token_uri(self: @ComponentState,token_id:u256)-> ByteArray{ + ERC721Impl::_require_owned(token_id); let mut erc721_component= get_dep_component!(self, ERC721); let base_uri:ByteArray=erc721_component.base_uri(); let token_uri:ByteArray= self.token_uris.read(token_id); diff --git a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo new file mode 100644 index 000000000..4abf5132d --- /dev/null +++ b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo @@ -0,0 +1,104 @@ +use openzeppelin::tests::mocks:erc721_uri_storage_mocks::ERC721URIstorageMock; +use openzeppelin::tests::utils::constants::{ + DATA, ZERO, OWNER, CALLER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, TOKEN_ID, + TOKEN_ID_2, PUBKEY, BASE_URI, BASE_URI_2 +}; +use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; +use openzeppelin::introspection::src5; +use openzeppelin::introspection; +use openzeppelin::token::erc721::ERC721Component::{ERC721Impl, ERC721MetadataImpl, InternalImpl}; +use openzeppelin::token::erc721::ERC721Component; +use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent; +use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent::{ERC721URIstorageImpl,InternalImpl}; + +use openzeppelin::token::erc721::interface::IERC721; +use openzeppelin::token::erc721; +use openzeppelin::tests::utils; + +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::storage::{StorageMapMemberAccessTrait, StorageMemberAccessTrait}; +use starknet::testing; + + + +// +// Setup +// + +type ComponentState = ERC721URIstorageComponent::ComponentState; + +fn CONTRACT_STATE() -> ERC721URIstorageMock::ContractState { + ERC721URIstorageMock::contract_state_for_testing() +} +fn COMPONENT_STATE() -> ComponentState { + ERC721URIstorageComponent::component_state_for_testing() +} + +//constructor is inside only +fn setup() -> ComponentState { + let mut state = COMPONENT_STATE(); + let mock_state=CONTRACT_STATE(); + mock_state.erc721.initializer(NAME(), SYMBOL(), BASE_URI()); + mock_state.erc721.mint(OWNER(),TOKEN_ID); + utils::drop_event(ZERO()); + state +} + +//check token_uri for minted ones are by default empty +#[test] +fn test_token_uri() { + let state = setup(); + let uri = state.token_uri(TOKEN_ID); + let expected = format!("{}{}", BASE_URI(), TOKEN_ID); + assert_eq!(uri, expected); +} + +#[test] +#[should_panic(expected: ('ERC721: invalid token ID',))] +fn test_token_uri_non_minted() { + let state = setup(); + state.token_uri(7); +} + +//check setter (set_token_uri) +#[test] +fn test_set_token_uri(){ + let state=setup(); + + state.set_token_uri(TOKEN_ID,BASE_URI_2); + assert_only_event_metadata_update(TOKEN_ID); + + let expected= format!("{}{}{}", BASE_URI(), TOKEN_ID,BASE_URI_2); + let uri=state.token_uri(TOKEN_ID); + + assert_eq!(uri,expected); +} + + + + + +// +// Helpers +// +fn assert_event_metadata_update(contract:ContractAddress,token_id:u256){ + let event=utils::pop_log::(contract).unwrap(); + let expected=ERC721URIstorageComponent::Event::MetadataUpdate( + MetadataUpdate{token_id} + ); + assert!(event==expected); + + //check indexed keys + ley mut indexed_keys=array![]; + indexed_keys.append_serde(selector!("MetadataUpdate")); + indexed_keys.append_serde(token_id); + utisl:::assert_indexed_keys(event,indexed_keys.span()) +} + +fn assert_only_event_metadata_update( + contract: ContractAddress, token_id : u256 +) { + assert_event_metadata_update(contract, token_id); + utils::assert_no_events_left(contract); +} \ No newline at end of file diff --git a/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo b/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo new file mode 100644 index 000000000..006afa895 --- /dev/null +++ b/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo @@ -0,0 +1,56 @@ +#[starknet::contract] +pub(crate) mod ERC721URIstorageMock { + use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::token::erc721::ERC721Component; + use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent::InternalTrait as ERC20VotesInternalTrait; + use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent; + use starknet::ContractAddress; + + component!(path: ERC721Component, storage: erc721, event: ERC721Event); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // ERC721 + #[abi(embed_v0)] + impl ERC721Impl = ERC721Component::ERC721Impl; + impl ERC721InternalImpl = ERC721Component::InternalImpl; + #[abi(embed_v0)] + impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + //ERC721URIstorage + #[abi(embed_v0)] + impl ERC721URIstorage=ERC721URIstorageComponent::ERC721URIstorageImpl + + #[storage] + struct Storage { + #[substorage(v0)] + erc721: ERC721Component::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC721URIstorageEvent: ERC721URIstorageComponent::Event, + #[flat] + ERC721Event: ERC721Component::Event, + } + + #[constructor] + fn constructor( + ref self: ContractState, + name: ByteArray, + symbol: ByteArray, + base_uri: ByteArray, + recipient: ContractAddress, + token_id: u256 + ) { + self.erc721.initializer(name, symbol, base_uri); + self.erc721.mint(recipient, token_id); + } +} From f33d1c4d48891a905bbe192e27e06cdf2966c3e9 Mon Sep 17 00:00:00 2001 From: gerceboss Date: Fri, 28 Jun 2024 16:55:02 +0530 Subject: [PATCH 03/22] added SAMPLE_URI and tests --- .../extensions/erc721_uri_storage.cairo | 2 +- .../erc721/test_erc721_uri_storage.cairo | 86 ++++++++++--- src/tests/token/erc721.cairo | 6 + src/tests/utils/constants.cairo | 119 ++++++++++++++++++ 4 files changed, 197 insertions(+), 16 deletions(-) create mode 100644 src/tests/token/erc721.cairo create mode 100644 src/tests/utils/constants.cairo diff --git a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo index 7a2c1abb7..bad553228 100644 --- a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo +++ b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo @@ -75,7 +75,7 @@ pub mod ERC721URIstorageComponent { fn _token_uri(self: @ComponentState,token_id:u256)-> ByteArray{ ERC721Impl::_require_owned(token_id); let mut erc721_component= get_dep_component!(self, ERC721); - let base_uri:ByteArray=erc721_component.base_uri(); + let base_uri:ByteArray=erc721_component._base_uri(); let token_uri:ByteArray= self.token_uris.read(token_id); if base_uri.len()==0{ return token_uri; diff --git a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo index 4abf5132d..a3085d0b3 100644 --- a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo +++ b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo @@ -1,20 +1,20 @@ -use openzeppelin::tests::mocks:erc721_uri_storage_mocks::ERC721URIstorageMock; +use openzeppelin::tests::mocks::erc721_uri_storage_mocks::ERC721URIstorageMock; use openzeppelin::tests::utils::constants::{ DATA, ZERO, OWNER, CALLER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, TOKEN_ID, - TOKEN_ID_2, PUBKEY, BASE_URI, BASE_URI_2 + TOKEN_ID_2, PUBKEY, BASE_URI, BASE_URI_2,SAMPLE_URI }; use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; use openzeppelin::introspection::src5; use openzeppelin::introspection; -use openzeppelin::token::erc721::ERC721Component::{ERC721Impl, ERC721MetadataImpl, InternalImpl}; +use openzeppelin::token::erc721::ERC721Component::{ERC721MetadataImpl, InternalImpl}; use openzeppelin::token::erc721::ERC721Component; use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent; -use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent::{ERC721URIstorageImpl,InternalImpl}; +use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent::ERC721URIstorageImpl; use openzeppelin::token::erc721::interface::IERC721; use openzeppelin::token::erc721; use openzeppelin::tests::utils; - +use openzeppelin::utils::serde::SerializedAppend; use starknet::ContractAddress; use starknet::contract_address_const; use starknet::storage::{StorageMapMemberAccessTrait, StorageMemberAccessTrait}; @@ -40,17 +40,16 @@ fn setup() -> ComponentState { let mut state = COMPONENT_STATE(); let mock_state=CONTRACT_STATE(); mock_state.erc721.initializer(NAME(), SYMBOL(), BASE_URI()); - mock_state.erc721.mint(OWNER(),TOKEN_ID); + state.mint(OWNER(),TOKEN_ID); utils::drop_event(ZERO()); state } -//check token_uri for minted ones are by default empty #[test] fn test_token_uri() { let state = setup(); let uri = state.token_uri(TOKEN_ID); - let expected = format!("{}{}", BASE_URI(), TOKEN_ID); + let expected = ""; assert_eq!(uri, expected); } @@ -58,26 +57,83 @@ fn test_token_uri() { #[should_panic(expected: ('ERC721: invalid token ID',))] fn test_token_uri_non_minted() { let state = setup(); - state.token_uri(7); + state.token_uri(TOKEN_ID_2); } -//check setter (set_token_uri) #[test] fn test_set_token_uri(){ let state=setup(); - state.set_token_uri(TOKEN_ID,BASE_URI_2); - assert_only_event_metadata_update(TOKEN_ID); + state.set_token_uri(TOKEN_ID,SAMPLE_URI()); + assert_only_event_metadata_update(ZERO(),TOKEN_ID);//checking event is emmitted or not - let expected= format!("{}{}{}", BASE_URI(), TOKEN_ID,BASE_URI_2); + let expected= format!("{}",SAMPLE_URI()); let uri=state.token_uri(TOKEN_ID); assert_eq!(uri,expected); } +#[test] +fn test_set_token_uri_nonexistent(){ + let mut state=setup(); + + state.set_token_uri(TOKEN_ID_2,SAMPLE_URI()); + assert_only_event_metadata_update(ZERO(),TOKEN_ID_2);//checking event is emitted or not + + + //check accesible after minting + state.mint(RECIPIENT(),TOKEN_ID_2); + + let expected= format!("{}",SAMPLE_URI()); + let uri=state.token_uri(TOKEN_ID); + + assert_eq!(uri,expected); +} +#[test] +fn test_set_base_uri(){ + let mut state=setup();//its the component state + state._set_base_uri(BASE_URI()); + let base_uri=state._base_uri(); + + assert_eq!(base_uri, BASE_URI()); +} +#[test] +fn test_base_uri_is_prefix(){ + let mut state=setup();//its the component state + + state._set_base_uri(BASE_URI()); + state.set_token_uri(TOKEN_ID,SAMPLE_URI()); + + let token_uri=state.token_uri(TOKEN_ID); + let expected=format!("{}{}",BASE_URI(),SAMPLE_URI()); + assert_eq!(token_uri,expected); +} + +#[test] +fn test_base_uri_2_is_set_as_prefix(){ + let mut state=setup();//its the component state + + state._set_base_uri(BASE_URI_2()); + state.set_token_uri(TOKEN_ID,SAMPLE_URI()); + + let token_uri=state.token_uri(TOKEN_ID); + let expected=format!("{}{}",BASE_URI_2(),SAMPLE_URI()); + assert_eq!(token_uri,expected); +} + +#[test] +fn test_base_uri_and_token_id(){ + let mut state=setup();//its the component state + + state._set_base_uri(BASE_URI()); + + let token_uri=state.token_uri(TOKEN_ID); + let expected=format!("{}{}",BASE_URI(),TOKEN_ID); + assert_eq!(token_uri,expected); +} // // Helpers @@ -90,10 +146,10 @@ fn assert_event_metadata_update(contract:ContractAddress,token_id:u256){ assert!(event==expected); //check indexed keys - ley mut indexed_keys=array![]; + let mut indexed_keys=array![]; indexed_keys.append_serde(selector!("MetadataUpdate")); indexed_keys.append_serde(token_id); - utisl:::assert_indexed_keys(event,indexed_keys.span()) + utils::assert_indexed_keys(event,indexed_keys.span()) } fn assert_only_event_metadata_update( diff --git a/src/tests/token/erc721.cairo b/src/tests/token/erc721.cairo new file mode 100644 index 000000000..c48cd876b --- /dev/null +++ b/src/tests/token/erc721.cairo @@ -0,0 +1,6 @@ +pub(crate) mod common; + +mod test_dual721; +mod test_dual721_receiver; +mod test_erc721; +mod test_erc721_uri_storage; diff --git a/src/tests/utils/constants.cairo b/src/tests/utils/constants.cairo new file mode 100644 index 000000000..3b38cc4c3 --- /dev/null +++ b/src/tests/utils/constants.cairo @@ -0,0 +1,119 @@ +use openzeppelin::account::interface::EthPublicKey; +use starknet::ClassHash; +use starknet::ContractAddress; +use starknet::SyscallResultTrait; +use starknet::class_hash::class_hash_const; +use starknet::contract_address_const; +use starknet::secp256_trait::Secp256Trait; + +pub(crate) const DECIMALS: u8 = 18_u8; +pub(crate) const SUPPLY: u256 = 2000; +pub(crate) const VALUE: u256 = 300; +pub(crate) const ROLE: felt252 = 'ROLE'; +pub(crate) const OTHER_ROLE: felt252 = 'OTHER_ROLE'; +pub(crate) const TOKEN_ID: u256 = 21; +pub(crate) const TOKEN_ID_2: u256 = 121; +pub(crate) const TOKEN_VALUE: u256 = 42; +pub(crate) const TOKEN_VALUE_2: u256 = 142; +pub(crate) const PUBKEY: felt252 = 'PUBKEY'; +pub(crate) const NEW_PUBKEY: felt252 = + 0x26da8d11938b76025862be14fdb8b28438827f73e75e86f7bfa38b196951fa7; +pub(crate) const DAPP_NAME: felt252 = 'DAPP_NAME'; +pub(crate) const DAPP_VERSION: felt252 = 'DAPP_VERSION'; +pub(crate) const SALT: felt252 = 'SALT'; +pub(crate) const SUCCESS: felt252 = 123123; +pub(crate) const FAILURE: felt252 = 456456; +pub(crate) const MIN_TRANSACTION_VERSION: felt252 = 1; +// 2**128 +pub(crate) const QUERY_OFFSET: felt252 = 0x100000000000000000000000000000000; +// QUERY_OFFSET + MIN_TRANSACTION_VERSION +pub(crate) const QUERY_VERSION: felt252 = 0x100000000000000000000000000000001; + +pub(crate) fn NAME() -> ByteArray { + "NAME" +} + +pub(crate) fn SYMBOL() -> ByteArray { + "SYMBOL" +} + +pub(crate) fn BASE_URI() -> ByteArray { + "https://api.example.com/v1/" +} + +pub(crate) fn BASE_URI_2() -> ByteArray { + "https://api.example.com/v2/" +} +pub(crate) fn SAMPLE_URI() -> ByteArray { + "mock://mytoken" +} + +pub(crate) fn ETH_PUBKEY() -> EthPublicKey { + Secp256Trait::secp256_ec_get_point_from_x_syscall(3, false).unwrap_syscall().unwrap() +} + +pub(crate) fn NEW_ETH_PUBKEY() -> EthPublicKey { + Secp256Trait::secp256_ec_get_point_from_x_syscall(4, false).unwrap_syscall().unwrap() +} + +pub(crate) fn ADMIN() -> ContractAddress { + contract_address_const::<'ADMIN'>() +} + +pub(crate) fn AUTHORIZED() -> ContractAddress { + contract_address_const::<'AUTHORIZED'>() +} + +pub(crate) fn ZERO() -> ContractAddress { + contract_address_const::<0>() +} + +pub(crate) fn CLASS_HASH_ZERO() -> ClassHash { + class_hash_const::<0>() +} + +pub(crate) fn CALLER() -> ContractAddress { + contract_address_const::<'CALLER'>() +} + +pub(crate) fn OWNER() -> ContractAddress { + contract_address_const::<'OWNER'>() +} + +pub(crate) fn NEW_OWNER() -> ContractAddress { + contract_address_const::<'NEW_OWNER'>() +} + +pub(crate) fn OTHER() -> ContractAddress { + contract_address_const::<'OTHER'>() +} + +pub(crate) fn OTHER_ADMIN() -> ContractAddress { + contract_address_const::<'OTHER_ADMIN'>() +} + +pub(crate) fn SPENDER() -> ContractAddress { + contract_address_const::<'SPENDER'>() +} + +pub(crate) fn RECIPIENT() -> ContractAddress { + contract_address_const::<'RECIPIENT'>() +} + +pub(crate) fn OPERATOR() -> ContractAddress { + contract_address_const::<'OPERATOR'>() +} + +pub(crate) fn DATA(success: bool) -> Span { + let mut data = array![]; + if success { + data.append(SUCCESS); + } else { + data.append(FAILURE); + } + data.span() +} + +pub(crate) fn EMPTY_DATA() -> Span { + array![].span() +} From 14df649cfffd21545d075d4706fd0fa1c03afc75 Mon Sep 17 00:00:00 2001 From: gerceboss Date: Mon, 1 Jul 2024 02:02:03 +0530 Subject: [PATCH 04/22] included paths removed bugs --- packages/presets/src/tests/mocks.cairo | 1 + packages/token/src/erc721.cairo | 1 + .../extensions/erc721_uri_storage.cairo | 36 ++++++++--------- packages/token/src/erc721/interface.cairo | 40 +++++++++++++++++++ .../erc721/test_erc721_uri_storage.cairo | 8 ++-- .../mocks/erc721_uri_storage_mocks.cairo | 12 ++++-- 6 files changed, 73 insertions(+), 25 deletions(-) diff --git a/packages/presets/src/tests/mocks.cairo b/packages/presets/src/tests/mocks.cairo index 2037caeeb..2a2959d15 100644 --- a/packages/presets/src/tests/mocks.cairo +++ b/packages/presets/src/tests/mocks.cairo @@ -4,6 +4,7 @@ pub(crate) mod erc1155_receiver_mocks; pub(crate) mod erc20_mocks; pub(crate) mod erc721_mocks; pub(crate) mod erc721_receiver_mocks; +pub(crate) mod erc721_uri_storage_mocks; pub(crate) mod eth_account_mocks; pub(crate) mod non_implementing_mock; pub(crate) mod src5_mocks; diff --git a/packages/token/src/erc721.cairo b/packages/token/src/erc721.cairo index 028fdcdeb..28c210486 100644 --- a/packages/token/src/erc721.cairo +++ b/packages/token/src/erc721.cairo @@ -2,6 +2,7 @@ pub mod dual721; pub mod dual721_receiver; pub mod erc721; pub mod erc721_receiver; +pub mod extensions; pub mod interface; pub use erc721::ERC721Component; diff --git a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo index bad553228..0bb400aae 100644 --- a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo +++ b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo @@ -1,18 +1,15 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.14.0 (token/erc721/extensions/erc721_uri_storage.cairo) -use starknet::ContractAddress; - -pub trait IERC721URIstorage{ - fn token_uri(self: @ComponentState,token_id:u256)->ByteArray; - fn set_token_uri(ref self: ComponentState,token_id:u256,_token_uri:ByteArray) -} - #[starknet::component] pub mod ERC721URIstorageComponent { - use openzeppelin::token::erc721::ERC721Component; - use openzeppelin::token::erc721:InternalImpl as ERC721Impl; - use openzeppelin::token::erc721::interface::{IERC721,IERC721Metadata}; + use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; + use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; + use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::token::erc721::{ERC721Component,ERC721HooksEmptyImpl,ERC721HooksTrait}; + use openzeppelin::token::erc721::ERC721Component::InternalImpl as ERC721Impl; + use openzeppelin::token::erc721::ERC721Component::ERC721Metadata; + use openzeppelin::token::erc721::interface::{IERC721,IERC721Metadata,IERC721URIstorage}; use starknet::ContractAddress; #[storage] @@ -30,16 +27,18 @@ pub mod ERC721URIstorageComponent { #[derive(Drop, PartialEq, starknet::Event)] pub struct MetadataUpdate{ #[key] - pub token_id: u256, + token_id: u256, } #[embeddable_as(ERC721URIstorageImpl)] impl ERC721URIstorage< TContractState, +HasComponent, + +SRC5Component::HasComponent, +ERC721Component::HasComponent, + +ERC721HooksTrait, +Drop - > of super::IERC721URIstorage> { + > of IERC721URIstorage> { /// Returns the Uniform Resource Identifier (URI) for the `token_id` token. /// If the URI is not set, the return value will be an empty ByteArray. @@ -65,17 +64,18 @@ pub mod ERC721URIstorageComponent { pub impl InternalImpl< TContractState, +HasComponent, + impl SRC5: SRC5Component::HasComponent, impl ERC721: ERC721Component::HasComponent, - +ERC721Component::ERC721HooksTrait, + impl Hooks: ERC721HooksTrait, +Drop > of InternalTrait { /// Returns the `token_uri` for the `token_id` /// if needed, returns the concatenated string - fn _token_uri(self: @ComponentState,token_id:u256)-> ByteArray{ - ERC721Impl::_require_owned(token_id); + fn _token_uri(self: @ComponentState,token_id:u256)-> ByteArray{ let mut erc721_component= get_dep_component!(self, ERC721); - let base_uri:ByteArray=erc721_component._base_uri(); + ERC721Impl::_require_owned(erc721_component,token_id); + let base_uri:ByteArray=ERC721Impl::_base_uri(erc721_component); let token_uri:ByteArray= self.token_uris.read(token_id); if base_uri.len()==0{ return token_uri; @@ -83,13 +83,13 @@ pub mod ERC721URIstorageComponent { if token_uri.len()>0{ return format!("{}{}",base_uri,token_uri); } - return erc721_component.token_uri(token_id); + return ERC721Metadata::token_uri(erc721_component,token_id); } /// Sets or updates the `token_uri` for the respective `token_uri` /// /// Emits `MetadataUpdate` event - fn _set_token_uri(ref self: ComponentState,token_id:u256,_token_uri:ByteArray){ + fn _set_token_uri(ref self: ComponentState,token_id:u256,_token_uri:ByteArray){ self.token_uris.write(token_id,_token_uri); self.emit(MetadataUpdate{token_id:token_id}); } diff --git a/packages/token/src/erc721/interface.cairo b/packages/token/src/erc721/interface.cairo index 26f4cecac..cefed5013 100644 --- a/packages/token/src/erc721/interface.cairo +++ b/packages/token/src/erc721/interface.cairo @@ -58,6 +58,12 @@ pub trait IERC721MetadataCamelOnly { fn tokenURI(self: @TState, tokenId: u256) -> ByteArray; } +#[starknet::interface] +pub trait IERC721URIstorage{ + fn token_uri(self: @TState,token_id:u256)->ByteArray; + fn set_token_uri(ref self: TState,token_id:u256,_token_uri:ByteArray); +} + // // ERC721 ABI // @@ -109,6 +115,40 @@ pub trait ERC721ABI { fn tokenURI(self: @TState, tokenId: u256) -> ByteArray; } +// +// ERC721URIstorage ABI +// + +#[starknet::interface] +pub trait ERC721URIstorageABI { + // IERC721 + fn balance_of(self: @TState, account: ContractAddress) -> u256; + fn owner_of(self: @TState, token_id: u256) -> ContractAddress; + fn safe_transfer_from( + ref self: TState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ); + fn transfer_from(ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256); + fn approve(ref self: TState, to: ContractAddress, token_id: u256); + fn set_approval_for_all(ref self: TState, operator: ContractAddress, approved: bool); + fn get_approved(self: @TState, token_id: u256) -> ContractAddress; + fn is_approved_for_all( + self: @TState, owner: ContractAddress, operator: ContractAddress + ) -> bool; + + // IERC721Metadata + // fn name(self: @TState) -> ByteArray; + // fn symbol(self: @TState) -> ByteArray; + // fn decimals(self: @TState) -> u8; + + // IERC721URIstorage + fn token_uri(self: @TState,token_id:u256)->ByteArray; + fn set_token_uri(ref self: TState,token_id:u256,_token_uri:ByteArray); +} + // // ERC721Receiver // diff --git a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo index a3085d0b3..13d73d838 100644 --- a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo +++ b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo @@ -1,15 +1,17 @@ use openzeppelin::tests::mocks::erc721_uri_storage_mocks::ERC721URIstorageMock; use openzeppelin::tests::utils::constants::{ - DATA, ZERO, OWNER, CALLER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, TOKEN_ID, + DATA, ZERO, OWNER, RECIPIENT, NAME, SYMBOL, TOKEN_ID, TOKEN_ID_2, PUBKEY, BASE_URI, BASE_URI_2,SAMPLE_URI }; use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; use openzeppelin::introspection::src5; use openzeppelin::introspection; -use openzeppelin::token::erc721::ERC721Component::{ERC721MetadataImpl, InternalImpl}; +use openzeppelin::token::erc721::ERC721Component::ERC721MetadataImpl; +use openzeppelin::token::erc721::ERC721Component::InternalImpl as ERC721Impl; use openzeppelin::token::erc721::ERC721Component; +use openzeppelin::token::erc721::extensions::erc721_uri_storage::MetadataUpdate; use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent; -use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent::ERC721URIstorageImpl; +use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent::{ERC721URIstorageImpl,InternalImpl}; use openzeppelin::token::erc721::interface::IERC721; use openzeppelin::token::erc721; diff --git a/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo b/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo index 006afa895..6d2d1e01c 100644 --- a/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo +++ b/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo @@ -2,13 +2,18 @@ pub(crate) mod ERC721URIstorageMock { use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::token::erc721::ERC721Component; - use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent::InternalTrait as ERC20VotesInternalTrait; + use openzeppelin::token::erc721::extensions::ERC721Component::InternalImpl; use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent; use starknet::ContractAddress; + component!(path: ERC721URIstorageComponent, storage: erc721_uri_storage, event: ERC721URIstorageEvent); component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); + //ERC721URIstorage + #[abi(embed_v0)] + impl ERC721URIstorageImpl=ERC721URIstorageComponent::ERC721URIstorageImpl; + // ERC721 #[abi(embed_v0)] impl ERC721Impl = ERC721Component::ERC721Impl; @@ -20,9 +25,6 @@ pub(crate) mod ERC721URIstorageMock { #[abi(embed_v0)] impl SRC5Impl = SRC5Component::SRC5Impl; - //ERC721URIstorage - #[abi(embed_v0)] - impl ERC721URIstorage=ERC721URIstorageComponent::ERC721URIstorageImpl #[storage] struct Storage { @@ -39,6 +41,8 @@ pub(crate) mod ERC721URIstorageMock { ERC721URIstorageEvent: ERC721URIstorageComponent::Event, #[flat] ERC721Event: ERC721Component::Event, + #[flat] + SRC5Event: SRC5Component::Event, } #[constructor] From 1421c64a4341f8a81b2d00c20c8e03096bd882b9 Mon Sep 17 00:00:00 2001 From: gerceboss Date: Tue, 2 Jul 2024 00:04:04 +0530 Subject: [PATCH 05/22] fixed implementation of ERC721URIstorage extension --- .../extensions/erc721_uri_storage.cairo | 51 ++++++++++++------- packages/token/src/erc721/interface.cairo | 15 ++---- .../erc721/test_erc721_uri_storage.cairo | 40 ++++++++------- .../mocks/erc721_uri_storage_mocks.cairo | 9 ++-- 4 files changed, 65 insertions(+), 50 deletions(-) diff --git a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo index 0bb400aae..41b390842 100644 --- a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo +++ b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo @@ -6,10 +6,9 @@ pub mod ERC721URIstorageComponent { use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc721::{ERC721Component,ERC721HooksEmptyImpl,ERC721HooksTrait}; + use openzeppelin::token::erc721::{ERC721Component,ERC721HooksEmptyImpl}; use openzeppelin::token::erc721::ERC721Component::InternalImpl as ERC721Impl; - use openzeppelin::token::erc721::ERC721Component::ERC721Metadata; - use openzeppelin::token::erc721::interface::{IERC721,IERC721Metadata,IERC721URIstorage}; + use openzeppelin::token::erc721::interface::{IERC721,IERC721Metadata}; use starknet::ContractAddress; #[storage] @@ -27,7 +26,7 @@ pub mod ERC721URIstorageComponent { #[derive(Drop, PartialEq, starknet::Event)] pub struct MetadataUpdate{ #[key] - token_id: u256, + pub token_id: u256, } #[embeddable_as(ERC721URIstorageImpl)] @@ -36,10 +35,16 @@ pub mod ERC721URIstorageComponent { +HasComponent, +SRC5Component::HasComponent, +ERC721Component::HasComponent, - +ERC721HooksTrait, +Drop - > of IERC721URIstorage> { + > of IERC721Metadata> { + fn name(self:@ComponentState)->ByteArray{ + self._name() + } + + fn symbol(self:@ComponentState)->ByteArray{ + self._symbol() + } /// Returns the Uniform Resource Identifier (URI) for the `token_id` token. /// If the URI is not set, the return value will be an empty ByteArray. /// @@ -49,11 +54,6 @@ pub mod ERC721URIstorageComponent { fn token_uri(self: @ComponentState,token_id:u256)->ByteArray{ self._token_uri(token_id) } - - /// Sets the Uniform Resource Identifier (URI) for the `token_id` token. - fn set_token_uri(ref self: ComponentState,token_id:u256,_token_uri:ByteArray){ - self._set_token_uri(token_id,_token_uri); - } } // @@ -66,10 +66,21 @@ pub mod ERC721URIstorageComponent { +HasComponent, impl SRC5: SRC5Component::HasComponent, impl ERC721: ERC721Component::HasComponent, - impl Hooks: ERC721HooksTrait, +Drop > of InternalTrait { + fn _name(self:@ComponentState)->ByteArray{ + let erc721_component= get_dep_component!(self, ERC721); + let name=erc721_component.ERC721_name.read(); + return name; + } + + fn _symbol(self:@ComponentState)->ByteArray{ + let erc721_component= get_dep_component!(self, ERC721); + let symbol=erc721_component.ERC721_symbol.read(); + return symbol; + } + /// Returns the `token_uri` for the `token_id` /// if needed, returns the concatenated string fn _token_uri(self: @ComponentState,token_id:u256)-> ByteArray{ @@ -83,17 +94,21 @@ pub mod ERC721URIstorageComponent { if token_uri.len()>0{ return format!("{}{}",base_uri,token_uri); } - return ERC721Metadata::token_uri(erc721_component,token_id); + + //token_uri implementation from the ERC721Metadata + if base_uri.len() == 0 { + return ""; + } else { + return format!("{}{}", base_uri, token_id); + } } /// Sets or updates the `token_uri` for the respective `token_uri` /// /// Emits `MetadataUpdate` event - fn _set_token_uri(ref self: ComponentState,token_id:u256,_token_uri:ByteArray){ - self.token_uris.write(token_id,_token_uri); + fn set_token_uri(ref self: ComponentState,token_id:u256,token_uri:ByteArray){ + self.token_uris.write(token_id,token_uri); self.emit(MetadataUpdate{token_id:token_id}); } } -} - - +} \ No newline at end of file diff --git a/packages/token/src/erc721/interface.cairo b/packages/token/src/erc721/interface.cairo index cefed5013..cadb44152 100644 --- a/packages/token/src/erc721/interface.cairo +++ b/packages/token/src/erc721/interface.cairo @@ -58,11 +58,6 @@ pub trait IERC721MetadataCamelOnly { fn tokenURI(self: @TState, tokenId: u256) -> ByteArray; } -#[starknet::interface] -pub trait IERC721URIstorage{ - fn token_uri(self: @TState,token_id:u256)->ByteArray; - fn set_token_uri(ref self: TState,token_id:u256,_token_uri:ByteArray); -} // // ERC721 ABI @@ -140,13 +135,11 @@ pub trait ERC721URIstorageABI { ) -> bool; // IERC721Metadata - // fn name(self: @TState) -> ByteArray; - // fn symbol(self: @TState) -> ByteArray; - // fn decimals(self: @TState) -> u8; - - // IERC721URIstorage + fn name(self: @TState) -> ByteArray; + fn symbol(self: @TState) -> ByteArray; fn token_uri(self: @TState,token_id:u256)->ByteArray; - fn set_token_uri(ref self: TState,token_id:u256,_token_uri:ByteArray); + + // fn set_token_uri(ref self: TState,token_id:u256,_token_uri:ByteArray); } // diff --git a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo index 13d73d838..90263ce21 100644 --- a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo +++ b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo @@ -6,10 +6,10 @@ use openzeppelin::tests::utils::constants::{ use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; use openzeppelin::introspection::src5; use openzeppelin::introspection; -use openzeppelin::token::erc721::ERC721Component::ERC721MetadataImpl; -use openzeppelin::token::erc721::ERC721Component::InternalImpl as ERC721Impl; +use openzeppelin::token::erc721::ERC721Component::InternalImpl as ERC721InternalImpl; use openzeppelin::token::erc721::ERC721Component; -use openzeppelin::token::erc721::extensions::erc721_uri_storage::MetadataUpdate; +use openzeppelin::token::erc721::ERC721Component::ERC721Impl; +use openzeppelin::token::erc721::extensions::erc721_uri_storage::ERC721URIstorageComponent::MetadataUpdate; use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent; use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent::{ERC721URIstorageImpl,InternalImpl}; @@ -40,9 +40,9 @@ fn COMPONENT_STATE() -> ComponentState { //constructor is inside only fn setup() -> ComponentState { let mut state = COMPONENT_STATE(); - let mock_state=CONTRACT_STATE(); + let mut mock_state=CONTRACT_STATE(); mock_state.erc721.initializer(NAME(), SYMBOL(), BASE_URI()); - state.mint(OWNER(),TOKEN_ID); + mock_state.erc721.mint(OWNER(),TOKEN_ID); utils::drop_event(ZERO()); state } @@ -64,9 +64,9 @@ fn test_token_uri_non_minted() { #[test] fn test_set_token_uri(){ - let state=setup(); + let mut state=setup(); - state.set_token_uri(TOKEN_ID,SAMPLE_URI()); + state.set_token_uri(TOKEN_ID,SAMPLE_URI());//internal function? assert_only_event_metadata_update(ZERO(),TOKEN_ID);//checking event is emmitted or not let expected= format!("{}",SAMPLE_URI()); @@ -82,9 +82,9 @@ fn test_set_token_uri_nonexistent(){ state.set_token_uri(TOKEN_ID_2,SAMPLE_URI()); assert_only_event_metadata_update(ZERO(),TOKEN_ID_2);//checking event is emitted or not - + let mut mock_contract_state=CONTRACT_STATE(); //check accesible after minting - state.mint(RECIPIENT(),TOKEN_ID_2); + mock_contract_state.erc721.mint(RECIPIENT(),TOKEN_ID_2); let expected= format!("{}",SAMPLE_URI()); let uri=state.token_uri(TOKEN_ID); @@ -94,10 +94,12 @@ fn test_set_token_uri_nonexistent(){ #[test] fn test_set_base_uri(){ - let mut state=setup();//its the component state + let mut _state=setup();//its the component state - state._set_base_uri(BASE_URI()); - let base_uri=state._base_uri(); + let mut mock_contract_state=CONTRACT_STATE(); + mock_contract_state.erc721._set_base_uri(BASE_URI()); + + let base_uri=mock_contract_state.erc721._base_uri(); //internal of ERC721 assert_eq!(base_uri, BASE_URI()); } @@ -106,7 +108,8 @@ fn test_set_base_uri(){ fn test_base_uri_is_prefix(){ let mut state=setup();//its the component state - state._set_base_uri(BASE_URI()); + let mut mock_contract_state=CONTRACT_STATE(); + mock_contract_state.erc721._set_base_uri(BASE_URI()); state.set_token_uri(TOKEN_ID,SAMPLE_URI()); let token_uri=state.token_uri(TOKEN_ID); @@ -118,7 +121,9 @@ fn test_base_uri_is_prefix(){ fn test_base_uri_2_is_set_as_prefix(){ let mut state=setup();//its the component state - state._set_base_uri(BASE_URI_2()); + let mut mock_contract_state=CONTRACT_STATE(); + mock_contract_state.erc721._set_base_uri(BASE_URI_2()); + state.set_token_uri(TOKEN_ID,SAMPLE_URI()); let token_uri=state.token_uri(TOKEN_ID); @@ -130,7 +135,8 @@ fn test_base_uri_2_is_set_as_prefix(){ fn test_base_uri_and_token_id(){ let mut state=setup();//its the component state - state._set_base_uri(BASE_URI()); + let mut mock_contract_state=CONTRACT_STATE(); + mock_contract_state.erc721._set_base_uri(BASE_URI()); let token_uri=state.token_uri(TOKEN_ID); let expected=format!("{}{}",BASE_URI(),TOKEN_ID); @@ -140,7 +146,7 @@ fn test_base_uri_and_token_id(){ // // Helpers // -fn assert_event_metadata_update(contract:ContractAddress,token_id:u256){ +pub fn assert_event_metadata_update(contract: ContractAddress,token_id: u256){ let event=utils::pop_log::(contract).unwrap(); let expected=ERC721URIstorageComponent::Event::MetadataUpdate( MetadataUpdate{token_id} @@ -154,7 +160,7 @@ fn assert_event_metadata_update(contract:ContractAddress,token_id:u256){ utils::assert_indexed_keys(event,indexed_keys.span()) } -fn assert_only_event_metadata_update( +pub fn assert_only_event_metadata_update( contract: ContractAddress, token_id : u256 ) { assert_event_metadata_update(contract, token_id); diff --git a/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo b/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo index 6d2d1e01c..87ea3f74b 100644 --- a/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo +++ b/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo @@ -1,9 +1,10 @@ #[starknet::contract] pub(crate) mod ERC721URIstorageMock { use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc721::ERC721Component; - use openzeppelin::token::erc721::extensions::ERC721Component::InternalImpl; + use openzeppelin::token::erc721::{ERC721Component,ERC721HooksEmptyImpl}; + // use openzeppelin::token::erc721::ERC721Component::InternalImpl; use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent; + // use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent::InternalImpl; use starknet::ContractAddress; component!(path: ERC721URIstorageComponent, storage: erc721_uri_storage, event: ERC721URIstorageEvent); @@ -18,8 +19,6 @@ pub(crate) mod ERC721URIstorageMock { #[abi(embed_v0)] impl ERC721Impl = ERC721Component::ERC721Impl; impl ERC721InternalImpl = ERC721Component::InternalImpl; - #[abi(embed_v0)] - impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; // SRC5 #[abi(embed_v0)] @@ -28,6 +27,8 @@ pub(crate) mod ERC721URIstorageMock { #[storage] struct Storage { + #[substorage(v0)] + erc721_uri_storage: ERC721URIstorageComponent::Storage, #[substorage(v0)] erc721: ERC721Component::Storage, #[substorage(v0)] From 7e13d49f41e5f122d098bae536e3a423803438a3 Mon Sep 17 00:00:00 2001 From: gerceboss Date: Tue, 2 Jul 2024 01:35:25 +0530 Subject: [PATCH 06/22] updated documentation --- docs/modules/ROOT/pages/api/erc721.adoc | 111 ++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 09ab75d29..0dccde9cb 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -689,6 +689,117 @@ See <>. See <>. + +== Extensions + +[.contract] +[[ERC721URIstorageComponent]] +=== `++ERC721URIstorageComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc721/extensions/erc721_uri_storage.cairo[{github-icon},role=heading-link] + +```cairo +use openzeppelin::token::extensions::ERC721URIstorageComponent; +``` + +:MetadataUpdate: xref:ERC721URIstorageComponent-MetadataUpdate[MetadataUpdate] + +Extension of ERC721 to support dynamic NFTs. It is an implementation of <> but with a different `token_uri` behavior + +NOTE: Implementing xref:#ERC721Component[ERC721Component] is a requirement for this component to be implemented. + +This extension keeps a track of URIs of each `token_id` of a particular ERC721 token. The `token_uri` of any `token_id` can be set by calling the +internal function, xref:#ERC721URIstorageComponent-set_token_uri[set_token_uri]. Updated `token_uri` can be queried through the external function +xref:#ERC721URIstorageComponent-token_uri[token_uri] or the internal function xref:#ERC721URIstorageComponent-_token_uri[_token_uri]. + + +[.contract-index#ERC721URIstorageComponent-Embeddable-Impls] +.Embeddable Implementations +-- +[.sub-index#ERC721URIstorageComponent-Embeddable-Impls-ERC721URIstorageImpl] +.ERC721URIstorageImpl +* xref:#ERC721URIstorageComponent-name[`++name(self)++`] +* xref:#ERC721URIstorageComponent-symbol[`++symbol(self)++`] +* xref:#ERC721URIstorageComponent-token_uri[`++token_uri(self,token_id)++`] + +-- + +[.contract-index] +.Internal implementations +-- +.InternalImpl +* xref:#ERC721URIstorageComponent-_name[`++_name(self)++`] +* xref:#ERC721URIstorageComponent-_symbol[`++_symbol(self)++`] +* xref:#ERC721URIstorageComponent-_token_uri[`++_token_uri(self,token_id)++`] +* xref:#ERC721URIstorageComponent-set_token_uri[`++set_token_uri(self,token_id,token_uri)++`] + +-- + +[.contract-index] +.Events +-- +* xref:#ERC721URIstorageComponent-MetadataUpdate[`++MetadataUpdate(token_id)++`] +-- + +[#ERC721URIstorageComponent-Embeddable-functions] +==== Embeddable functions + +[.contract-item] +[[ERC721URIstorageComponent-name]] +==== `[.contract-item-name]#++name++#++(self: @ContractState) → ByteArray++` [.item-kind]#external# + +See <>. + +[.contract-item] +[[ERC721URIstorageComponent-symbol]] +==== `[.contract-item-name]#++symbol++#++(self: @ContractState) → ByteArray++` [.item-kind]#external# + +See <>. + +[.contract-item] +[[ERC721URIstorageComponent-token_uri]] +==== `[.contract-item-name]#++token_uri++#++(self: @ContractState, token_id : u256) → ByteArray++` [.item-kind]#external# + +See <> + +[#ERC721URIstorageComponent-Internal-functions] +==== Internal functions + +[.contract-item] +[[ERC721URIstorageComponent-name]] +==== `[.contract-item-name]#++name++#++(self: @ContractState) → ByteArray++` [.item-kind]#internall# + +See <>. + +[.contract-item] +[[ERC721URIstorageComponent-symbol]] +==== `[.contract-item-name]#++symbol++#++(self: @ContractState) → ByteArray++` [.item-kind]#internal# + +See <>. + +[.contract-item] +[[ERC721URIstorageComponent-_token_uri]] +==== `[.contract-item-name]#++_token_uri++#++(self: @ContractState, token_id: u256) → ByteArray++` [.item-kind]#internal# + +Returns the Uniform Resource Identifier (URI) for the `token_id` token. If a base URI is set, the resulting URI for each token will be the concatenation of the base URI and the token ID. For example, the base URI `https://token-cdn-domain/` would be returned as `https://token-cdn-domain/123` for token ID `123`. + +If the URI is not set for `token_id`, the return value will be an empty `ByteArray`. + +[.contract-item] +[[ERC721URIstorageComponent-se_token_uri]] +==== `[.contract-item-name]#++set_token_uri++#++(ref self: ContractState,token_id: u256,token_uri: ByteArray)++` [.item-kind]#internal# + +Sets the `token_uri` of `token_id`. + +Emits a {MetadataUpdate} event. + +[#ERC721URIstorageComponent-Events] +==== Events + +[.contract-item] +[[ERC721URIstorageComponent-MetadataUpdate]] +==== `[.contract-item-name]#++MetadataUpdate++#++(token_id: u256)++` [.item-kind]#event# + +Emitted when `token_uri` of `token_id` is set. + == Receiver [.contract] From fc034224e3ffac78a05a94ba91e7bd414f554dce Mon Sep 17 00:00:00 2001 From: gerceboss Date: Tue, 2 Jul 2024 01:42:46 +0530 Subject: [PATCH 07/22] updated documentation --- docs/modules/ROOT/pages/api/erc721.adoc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 0dccde9cb..994d7028a 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -779,9 +779,14 @@ See <>. [[ERC721URIstorageComponent-_token_uri]] ==== `[.contract-item-name]#++_token_uri++#++(self: @ContractState, token_id: u256) → ByteArray++` [.item-kind]#internal# -Returns the Uniform Resource Identifier (URI) for the `token_id` token. If a base URI is set, the resulting URI for each token will be the concatenation of the base URI and the token ID. For example, the base URI `https://token-cdn-domain/` would be returned as `https://token-cdn-domain/123` for token ID `123`. +Returns the Uniform Resource Identifier (URI) for the `token_id` token. -If the URI is not set for `token_id`, the return value will be an empty `ByteArray`. + +If a base URI is set and the token URI is set , the resulting URI for each token will be the concatenation of the base URI and the token URI. +If a base URI is set and the token URI is not set , the resulting URI for each token will be the concatenation of the base URI and the token ID. +If a base URI is not set and the token URI is set , the resulting URI for each token will be the token URI. + +If the base URI and token URI is not set for `token_id`, the return value will be an empty `ByteArray`. [.contract-item] [[ERC721URIstorageComponent-se_token_uri]] From ff4afb9943b12f5f59e51538865f8c168efea94d Mon Sep 17 00:00:00 2001 From: gerceboss Date: Tue, 2 Jul 2024 01:49:09 +0530 Subject: [PATCH 08/22] removed unused imports --- packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo b/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo index 87ea3f74b..5f5ea39ea 100644 --- a/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo +++ b/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo @@ -2,9 +2,7 @@ pub(crate) mod ERC721URIstorageMock { use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::token::erc721::{ERC721Component,ERC721HooksEmptyImpl}; - // use openzeppelin::token::erc721::ERC721Component::InternalImpl; use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent; - // use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent::InternalImpl; use starknet::ContractAddress; component!(path: ERC721URIstorageComponent, storage: erc721_uri_storage, event: ERC721URIstorageEvent); From ec6ab2e6ebad4bd914f940f0f935c99d034cc597 Mon Sep 17 00:00:00 2001 From: gerceboss Date: Tue, 2 Jul 2024 02:04:50 +0530 Subject: [PATCH 09/22] fixed typos lints --- docs/modules/ROOT/pages/api/erc721.adoc | 2 +- .../extensions/erc721_uri_storage.cairo | 60 +++---- packages/token/src/erc721/interface.cairo | 4 +- .../erc721/test_erc721_uri_storage.cairo | 150 +++++++++--------- .../mocks/erc721_uri_storage_mocks.cairo | 11 +- 5 files changed, 113 insertions(+), 114 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 994d7028a..d0a0258f7 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -765,7 +765,7 @@ See <>. diff --git a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo index 41b390842..220cc5811 100644 --- a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo +++ b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo @@ -6,25 +6,25 @@ pub mod ERC721URIstorageComponent { use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc721::{ERC721Component,ERC721HooksEmptyImpl}; use openzeppelin::token::erc721::ERC721Component::InternalImpl as ERC721Impl; - use openzeppelin::token::erc721::interface::{IERC721,IERC721Metadata}; + use openzeppelin::token::erc721::interface::{IERC721, IERC721Metadata}; + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; use starknet::ContractAddress; #[storage] struct Storage { - token_uris:LegacyMap, + token_uris: LegacyMap, } #[event] #[derive(Drop, PartialEq, starknet::Event)] pub enum Event { - MetadataUpdate:MetadataUpdate, + MetadataUpdate: MetadataUpdate, } /// Emitted when `token_uri` is changed for `token_id` #[derive(Drop, PartialEq, starknet::Event)] - pub struct MetadataUpdate{ + pub struct MetadataUpdate { #[key] pub token_id: u256, } @@ -37,12 +37,11 @@ pub mod ERC721URIstorageComponent { +ERC721Component::HasComponent, +Drop > of IERC721Metadata> { - - fn name(self:@ComponentState)->ByteArray{ + fn name(self: @ComponentState) -> ByteArray { self._name() } - - fn symbol(self:@ComponentState)->ByteArray{ + + fn symbol(self: @ComponentState) -> ByteArray { self._symbol() } /// Returns the Uniform Resource Identifier (URI) for the `token_id` token. @@ -51,7 +50,7 @@ pub mod ERC721URIstorageComponent { /// Requirements: /// /// - `token_id` exists. - fn token_uri(self: @ComponentState,token_id:u256)->ByteArray{ + fn token_uri(self: @ComponentState, token_id: u256) -> ByteArray { self._token_uri(token_id) } } @@ -68,31 +67,30 @@ pub mod ERC721URIstorageComponent { impl ERC721: ERC721Component::HasComponent, +Drop > of InternalTrait { - - fn _name(self:@ComponentState)->ByteArray{ - let erc721_component= get_dep_component!(self, ERC721); - let name=erc721_component.ERC721_name.read(); + fn _name(self: @ComponentState) -> ByteArray { + let erc721_component = get_dep_component!(self, ERC721); + let name = erc721_component.ERC721_name.read(); return name; } - - fn _symbol(self:@ComponentState)->ByteArray{ - let erc721_component= get_dep_component!(self, ERC721); - let symbol=erc721_component.ERC721_symbol.read(); + + fn _symbol(self: @ComponentState) -> ByteArray { + let erc721_component = get_dep_component!(self, ERC721); + let symbol = erc721_component.ERC721_symbol.read(); return symbol; } /// Returns the `token_uri` for the `token_id` /// if needed, returns the concatenated string - fn _token_uri(self: @ComponentState,token_id:u256)-> ByteArray{ - let mut erc721_component= get_dep_component!(self, ERC721); - ERC721Impl::_require_owned(erc721_component,token_id); - let base_uri:ByteArray=ERC721Impl::_base_uri(erc721_component); - let token_uri:ByteArray= self.token_uris.read(token_id); - if base_uri.len()==0{ + fn _token_uri(self: @ComponentState, token_id: u256) -> ByteArray { + let mut erc721_component = get_dep_component!(self, ERC721); + ERC721Impl::_require_owned(erc721_component, token_id); + let base_uri: ByteArray = ERC721Impl::_base_uri(erc721_component); + let token_uri: ByteArray = self.token_uris.read(token_id); + if base_uri.len() == 0 { return token_uri; } - if token_uri.len()>0{ - return format!("{}{}",base_uri,token_uri); + if token_uri.len() > 0 { + return format!("{}{}", base_uri, token_uri); } //token_uri implementation from the ERC721Metadata @@ -106,9 +104,11 @@ pub mod ERC721URIstorageComponent { /// Sets or updates the `token_uri` for the respective `token_uri` /// /// Emits `MetadataUpdate` event - fn set_token_uri(ref self: ComponentState,token_id:u256,token_uri:ByteArray){ - self.token_uris.write(token_id,token_uri); - self.emit(MetadataUpdate{token_id:token_id}); + fn set_token_uri( + ref self: ComponentState, token_id: u256, token_uri: ByteArray + ) { + self.token_uris.write(token_id, token_uri); + self.emit(MetadataUpdate { token_id: token_id }); } } -} \ No newline at end of file +} diff --git a/packages/token/src/erc721/interface.cairo b/packages/token/src/erc721/interface.cairo index cadb44152..8d5b69739 100644 --- a/packages/token/src/erc721/interface.cairo +++ b/packages/token/src/erc721/interface.cairo @@ -137,9 +137,7 @@ pub trait ERC721URIstorageABI { // IERC721Metadata fn name(self: @TState) -> ByteArray; fn symbol(self: @TState) -> ByteArray; - fn token_uri(self: @TState,token_id:u256)->ByteArray; - - // fn set_token_uri(ref self: TState,token_id:u256,_token_uri:ByteArray); + fn token_uri(self: @TState, token_id: u256) -> ByteArray; } // diff --git a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo index 90263ce21..36a09efdd 100644 --- a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo +++ b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo @@ -1,21 +1,23 @@ -use openzeppelin::tests::mocks::erc721_uri_storage_mocks::ERC721URIstorageMock; -use openzeppelin::tests::utils::constants::{ - DATA, ZERO, OWNER, RECIPIENT, NAME, SYMBOL, TOKEN_ID, - TOKEN_ID_2, PUBKEY, BASE_URI, BASE_URI_2,SAMPLE_URI -}; use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; use openzeppelin::introspection::src5; use openzeppelin::introspection; +use openzeppelin::tests::mocks::erc721_uri_storage_mocks::ERC721URIstorageMock; +use openzeppelin::tests::utils::constants::{ + DATA, ZERO, OWNER, RECIPIENT, NAME, SYMBOL, TOKEN_ID, TOKEN_ID_2, PUBKEY, BASE_URI, BASE_URI_2, + SAMPLE_URI +}; +use openzeppelin::tests::utils; +use openzeppelin::token::erc721::ERC721Component::ERC721Impl; use openzeppelin::token::erc721::ERC721Component::InternalImpl as ERC721InternalImpl; use openzeppelin::token::erc721::ERC721Component; -use openzeppelin::token::erc721::ERC721Component::ERC721Impl; -use openzeppelin::token::erc721::extensions::erc721_uri_storage::ERC721URIstorageComponent::MetadataUpdate; +use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent::{ + ERC721URIstorageImpl, InternalImpl +}; use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent; -use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent::{ERC721URIstorageImpl,InternalImpl}; +use openzeppelin::token::erc721::extensions::erc721_uri_storage::ERC721URIstorageComponent::MetadataUpdate; use openzeppelin::token::erc721::interface::IERC721; use openzeppelin::token::erc721; -use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; use starknet::ContractAddress; use starknet::contract_address_const; @@ -23,12 +25,12 @@ use starknet::storage::{StorageMapMemberAccessTrait, StorageMemberAccessTrait}; use starknet::testing; - // // Setup // -type ComponentState = ERC721URIstorageComponent::ComponentState; +type ComponentState = + ERC721URIstorageComponent::ComponentState; fn CONTRACT_STATE() -> ERC721URIstorageMock::ContractState { ERC721URIstorageMock::contract_state_for_testing() @@ -40,9 +42,9 @@ fn COMPONENT_STATE() -> ComponentState { //constructor is inside only fn setup() -> ComponentState { let mut state = COMPONENT_STATE(); - let mut mock_state=CONTRACT_STATE(); + let mut mock_state = CONTRACT_STATE(); mock_state.erc721.initializer(NAME(), SYMBOL(), BASE_URI()); - mock_state.erc721.mint(OWNER(),TOKEN_ID); + mock_state.erc721.mint(OWNER(), TOKEN_ID); utils::drop_event(ZERO()); state } @@ -63,106 +65,102 @@ fn test_token_uri_non_minted() { } #[test] -fn test_set_token_uri(){ - let mut state=setup(); - - state.set_token_uri(TOKEN_ID,SAMPLE_URI());//internal function? - assert_only_event_metadata_update(ZERO(),TOKEN_ID);//checking event is emmitted or not - - let expected= format!("{}",SAMPLE_URI()); - let uri=state.token_uri(TOKEN_ID); - - assert_eq!(uri,expected); +fn test_set_token_uri() { + let mut state = setup(); + + state.set_token_uri(TOKEN_ID, SAMPLE_URI()); //internal function? + assert_only_event_metadata_update(ZERO(), TOKEN_ID); //checking event is emitted or not + + let expected = format!("{}", SAMPLE_URI()); + let uri = state.token_uri(TOKEN_ID); + + assert_eq!(uri, expected); } #[test] -fn test_set_token_uri_nonexistent(){ - let mut state=setup(); - - state.set_token_uri(TOKEN_ID_2,SAMPLE_URI()); - assert_only_event_metadata_update(ZERO(),TOKEN_ID_2);//checking event is emitted or not - - let mut mock_contract_state=CONTRACT_STATE(); - //check accesible after minting - mock_contract_state.erc721.mint(RECIPIENT(),TOKEN_ID_2); - - let expected= format!("{}",SAMPLE_URI()); - let uri=state.token_uri(TOKEN_ID); - - assert_eq!(uri,expected); +fn test_set_token_uri_nonexistent() { + let mut state = setup(); + + state.set_token_uri(TOKEN_ID_2, SAMPLE_URI()); + assert_only_event_metadata_update(ZERO(), TOKEN_ID_2); //checking event is emitted or not + + let mut mock_contract_state = CONTRACT_STATE(); + //check accessible after minting + mock_contract_state.erc721.mint(RECIPIENT(), TOKEN_ID_2); + + let expected = format!("{}", SAMPLE_URI()); + let uri = state.token_uri(TOKEN_ID); + + assert_eq!(uri, expected); } #[test] -fn test_set_base_uri(){ - let mut _state=setup();//its the component state +fn test_set_base_uri() { + let mut _state = setup(); //its the component state - let mut mock_contract_state=CONTRACT_STATE(); + let mut mock_contract_state = CONTRACT_STATE(); mock_contract_state.erc721._set_base_uri(BASE_URI()); - - let base_uri=mock_contract_state.erc721._base_uri(); //internal of ERC721 - - assert_eq!(base_uri, BASE_URI()); + + let base_uri = mock_contract_state.erc721._base_uri(); //internal of ERC721 + + assert_eq!(base_uri, BASE_URI()); } #[test] -fn test_base_uri_is_prefix(){ - let mut state=setup();//its the component state +fn test_base_uri_is_prefix() { + let mut state = setup(); //its the component state - let mut mock_contract_state=CONTRACT_STATE(); + let mut mock_contract_state = CONTRACT_STATE(); mock_contract_state.erc721._set_base_uri(BASE_URI()); - state.set_token_uri(TOKEN_ID,SAMPLE_URI()); + state.set_token_uri(TOKEN_ID, SAMPLE_URI()); - let token_uri=state.token_uri(TOKEN_ID); - let expected=format!("{}{}",BASE_URI(),SAMPLE_URI()); - assert_eq!(token_uri,expected); + let token_uri = state.token_uri(TOKEN_ID); + let expected = format!("{}{}", BASE_URI(), SAMPLE_URI()); + assert_eq!(token_uri, expected); } #[test] -fn test_base_uri_2_is_set_as_prefix(){ - let mut state=setup();//its the component state +fn test_base_uri_2_is_set_as_prefix() { + let mut state = setup(); //its the component state - let mut mock_contract_state=CONTRACT_STATE(); + let mut mock_contract_state = CONTRACT_STATE(); mock_contract_state.erc721._set_base_uri(BASE_URI_2()); - state.set_token_uri(TOKEN_ID,SAMPLE_URI()); + state.set_token_uri(TOKEN_ID, SAMPLE_URI()); - let token_uri=state.token_uri(TOKEN_ID); - let expected=format!("{}{}",BASE_URI_2(),SAMPLE_URI()); - assert_eq!(token_uri,expected); + let token_uri = state.token_uri(TOKEN_ID); + let expected = format!("{}{}", BASE_URI_2(), SAMPLE_URI()); + assert_eq!(token_uri, expected); } #[test] -fn test_base_uri_and_token_id(){ - let mut state=setup();//its the component state +fn test_base_uri_and_token_id() { + let mut state = setup(); //its the component state - let mut mock_contract_state=CONTRACT_STATE(); + let mut mock_contract_state = CONTRACT_STATE(); mock_contract_state.erc721._set_base_uri(BASE_URI()); - let token_uri=state.token_uri(TOKEN_ID); - let expected=format!("{}{}",BASE_URI(),TOKEN_ID); - assert_eq!(token_uri,expected); + let token_uri = state.token_uri(TOKEN_ID); + let expected = format!("{}{}", BASE_URI(), TOKEN_ID); + assert_eq!(token_uri, expected); } // // Helpers // -pub fn assert_event_metadata_update(contract: ContractAddress,token_id: u256){ - let event=utils::pop_log::(contract).unwrap(); - let expected=ERC721URIstorageComponent::Event::MetadataUpdate( - MetadataUpdate{token_id} - ); - assert!(event==expected); +pub fn assert_event_metadata_update(contract: ContractAddress, token_id: u256) { + let event = utils::pop_log::(contract).unwrap(); + let expected = ERC721URIstorageComponent::Event::MetadataUpdate(MetadataUpdate { token_id }); + assert!(event == expected); //check indexed keys - let mut indexed_keys=array![]; + let mut indexed_keys = array![]; indexed_keys.append_serde(selector!("MetadataUpdate")); indexed_keys.append_serde(token_id); - utils::assert_indexed_keys(event,indexed_keys.span()) + utils::assert_indexed_keys(event, indexed_keys.span()) } -pub fn assert_only_event_metadata_update( - contract: ContractAddress, token_id : u256 -) { +pub fn assert_only_event_metadata_update(contract: ContractAddress, token_id: u256) { assert_event_metadata_update(contract, token_id); utils::assert_no_events_left(contract); -} \ No newline at end of file +} diff --git a/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo b/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo index 5f5ea39ea..004cd7114 100644 --- a/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo +++ b/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo @@ -1,17 +1,20 @@ #[starknet::contract] pub(crate) mod ERC721URIstorageMock { use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc721::{ERC721Component,ERC721HooksEmptyImpl}; use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent; + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; use starknet::ContractAddress; - component!(path: ERC721URIstorageComponent, storage: erc721_uri_storage, event: ERC721URIstorageEvent); + component!( + path: ERC721URIstorageComponent, storage: erc721_uri_storage, event: ERC721URIstorageEvent + ); component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); //ERC721URIstorage #[abi(embed_v0)] - impl ERC721URIstorageImpl=ERC721URIstorageComponent::ERC721URIstorageImpl; + impl ERC721URIstorageImpl = + ERC721URIstorageComponent::ERC721URIstorageImpl; // ERC721 #[abi(embed_v0)] @@ -21,7 +24,7 @@ pub(crate) mod ERC721URIstorageMock { // SRC5 #[abi(embed_v0)] impl SRC5Impl = SRC5Component::SRC5Impl; - + #[storage] struct Storage { From 2739e773c827b3bf9603f3d5a2d9664c48496e14 Mon Sep 17 00:00:00 2001 From: gerceboss Date: Tue, 2 Jul 2024 02:37:20 +0530 Subject: [PATCH 10/22] updated test implementation --- .../erc721/test_erc721_uri_storage.cairo | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo index 36a09efdd..601c581b0 100644 --- a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo +++ b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo @@ -43,7 +43,7 @@ fn COMPONENT_STATE() -> ComponentState { fn setup() -> ComponentState { let mut state = COMPONENT_STATE(); let mut mock_state = CONTRACT_STATE(); - mock_state.erc721.initializer(NAME(), SYMBOL(), BASE_URI()); + mock_state.erc721.initializer(NAME(), SYMBOL(), ""); mock_state.erc721.mint(OWNER(), TOKEN_ID); utils::drop_event(ZERO()); state @@ -53,8 +53,8 @@ fn setup() -> ComponentState { fn test_token_uri() { let state = setup(); let uri = state.token_uri(TOKEN_ID); - let expected = ""; - assert_eq!(uri, expected); + let expected = 0; + assert_eq!(uri.len(), expected); } #[test] @@ -68,10 +68,10 @@ fn test_token_uri_non_minted() { fn test_set_token_uri() { let mut state = setup(); - state.set_token_uri(TOKEN_ID, SAMPLE_URI()); //internal function? + state.set_token_uri(TOKEN_ID, SAMPLE_URI()); //internal function assert_only_event_metadata_update(ZERO(), TOKEN_ID); //checking event is emitted or not - let expected = format!("{}", SAMPLE_URI()); + let expected = SAMPLE_URI(); let uri = state.token_uri(TOKEN_ID); assert_eq!(uri, expected); @@ -88,7 +88,7 @@ fn test_set_token_uri_nonexistent() { //check accessible after minting mock_contract_state.erc721.mint(RECIPIENT(), TOKEN_ID_2); - let expected = format!("{}", SAMPLE_URI()); + let expected = SAMPLE_URI(); let uri = state.token_uri(TOKEN_ID); assert_eq!(uri, expected); @@ -96,7 +96,7 @@ fn test_set_token_uri_nonexistent() { #[test] fn test_set_base_uri() { - let mut _state = setup(); //its the component state + let mut _state = setup(); let mut mock_contract_state = CONTRACT_STATE(); mock_contract_state.erc721._set_base_uri(BASE_URI()); @@ -108,7 +108,7 @@ fn test_set_base_uri() { #[test] fn test_base_uri_is_prefix() { - let mut state = setup(); //its the component state + let mut state = setup(); let mut mock_contract_state = CONTRACT_STATE(); mock_contract_state.erc721._set_base_uri(BASE_URI()); @@ -121,7 +121,7 @@ fn test_base_uri_is_prefix() { #[test] fn test_base_uri_2_is_set_as_prefix() { - let mut state = setup(); //its the component state + let mut state = setup(); let mut mock_contract_state = CONTRACT_STATE(); mock_contract_state.erc721._set_base_uri(BASE_URI_2()); @@ -135,7 +135,7 @@ fn test_base_uri_2_is_set_as_prefix() { #[test] fn test_base_uri_and_token_id() { - let mut state = setup(); //its the component state + let mut state = setup(); let mut mock_contract_state = CONTRACT_STATE(); mock_contract_state.erc721._set_base_uri(BASE_URI()); From 0a47702d78876e2b3cf266a6b5aa8c230f22bc59 Mon Sep 17 00:00:00 2001 From: gerceboss Date: Tue, 2 Jul 2024 02:47:39 +0530 Subject: [PATCH 11/22] updated test_set_token_uri_nonexistent --- packages/token/src/tests/erc721/test_erc721_uri_storage.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo index 601c581b0..2ffbfbbd7 100644 --- a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo +++ b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo @@ -89,7 +89,7 @@ fn test_set_token_uri_nonexistent() { mock_contract_state.erc721.mint(RECIPIENT(), TOKEN_ID_2); let expected = SAMPLE_URI(); - let uri = state.token_uri(TOKEN_ID); + let uri = state.token_uri(TOKEN_ID_2); assert_eq!(uri, expected); } From 87e8870830e00ab9549d14e38405f4194525cff4 Mon Sep 17 00:00:00 2001 From: gerceboss Date: Wed, 3 Jul 2024 00:33:55 +0530 Subject: [PATCH 12/22] added suggestions-Changed variable names --- docs/modules/ROOT/pages/api/erc721.adoc | 62 +++++++++---------- packages/token/src/erc721/extensions.cairo | 2 +- .../extensions/erc721_uri_storage.cairo | 31 +++++----- packages/token/src/erc721/interface.cairo | 4 +- .../erc721/test_erc721_uri_storage.cairo | 24 +++---- .../mocks/erc721_uri_storage_mocks.cairo | 16 ++--- 6 files changed, 68 insertions(+), 71 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index d0a0258f7..28aa92931 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -693,32 +693,32 @@ See <>. == Extensions [.contract] -[[ERC721URIstorageComponent]] -=== `++ERC721URIstorageComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc721/extensions/erc721_uri_storage.cairo[{github-icon},role=heading-link] +[[ERC721URIStorageComponent]] +=== `++ERC721URIStorageComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc721/extensions/erc721_uri_storage.cairo[{github-icon},role=heading-link] ```cairo -use openzeppelin::token::extensions::ERC721URIstorageComponent; +use openzeppelin::token::extensions::ERC721URIStorageComponent; ``` -:MetadataUpdate: xref:ERC721URIstorageComponent-MetadataUpdate[MetadataUpdate] +:MetadataUpdated: xref:ERC721URIStorageComponent-MetadataUpdated[MetadataUpdated] Extension of ERC721 to support dynamic NFTs. It is an implementation of <> but with a different `token_uri` behavior NOTE: Implementing xref:#ERC721Component[ERC721Component] is a requirement for this component to be implemented. This extension keeps a track of URIs of each `token_id` of a particular ERC721 token. The `token_uri` of any `token_id` can be set by calling the -internal function, xref:#ERC721URIstorageComponent-set_token_uri[set_token_uri]. Updated `token_uri` can be queried through the external function -xref:#ERC721URIstorageComponent-token_uri[token_uri] or the internal function xref:#ERC721URIstorageComponent-_token_uri[_token_uri]. +internal function, xref:#ERC721URIStorageComponent-set_token_uri[set_token_uri]. Updated `token_uri` can be queried through the external function +xref:#ERC721URIStorageComponent-token_uri[token_uri] or the internal function xref:#ERC721URIStorageComponent-_token_uri[_token_uri]. -[.contract-index#ERC721URIstorageComponent-Embeddable-Impls] +[.contract-index#ERC721URIStorageComponent-Embeddable-Impls] .Embeddable Implementations -- -[.sub-index#ERC721URIstorageComponent-Embeddable-Impls-ERC721URIstorageImpl] -.ERC721URIstorageImpl -* xref:#ERC721URIstorageComponent-name[`++name(self)++`] -* xref:#ERC721URIstorageComponent-symbol[`++symbol(self)++`] -* xref:#ERC721URIstorageComponent-token_uri[`++token_uri(self,token_id)++`] +[.sub-index#ERC721URIStorageComponent-Embeddable-Impls-ERC721URIStorageImpl] +.ERC721URIStorageImpl +* xref:#ERC721URIStorageComponent-name[`++name(self)++`] +* xref:#ERC721URIStorageComponent-symbol[`++symbol(self)++`] +* xref:#ERC721URIStorageComponent-token_uri[`++token_uri(self,token_id)++`] -- @@ -726,57 +726,57 @@ xref:#ERC721URIstorageComponent-token_uri[token_uri] or the internal function xr .Internal implementations -- .InternalImpl -* xref:#ERC721URIstorageComponent-_name[`++_name(self)++`] -* xref:#ERC721URIstorageComponent-_symbol[`++_symbol(self)++`] -* xref:#ERC721URIstorageComponent-_token_uri[`++_token_uri(self,token_id)++`] -* xref:#ERC721URIstorageComponent-set_token_uri[`++set_token_uri(self,token_id,token_uri)++`] +* xref:#ERC721URIStorageComponent-_name[`++_name(self)++`] +* xref:#ERC721URIStorageComponent-_symbol[`++_symbol(self)++`] +* xref:#ERC721URIStorageComponent-_token_uri[`++_token_uri(self,token_id)++`] +* xref:#ERC721URIStorageComponent-set_token_uri[`++set_token_uri(self,token_id,token_uri)++`] -- [.contract-index] .Events -- -* xref:#ERC721URIstorageComponent-MetadataUpdate[`++MetadataUpdate(token_id)++`] +* xref:#ERC721URIStorageComponent-MetadataUpdated[`++MetadataUpdated(token_id)++`] -- -[#ERC721URIstorageComponent-Embeddable-functions] +[#ERC721URIStorageComponent-Embeddable-functions] ==== Embeddable functions [.contract-item] -[[ERC721URIstorageComponent-name]] +[[ERC721URIStorageComponent-name]] ==== `[.contract-item-name]#++name++#++(self: @ContractState) → ByteArray++` [.item-kind]#external# See <>. [.contract-item] -[[ERC721URIstorageComponent-symbol]] +[[ERC721URIStorageComponent-symbol]] ==== `[.contract-item-name]#++symbol++#++(self: @ContractState) → ByteArray++` [.item-kind]#external# See <>. [.contract-item] -[[ERC721URIstorageComponent-token_uri]] +[[ERC721URIStorageComponent-token_uri]] ==== `[.contract-item-name]#++token_uri++#++(self: @ContractState, token_id : u256) → ByteArray++` [.item-kind]#external# -See <> +See <> -[#ERC721URIstorageComponent-Internal-functions] +[#ERC721URIStorageComponent-Internal-functions] ==== Internal functions [.contract-item] -[[ERC721URIstorageComponent-name]] +[[ERC721URIStorageComponent-name]] ==== `[.contract-item-name]#++name++#++(self: @ContractState) → ByteArray++` [.item-kind]#internal# See <>. [.contract-item] -[[ERC721URIstorageComponent-symbol]] +[[ERC721URIStorageComponent-symbol]] ==== `[.contract-item-name]#++symbol++#++(self: @ContractState) → ByteArray++` [.item-kind]#internal# See <>. [.contract-item] -[[ERC721URIstorageComponent-_token_uri]] +[[ERC721URIStorageComponent-_token_uri]] ==== `[.contract-item-name]#++_token_uri++#++(self: @ContractState, token_id: u256) → ByteArray++` [.item-kind]#internal# Returns the Uniform Resource Identifier (URI) for the `token_id` token. @@ -789,19 +789,19 @@ If a base URI is not set and the token URI is set , the resulting URI for each t If the base URI and token URI is not set for `token_id`, the return value will be an empty `ByteArray`. [.contract-item] -[[ERC721URIstorageComponent-se_token_uri]] +[[ERC721URIStorageComponent-se_token_uri]] ==== `[.contract-item-name]#++set_token_uri++#++(ref self: ContractState,token_id: u256,token_uri: ByteArray)++` [.item-kind]#internal# Sets the `token_uri` of `token_id`. -Emits a {MetadataUpdate} event. +Emits a {MetadataUpdated} event. -[#ERC721URIstorageComponent-Events] +[#ERC721URIStorageComponent-Events] ==== Events [.contract-item] -[[ERC721URIstorageComponent-MetadataUpdate]] -==== `[.contract-item-name]#++MetadataUpdate++#++(token_id: u256)++` [.item-kind]#event# +[[ERC721URIStorageComponent-MetadataUpdated]] +==== `[.contract-item-name]#++MetadataUpdated++#++(token_id: u256)++` [.item-kind]#event# Emitted when `token_uri` of `token_id` is set. diff --git a/packages/token/src/erc721/extensions.cairo b/packages/token/src/erc721/extensions.cairo index 5c08a2fe3..7868b6f1e 100644 --- a/packages/token/src/erc721/extensions.cairo +++ b/packages/token/src/erc721/extensions.cairo @@ -1,3 +1,3 @@ pub mod erc721_uri_storage; -pub use erc721_uri_storage::ERC721URIstorageComponent; +pub use erc721_uri_storage::ERC721URIStorageComponent; diff --git a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo index 220cc5811..853bed4b2 100644 --- a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo +++ b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo @@ -2,7 +2,7 @@ // OpenZeppelin Contracts for Cairo v0.14.0 (token/erc721/extensions/erc721_uri_storage.cairo) #[starknet::component] -pub mod ERC721URIstorageComponent { +pub mod ERC721URIStorageComponent { use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; use openzeppelin::introspection::src5::SRC5Component; @@ -13,24 +13,24 @@ pub mod ERC721URIstorageComponent { #[storage] struct Storage { - token_uris: LegacyMap, + ERC721URIStorage_token_uris: LegacyMap, } #[event] #[derive(Drop, PartialEq, starknet::Event)] pub enum Event { - MetadataUpdate: MetadataUpdate, + MetadataUpdated: MetadataUpdated, } /// Emitted when `token_uri` is changed for `token_id` #[derive(Drop, PartialEq, starknet::Event)] - pub struct MetadataUpdate { + pub struct MetadataUpdated { #[key] pub token_id: u256, } - #[embeddable_as(ERC721URIstorageImpl)] - impl ERC721URIstorage< + #[embeddable_as(ERC721URIStorageImpl)] + impl ERC721URIStorage< TContractState, +HasComponent, +SRC5Component::HasComponent, @@ -44,6 +44,7 @@ pub mod ERC721URIstorageComponent { fn symbol(self: @ComponentState) -> ByteArray { self._symbol() } + /// Returns the Uniform Resource Identifier (URI) for the `token_id` token. /// If the URI is not set, the return value will be an empty ByteArray. /// @@ -70,13 +71,13 @@ pub mod ERC721URIstorageComponent { fn _name(self: @ComponentState) -> ByteArray { let erc721_component = get_dep_component!(self, ERC721); let name = erc721_component.ERC721_name.read(); - return name; + name } fn _symbol(self: @ComponentState) -> ByteArray { let erc721_component = get_dep_component!(self, ERC721); let symbol = erc721_component.ERC721_symbol.read(); - return symbol; + symbol } /// Returns the `token_uri` for the `token_id` @@ -85,7 +86,7 @@ pub mod ERC721URIstorageComponent { let mut erc721_component = get_dep_component!(self, ERC721); ERC721Impl::_require_owned(erc721_component, token_id); let base_uri: ByteArray = ERC721Impl::_base_uri(erc721_component); - let token_uri: ByteArray = self.token_uris.read(token_id); + let token_uri: ByteArray = self.ERC721URIStorage_token_uris.read(token_id); if base_uri.len() == 0 { return token_uri; } @@ -94,21 +95,17 @@ pub mod ERC721URIstorageComponent { } //token_uri implementation from the ERC721Metadata - if base_uri.len() == 0 { - return ""; - } else { - return format!("{}{}", base_uri, token_id); - } + return format!("{}{}", base_uri, token_id); } /// Sets or updates the `token_uri` for the respective `token_uri` /// - /// Emits `MetadataUpdate` event + /// Emits `MetadataUpdated` event fn set_token_uri( ref self: ComponentState, token_id: u256, token_uri: ByteArray ) { - self.token_uris.write(token_id, token_uri); - self.emit(MetadataUpdate { token_id: token_id }); + self.ERC721URIStorage_token_uris.write(token_id, token_uri); + self.emit(MetadataUpdated { token_id: token_id }); } } } diff --git a/packages/token/src/erc721/interface.cairo b/packages/token/src/erc721/interface.cairo index 8d5b69739..bab51af1a 100644 --- a/packages/token/src/erc721/interface.cairo +++ b/packages/token/src/erc721/interface.cairo @@ -111,11 +111,11 @@ pub trait ERC721ABI { } // -// ERC721URIstorage ABI +// ERC721URIStorage ABI // #[starknet::interface] -pub trait ERC721URIstorageABI { +pub trait ERC721URIStorageABI { // IERC721 fn balance_of(self: @TState, account: ContractAddress) -> u256; fn owner_of(self: @TState, token_id: u256) -> ContractAddress; diff --git a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo index 2ffbfbbd7..6f272a149 100644 --- a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo +++ b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo @@ -1,7 +1,7 @@ use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; use openzeppelin::introspection::src5; use openzeppelin::introspection; -use openzeppelin::tests::mocks::erc721_uri_storage_mocks::ERC721URIstorageMock; +use openzeppelin::tests::mocks::erc721_uri_storage_mocks::ERC721URIStorageMock; use openzeppelin::tests::utils::constants::{ DATA, ZERO, OWNER, RECIPIENT, NAME, SYMBOL, TOKEN_ID, TOKEN_ID_2, PUBKEY, BASE_URI, BASE_URI_2, SAMPLE_URI @@ -10,11 +10,11 @@ use openzeppelin::tests::utils; use openzeppelin::token::erc721::ERC721Component::ERC721Impl; use openzeppelin::token::erc721::ERC721Component::InternalImpl as ERC721InternalImpl; use openzeppelin::token::erc721::ERC721Component; -use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent::{ - ERC721URIstorageImpl, InternalImpl +use openzeppelin::token::erc721::extensions::ERC721URIStorageComponent::{ + ERC721URIStorageImpl, InternalImpl }; -use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent; -use openzeppelin::token::erc721::extensions::erc721_uri_storage::ERC721URIstorageComponent::MetadataUpdate; +use openzeppelin::token::erc721::extensions::ERC721URIStorageComponent; +use openzeppelin::token::erc721::extensions::erc721_uri_storage::ERC721URIStorageComponent::MetadataUpdated; use openzeppelin::token::erc721::interface::IERC721; use openzeppelin::token::erc721; @@ -30,13 +30,13 @@ use starknet::testing; // type ComponentState = - ERC721URIstorageComponent::ComponentState; + ERC721URIStorageComponent::ComponentState; -fn CONTRACT_STATE() -> ERC721URIstorageMock::ContractState { - ERC721URIstorageMock::contract_state_for_testing() +fn CONTRACT_STATE() -> ERC721URIStorageMock::ContractState { + ERC721URIStorageMock::contract_state_for_testing() } fn COMPONENT_STATE() -> ComponentState { - ERC721URIstorageComponent::component_state_for_testing() + ERC721URIStorageComponent::component_state_for_testing() } //constructor is inside only @@ -149,13 +149,13 @@ fn test_base_uri_and_token_id() { // Helpers // pub fn assert_event_metadata_update(contract: ContractAddress, token_id: u256) { - let event = utils::pop_log::(contract).unwrap(); - let expected = ERC721URIstorageComponent::Event::MetadataUpdate(MetadataUpdate { token_id }); + let event = utils::pop_log::(contract).unwrap(); + let expected = ERC721URIStorageComponent::Event::MetadataUpdated(MetadataUpdated { token_id }); assert!(event == expected); //check indexed keys let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("MetadataUpdate")); + indexed_keys.append_serde(selector!("MetadataUpdated")); indexed_keys.append_serde(token_id); utils::assert_indexed_keys(event, indexed_keys.span()) } diff --git a/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo b/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo index 004cd7114..24d26d0fa 100644 --- a/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo +++ b/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo @@ -1,20 +1,20 @@ #[starknet::contract] -pub(crate) mod ERC721URIstorageMock { +pub(crate) mod ERC721URIStorageMock { use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc721::extensions::ERC721URIstorageComponent; + use openzeppelin::token::erc721::extensions::ERC721URIStorageComponent; use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; use starknet::ContractAddress; component!( - path: ERC721URIstorageComponent, storage: erc721_uri_storage, event: ERC721URIstorageEvent + path: ERC721URIStorageComponent, storage: erc721_uri_storage, event: ERC721URIStorageEvent ); component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); - //ERC721URIstorage + //ERC721URIStorage #[abi(embed_v0)] - impl ERC721URIstorageImpl = - ERC721URIstorageComponent::ERC721URIstorageImpl; + impl ERC721URIStorageImpl = + ERC721URIStorageComponent::ERC721URIStorageImpl; // ERC721 #[abi(embed_v0)] @@ -29,7 +29,7 @@ pub(crate) mod ERC721URIstorageMock { #[storage] struct Storage { #[substorage(v0)] - erc721_uri_storage: ERC721URIstorageComponent::Storage, + erc721_uri_storage: ERC721URIStorageComponent::Storage, #[substorage(v0)] erc721: ERC721Component::Storage, #[substorage(v0)] @@ -40,7 +40,7 @@ pub(crate) mod ERC721URIstorageMock { #[derive(Drop, starknet::Event)] enum Event { #[flat] - ERC721URIstorageEvent: ERC721URIstorageComponent::Event, + ERC721URIStorageEvent: ERC721URIStorageComponent::Event, #[flat] ERC721Event: ERC721Component::Event, #[flat] From 9b86c3b38d4dd578746fd3cb761f043cff96b1ba Mon Sep 17 00:00:00 2001 From: gerceboss Date: Wed, 10 Jul 2024 16:36:58 +0530 Subject: [PATCH 13/22] applied changes removed unnecessary internalImpl --- docs/modules/ROOT/pages/api/erc721.adoc | 54 +++++--------- .../extensions/erc721_uri_storage.cairo | 74 ++++++++----------- packages/token/src/erc721/interface.cairo | 31 -------- .../erc721/test_erc721_uri_storage.cairo | 62 +++++----------- .../mocks/erc721_uri_storage_mocks.cairo | 3 +- src/tests/utils/constants.cairo | 1 + 6 files changed, 70 insertions(+), 155 deletions(-) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 28aa92931..e816ceeca 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -689,7 +689,6 @@ See <>. See <>. - == Extensions [.contract] @@ -702,14 +701,14 @@ use openzeppelin::token::extensions::ERC721URIStorageComponent; :MetadataUpdated: xref:ERC721URIStorageComponent-MetadataUpdated[MetadataUpdated] -Extension of ERC721 to support dynamic NFTs. It is an implementation of <> but with a different `token_uri` behavior +Extension of ERC721 to support dynamic NFTs. +It is an implementation of <> but with a different `token_uri` behavior. NOTE: Implementing xref:#ERC721Component[ERC721Component] is a requirement for this component to be implemented. -This extension keeps a track of URIs of each `token_id` of a particular ERC721 token. The `token_uri` of any `token_id` can be set by calling the -internal function, xref:#ERC721URIStorageComponent-set_token_uri[set_token_uri]. Updated `token_uri` can be queried through the external function -xref:#ERC721URIStorageComponent-token_uri[token_uri] or the internal function xref:#ERC721URIStorageComponent-_token_uri[_token_uri]. - +This extension keeps a track of URIs of each token id of a particular ERC721 token. The URI of any `token_id` can be set by calling the +internal function, xref:#ERC721URIStorageComponent-set_token_uri[set_token_uri]. Updated `token_uri` can be queried through the external function +xref:#ERC721URIStorageComponent-token_uri[token_uri]. [.contract-index#ERC721URIStorageComponent-Embeddable-Impls] .Embeddable Implementations @@ -718,7 +717,7 @@ xref:#ERC721URIStorageComponent-token_uri[token_uri] or the internal function xr .ERC721URIStorageImpl * xref:#ERC721URIStorageComponent-name[`++name(self)++`] * xref:#ERC721URIStorageComponent-symbol[`++symbol(self)++`] -* xref:#ERC721URIStorageComponent-token_uri[`++token_uri(self,token_id)++`] +* xref:#ERC721URIStorageComponent-token_uri[`++token_uri(self, token_id)++`] -- @@ -726,10 +725,7 @@ xref:#ERC721URIStorageComponent-token_uri[token_uri] or the internal function xr .Internal implementations -- .InternalImpl -* xref:#ERC721URIStorageComponent-_name[`++_name(self)++`] -* xref:#ERC721URIStorageComponent-_symbol[`++_symbol(self)++`] -* xref:#ERC721URIStorageComponent-_token_uri[`++_token_uri(self,token_id)++`] -* xref:#ERC721URIStorageComponent-set_token_uri[`++set_token_uri(self,token_id,token_uri)++`] +* xref:#ERC721URIStorageComponent-set_token_uri[`++set_token_uri(self, token_id, token_uri)++`] -- @@ -758,39 +754,23 @@ See <>. [[ERC721URIStorageComponent-token_uri]] ==== `[.contract-item-name]#++token_uri++#++(self: @ContractState, token_id : u256) → ByteArray++` [.item-kind]#external# -See <> - -[#ERC721URIStorageComponent-Internal-functions] -==== Internal functions - -[.contract-item] -[[ERC721URIStorageComponent-name]] -==== `[.contract-item-name]#++name++#++(self: @ContractState) → ByteArray++` [.item-kind]#internal# - -See <>. - -[.contract-item] -[[ERC721URIStorageComponent-symbol]] -==== `[.contract-item-name]#++symbol++#++(self: @ContractState) → ByteArray++` [.item-kind]#internal# - -See <>. +Returns the Uniform Resource Identifier (URI) for the `token_id` token. -[.contract-item] -[[ERC721URIStorageComponent-_token_uri]] -==== `[.contract-item-name]#++_token_uri++#++(self: @ContractState, token_id: u256) → ByteArray++` [.item-kind]#internal# -Returns the Uniform Resource Identifier (URI) for the `token_id` token. +If a base URI is set and the token URI is set , the resulting URI for each token will be the concatenation of the base URI and the token URI. +If a base URI is set and the token URI is not set , the resulting URI for each token will be the concatenation of the base URI and the `token_id`. -If a base URI is set and the token URI is set , the resulting URI for each token will be the concatenation of the base URI and the token URI. -If a base URI is set and the token URI is not set , the resulting URI for each token will be the concatenation of the base URI and the token ID. If a base URI is not set and the token URI is set , the resulting URI for each token will be the token URI. -If the base URI and token URI is not set for `token_id`, the return value will be an empty `ByteArray`. +If the base URI and token URI are not set for `token_id`, the return value will be an empty `ByteArray`. + +[#ERC721URIStorageComponent-Internal-functions] +==== Internal functions [.contract-item] -[[ERC721URIStorageComponent-se_token_uri]] -==== `[.contract-item-name]#++set_token_uri++#++(ref self: ContractState,token_id: u256,token_uri: ByteArray)++` [.item-kind]#internal# +[[ERC721URIStorageComponent-set_token_uri]] +==== `[.contract-item-name]#++set_token_uri++#++(ref self: ContractState, token_id: u256, token_uri: ByteArray)++` [.item-kind]#internal# Sets the `token_uri` of `token_id`. @@ -803,7 +783,7 @@ Emits a {MetadataUpdated} event. [[ERC721URIStorageComponent-MetadataUpdated]] ==== `[.contract-item-name]#++MetadataUpdated++#++(token_id: u256)++` [.item-kind]#event# -Emitted when `token_uri` of `token_id` is set. +Emitted when the URI of `token_id` is set. == Receiver diff --git a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo index 853bed4b2..ecee78a72 100644 --- a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo +++ b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo @@ -1,13 +1,12 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.14.0 (token/erc721/extensions/erc721_uri_storage.cairo) +// todo: add module description #[starknet::component] pub mod ERC721URIStorageComponent { - use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; - use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::token::erc721::ERC721Component::InternalImpl as ERC721Impl; - use openzeppelin::token::erc721::interface::{IERC721, IERC721Metadata}; + use openzeppelin::token::erc721::interface::IERC721Metadata; use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; use starknet::ContractAddress; @@ -22,10 +21,9 @@ pub mod ERC721URIStorageComponent { MetadataUpdated: MetadataUpdated, } - /// Emitted when `token_uri` is changed for `token_id` + /// Emitted when `token_uri` is changed for `token_id`. #[derive(Drop, PartialEq, starknet::Event)] pub struct MetadataUpdated { - #[key] pub token_id: u256, } @@ -34,15 +32,19 @@ pub mod ERC721URIStorageComponent { TContractState, +HasComponent, +SRC5Component::HasComponent, - +ERC721Component::HasComponent, + impl ERC721: ERC721Component::HasComponent, +Drop > of IERC721Metadata> { + /// Returns the NFT name. fn name(self: @ComponentState) -> ByteArray { - self._name() + let erc721_component = get_dep_component!(self, ERC721); + erc721_component.ERC721_name.read() } + /// Returns the NFT symbol. fn symbol(self: @ComponentState) -> ByteArray { - self._symbol() + let erc721_component = get_dep_component!(self, ERC721); + erc721_component.ERC721_symbol.read() } /// Returns the Uniform Resource Identifier (URI) for the `token_id` token. @@ -52,55 +54,41 @@ pub mod ERC721URIStorageComponent { /// /// - `token_id` exists. fn token_uri(self: @ComponentState, token_id: u256) -> ByteArray { - self._token_uri(token_id) - } - } - - // - // Internal - // - - #[generate_trait] - pub impl InternalImpl< - TContractState, - +HasComponent, - impl SRC5: SRC5Component::HasComponent, - impl ERC721: ERC721Component::HasComponent, - +Drop - > of InternalTrait { - fn _name(self: @ComponentState) -> ByteArray { - let erc721_component = get_dep_component!(self, ERC721); - let name = erc721_component.ERC721_name.read(); - name - } - - fn _symbol(self: @ComponentState) -> ByteArray { - let erc721_component = get_dep_component!(self, ERC721); - let symbol = erc721_component.ERC721_symbol.read(); - symbol - } - - /// Returns the `token_uri` for the `token_id` - /// if needed, returns the concatenated string - fn _token_uri(self: @ComponentState, token_id: u256) -> ByteArray { let mut erc721_component = get_dep_component!(self, ERC721); - ERC721Impl::_require_owned(erc721_component, token_id); + erc721_component._require_owned(token_id); let base_uri: ByteArray = ERC721Impl::_base_uri(erc721_component); let token_uri: ByteArray = self.ERC721URIStorage_token_uris.read(token_id); + + // If there is no base_uri, return the token_uri. if base_uri.len() == 0 { return token_uri; } + + // If both are set, concatenate the base_uri and token_uri. if token_uri.len() > 0 { return format!("{}{}", base_uri, token_uri); } - //token_uri implementation from the ERC721Metadata + // Implementation from ERC721Metadata::token_uri return format!("{}{}", base_uri, token_id); } + } - /// Sets or updates the `token_uri` for the respective `token_uri` + // + // Internal + // + + #[generate_trait] + pub impl InternalImpl< + TContractState, + +HasComponent, + +SRC5Component::HasComponent, + +ERC721Component::HasComponent, + +Drop + > of InternalTrait { + /// Sets or updates the `token_uri` for the respective `token_id` /// - /// Emits `MetadataUpdated` event + /// Emits `MetadataUpdated` event. fn set_token_uri( ref self: ComponentState, token_id: u256, token_uri: ByteArray ) { diff --git a/packages/token/src/erc721/interface.cairo b/packages/token/src/erc721/interface.cairo index bab51af1a..26f4cecac 100644 --- a/packages/token/src/erc721/interface.cairo +++ b/packages/token/src/erc721/interface.cairo @@ -58,7 +58,6 @@ pub trait IERC721MetadataCamelOnly { fn tokenURI(self: @TState, tokenId: u256) -> ByteArray; } - // // ERC721 ABI // @@ -110,36 +109,6 @@ pub trait ERC721ABI { fn tokenURI(self: @TState, tokenId: u256) -> ByteArray; } -// -// ERC721URIStorage ABI -// - -#[starknet::interface] -pub trait ERC721URIStorageABI { - // IERC721 - fn balance_of(self: @TState, account: ContractAddress) -> u256; - fn owner_of(self: @TState, token_id: u256) -> ContractAddress; - fn safe_transfer_from( - ref self: TState, - from: ContractAddress, - to: ContractAddress, - token_id: u256, - data: Span - ); - fn transfer_from(ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256); - fn approve(ref self: TState, to: ContractAddress, token_id: u256); - fn set_approval_for_all(ref self: TState, operator: ContractAddress, approved: bool); - fn get_approved(self: @TState, token_id: u256) -> ContractAddress; - fn is_approved_for_all( - self: @TState, owner: ContractAddress, operator: ContractAddress - ) -> bool; - - // IERC721Metadata - fn name(self: @TState) -> ByteArray; - fn symbol(self: @TState) -> ByteArray; - fn token_uri(self: @TState, token_id: u256) -> ByteArray; -} - // // ERC721Receiver // diff --git a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo index 6f272a149..0b7dfbf15 100644 --- a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo +++ b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo @@ -1,28 +1,16 @@ -use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; -use openzeppelin::introspection::src5; -use openzeppelin::introspection; use openzeppelin::tests::mocks::erc721_uri_storage_mocks::ERC721URIStorageMock; use openzeppelin::tests::utils::constants::{ - DATA, ZERO, OWNER, RECIPIENT, NAME, SYMBOL, TOKEN_ID, TOKEN_ID_2, PUBKEY, BASE_URI, BASE_URI_2, - SAMPLE_URI + ZERO, OWNER, RECIPIENT, NAME, SYMBOL, TOKEN_ID, TOKEN_ID_2, BASE_URI, BASE_URI_2, SAMPLE_URI }; use openzeppelin::tests::utils; -use openzeppelin::token::erc721::ERC721Component::ERC721Impl; use openzeppelin::token::erc721::ERC721Component::InternalImpl as ERC721InternalImpl; -use openzeppelin::token::erc721::ERC721Component; use openzeppelin::token::erc721::extensions::ERC721URIStorageComponent::{ ERC721URIStorageImpl, InternalImpl }; use openzeppelin::token::erc721::extensions::ERC721URIStorageComponent; use openzeppelin::token::erc721::extensions::erc721_uri_storage::ERC721URIStorageComponent::MetadataUpdated; - -use openzeppelin::token::erc721::interface::IERC721; -use openzeppelin::token::erc721; use openzeppelin::utils::serde::SerializedAppend; use starknet::ContractAddress; -use starknet::contract_address_const; -use starknet::storage::{StorageMapMemberAccessTrait, StorageMemberAccessTrait}; -use starknet::testing; // @@ -50,11 +38,11 @@ fn setup() -> ComponentState { } #[test] -fn test_token_uri() { +fn test_token_uri_when_not_set() { let state = setup(); let uri = state.token_uri(TOKEN_ID); - let expected = 0; - assert_eq!(uri.len(), expected); + let empty = 0; + assert_eq!(uri.len(), empty); } #[test] @@ -68,8 +56,8 @@ fn test_token_uri_non_minted() { fn test_set_token_uri() { let mut state = setup(); - state.set_token_uri(TOKEN_ID, SAMPLE_URI()); //internal function - assert_only_event_metadata_update(ZERO(), TOKEN_ID); //checking event is emitted or not + state.set_token_uri(TOKEN_ID, SAMPLE_URI()); + assert_only_event_metadata_update(ZERO(), TOKEN_ID); let expected = SAMPLE_URI(); let uri = state.token_uri(TOKEN_ID); @@ -82,7 +70,7 @@ fn test_set_token_uri_nonexistent() { let mut state = setup(); state.set_token_uri(TOKEN_ID_2, SAMPLE_URI()); - assert_only_event_metadata_update(ZERO(), TOKEN_ID_2); //checking event is emitted or not + assert_only_event_metadata_update(ZERO(), TOKEN_ID_2); let mut mock_contract_state = CONTRACT_STATE(); //check accessible after minting @@ -95,19 +83,7 @@ fn test_set_token_uri_nonexistent() { } #[test] -fn test_set_base_uri() { - let mut _state = setup(); - - let mut mock_contract_state = CONTRACT_STATE(); - mock_contract_state.erc721._set_base_uri(BASE_URI()); - - let base_uri = mock_contract_state.erc721._base_uri(); //internal of ERC721 - - assert_eq!(base_uri, BASE_URI()); -} - -#[test] -fn test_base_uri_is_prefix() { +fn test_token_uri_with_base_uri() { let mut state = setup(); let mut mock_contract_state = CONTRACT_STATE(); @@ -124,17 +100,18 @@ fn test_base_uri_2_is_set_as_prefix() { let mut state = setup(); let mut mock_contract_state = CONTRACT_STATE(); - mock_contract_state.erc721._set_base_uri(BASE_URI_2()); - + mock_contract_state.erc721._set_base_uri(BASE_URI()); state.set_token_uri(TOKEN_ID, SAMPLE_URI()); + mock_contract_state.erc721._set_base_uri(BASE_URI_2()); + let token_uri = state.token_uri(TOKEN_ID); let expected = format!("{}{}", BASE_URI_2(), SAMPLE_URI()); assert_eq!(token_uri, expected); } #[test] -fn test_base_uri_and_token_id() { +fn test_token_uri_with_base_uri_and_token_id() { let mut state = setup(); let mut mock_contract_state = CONTRACT_STATE(); @@ -145,22 +122,23 @@ fn test_base_uri_and_token_id() { assert_eq!(token_uri, expected); } +// todo: add this test +// todo: CHANGELOG.mod +// todo: test and provide transaction hash +//#[test] +//fn test_token_uri_persists_when_burned_and_minted + // // Helpers // + pub fn assert_event_metadata_update(contract: ContractAddress, token_id: u256) { let event = utils::pop_log::(contract).unwrap(); let expected = ERC721URIStorageComponent::Event::MetadataUpdated(MetadataUpdated { token_id }); assert!(event == expected); - - //check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("MetadataUpdated")); - indexed_keys.append_serde(token_id); - utils::assert_indexed_keys(event, indexed_keys.span()) } -pub fn assert_only_event_metadata_update(contract: ContractAddress, token_id: u256) { +fn assert_only_event_metadata_update(contract: ContractAddress, token_id: u256) { assert_event_metadata_update(contract, token_id); utils::assert_no_events_left(contract); } diff --git a/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo b/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo index 24d26d0fa..c8f054643 100644 --- a/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo +++ b/packages/token/src/tests/mocks/erc721_uri_storage_mocks.cairo @@ -11,7 +11,7 @@ pub(crate) mod ERC721URIStorageMock { component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); - //ERC721URIStorage + // ERC721URIStorage #[abi(embed_v0)] impl ERC721URIStorageImpl = ERC721URIStorageComponent::ERC721URIStorageImpl; @@ -25,7 +25,6 @@ pub(crate) mod ERC721URIStorageMock { #[abi(embed_v0)] impl SRC5Impl = SRC5Component::SRC5Impl; - #[storage] struct Storage { #[substorage(v0)] diff --git a/src/tests/utils/constants.cairo b/src/tests/utils/constants.cairo index 3b38cc4c3..d91ec7f73 100644 --- a/src/tests/utils/constants.cairo +++ b/src/tests/utils/constants.cairo @@ -44,6 +44,7 @@ pub(crate) fn BASE_URI() -> ByteArray { pub(crate) fn BASE_URI_2() -> ByteArray { "https://api.example.com/v2/" } + pub(crate) fn SAMPLE_URI() -> ByteArray { "mock://mytoken" } From 6728f49d990a34c1010c697e457a928a94098e49 Mon Sep 17 00:00:00 2001 From: gerceboss Date: Wed, 10 Jul 2024 16:39:30 +0530 Subject: [PATCH 14/22] fixed linting --- packages/token/src/erc721/extensions/erc721_uri_storage.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo index ecee78a72..1be53d0a1 100644 --- a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo +++ b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo @@ -64,7 +64,7 @@ pub mod ERC721URIStorageComponent { return token_uri; } - // If both are set, concatenate the base_uri and token_uri. + // If both are set, concatenate the base_uri and token_uri. if token_uri.len() > 0 { return format!("{}{}", base_uri, token_uri); } From 5fcb2dd3bdee06f1442677da8d592617cf263dbb Mon Sep 17 00:00:00 2001 From: gerceboss Date: Wed, 10 Jul 2024 23:41:18 +0530 Subject: [PATCH 15/22] added CHANGELOG resolved suggestions --- CHANGELOG.md | 7 +++++++ .../extensions/erc721_uri_storage.cairo | 9 +++++++-- .../erc721/test_erc721_uri_storage.cairo | 19 +++++++++++++++++-- src/tests/utils/constants.cairo | 1 + 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2b941d27..71836eac4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed `num_checkpoints` and `checkpoints` from `ERC20VotesABI`. +### Added (2024-07-10) + +- ERC721URIStorage (2024-07-10) in `/src/token/erc721/extensions` +- ERC721URIStorage mock in `/src/tests/mocks/` +- ERC721URIStorage tests in `/src/tests/token/erc721/` +- Documentation for ERC721URIStorage extension in `/docs/modules/ROOT/api/erc721` + ## 0.14.0 (2024-06-14) ### Changed (Breaking) diff --git a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo index 1be53d0a1..1f48008cf 100644 --- a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo +++ b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo @@ -1,6 +1,11 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.14.0 (token/erc721/extensions/erc721_uri_storage.cairo) -// todo: add module description + +/// # ERC721URIStorage Component +/// +/// The ERC721URIStorage component enhances ERC721 tokens by keeping track of unique URIs with each token id, +/// and providing a functionality of metadata updates. Each token's URI, set during minting, is immutable, ensuring +/// the integrity of the metadata. It provides a reliable mechanism for linking on-chain tokens to off-chain metadata. #[starknet::component] pub mod ERC721URIStorageComponent { @@ -47,8 +52,8 @@ pub mod ERC721URIStorageComponent { erc721_component.ERC721_symbol.read() } + /// Returns the Uniform Resource Identifier (URI) for the `token_id` token. - /// If the URI is not set, the return value will be an empty ByteArray. /// /// Requirements: /// diff --git a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo index 0b7dfbf15..7ea00cd48 100644 --- a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo +++ b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo @@ -123,10 +123,25 @@ fn test_token_uri_with_base_uri_and_token_id() { } // todo: add this test -// todo: CHANGELOG.mod // todo: test and provide transaction hash //#[test] -//fn test_token_uri_persists_when_burned_and_minted +//fn test_token_uri_persists_when_burned_and_minted() { +// let mut state = setup(); +// +// state.set_token_uri(TOKEN_ID, SAMPLE_URI()); +// +// let mut mock_contract_state = CONTRACT_STATE(); +// mock_contract_state.erc721.burn(TOKEN_ID); +// +// //must panic with error 'ERC721: invalid token ID' +// state.token_uri(TOKEN_ID); +// +// mock_contract_state.erc721.mint(OWNER(), TOKEN_ID); +// +// let token_uri = state.token_uri(TOKEN_ID); +// let expected = SAMPLE_URI(); +// assert_eq!(token_uri, expected); +//} // // Helpers diff --git a/src/tests/utils/constants.cairo b/src/tests/utils/constants.cairo index d91ec7f73..5659e526b 100644 --- a/src/tests/utils/constants.cairo +++ b/src/tests/utils/constants.cairo @@ -45,6 +45,7 @@ pub(crate) fn BASE_URI_2() -> ByteArray { "https://api.example.com/v2/" } + pub(crate) fn SAMPLE_URI() -> ByteArray { "mock://mytoken" } From 2d4390190a2951cef19d53bdc9abcbec363453fa Mon Sep 17 00:00:00 2001 From: gerceboss Date: Thu, 11 Jul 2024 20:15:31 +0530 Subject: [PATCH 16/22] added test_token_uri_persists_when_burnt_and_minted --- .../erc721/test_erc721_uri_storage.cairo | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo index 7ea00cd48..c8cec2bb3 100644 --- a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo +++ b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo @@ -122,26 +122,22 @@ fn test_token_uri_with_base_uri_and_token_id() { assert_eq!(token_uri, expected); } -// todo: add this test // todo: test and provide transaction hash -//#[test] -//fn test_token_uri_persists_when_burned_and_minted() { -// let mut state = setup(); -// -// state.set_token_uri(TOKEN_ID, SAMPLE_URI()); -// -// let mut mock_contract_state = CONTRACT_STATE(); -// mock_contract_state.erc721.burn(TOKEN_ID); -// -// //must panic with error 'ERC721: invalid token ID' -// state.token_uri(TOKEN_ID); -// -// mock_contract_state.erc721.mint(OWNER(), TOKEN_ID); -// -// let token_uri = state.token_uri(TOKEN_ID); -// let expected = SAMPLE_URI(); -// assert_eq!(token_uri, expected); -//} +#[test] +fn test_token_uri_persists_when_burned_and_minted() { + let mut state = setup(); + + state.set_token_uri(TOKEN_ID, SAMPLE_URI()); + + let mut mock_contract_state = CONTRACT_STATE(); + mock_contract_state.erc721.burn(TOKEN_ID); + + mock_contract_state.erc721.mint(OWNER(), TOKEN_ID); + + let token_uri = state.token_uri(TOKEN_ID); + let expected = SAMPLE_URI(); + assert_eq!(token_uri, expected); +} // // Helpers From a0b2a1750ebe94df629f8045188e6879309f54ab Mon Sep 17 00:00:00 2001 From: gerceboss Date: Thu, 11 Jul 2024 23:09:07 +0530 Subject: [PATCH 17/22] tested on test network --- packages/token/src/tests/erc721/test_erc721_uri_storage.cairo | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo index c8cec2bb3..a74df3e5e 100644 --- a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo +++ b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo @@ -122,7 +122,6 @@ fn test_token_uri_with_base_uri_and_token_id() { assert_eq!(token_uri, expected); } -// todo: test and provide transaction hash #[test] fn test_token_uri_persists_when_burned_and_minted() { let mut state = setup(); From a12f5e5f12a0a6c9a94c46cdde8a7cfe68f59bd4 Mon Sep 17 00:00:00 2001 From: gerceboss Date: Mon, 15 Jul 2024 17:01:28 +0530 Subject: [PATCH 18/22] applied suggestions fixed documentation --- CHANGELOG.md | 5 +---- docs/modules/ROOT/pages/api/erc721.adoc | 14 +++++++------- .../src/erc721/extensions/erc721_uri_storage.cairo | 12 +++++------- .../src/tests/erc721/test_erc721_uri_storage.cairo | 5 ++--- src/tests/utils/constants.cairo | 1 - 5 files changed, 15 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71836eac4..3772ec8a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,10 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added (2024-07-10) -- ERC721URIStorage (2024-07-10) in `/src/token/erc721/extensions` -- ERC721URIStorage mock in `/src/tests/mocks/` -- ERC721URIStorage tests in `/src/tests/token/erc721/` -- Documentation for ERC721URIStorage extension in `/docs/modules/ROOT/api/erc721` +- ERC721URIStorage (#1031) ## 0.14.0 (2024-06-14) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index e816ceeca..0ed8bca74 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -701,14 +701,14 @@ use openzeppelin::token::extensions::ERC721URIStorageComponent; :MetadataUpdated: xref:ERC721URIStorageComponent-MetadataUpdated[MetadataUpdated] -Extension of ERC721 to support dynamic NFTs. +Extension of ERC721 to support storage-based URI management. It is an implementation of <> but with a different `token_uri` behavior. NOTE: Implementing xref:#ERC721Component[ERC721Component] is a requirement for this component to be implemented. -This extension keeps a track of URIs of each token id of a particular ERC721 token. The URI of any `token_id` can be set by calling the -internal function, xref:#ERC721URIStorageComponent-set_token_uri[set_token_uri]. Updated `token_uri` can be queried through the external function -xref:#ERC721URIStorageComponent-token_uri[token_uri]. +The ERC721URIStorage component provides a flexible IERC721Metadata implementation that enables storage-based token URI management. +The URI of any `token_id` can be set by calling the internal function, xref:#ERC721URIStorageComponent-set_token_uri[set_token_uri]. +Updated `token_uri` can be queried through the external function xref:#ERC721URIStorageComponent-token_uri[token_uri]. [.contract-index#ERC721URIStorageComponent-Embeddable-Impls] .Embeddable Implementations @@ -757,11 +757,11 @@ See <>. Returns the Uniform Resource Identifier (URI) for the `token_id` token. -If a base URI is set and the token URI is set , the resulting URI for each token will be the concatenation of the base URI and the token URI. +If a base URI is set and the token URI is set, the resulting URI for each token will be the concatenation of the base URI and the token URI. -If a base URI is set and the token URI is not set , the resulting URI for each token will be the concatenation of the base URI and the `token_id`. +If a base URI is set and the token URI is not set, the resulting URI for each token will be the concatenation of the base URI and the `token_id`. -If a base URI is not set and the token URI is set , the resulting URI for each token will be the token URI. +If a base URI is not set and the token URI is set, the resulting URI for each token will be the token URI. If the base URI and token URI are not set for `token_id`, the return value will be an empty `ByteArray`. diff --git a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo index 1f48008cf..1784ddddd 100644 --- a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo +++ b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo @@ -3,10 +3,8 @@ /// # ERC721URIStorage Component /// -/// The ERC721URIStorage component enhances ERC721 tokens by keeping track of unique URIs with each token id, -/// and providing a functionality of metadata updates. Each token's URI, set during minting, is immutable, ensuring -/// the integrity of the metadata. It provides a reliable mechanism for linking on-chain tokens to off-chain metadata. - +/// The ERC721URIStorage component provides a flexible IERC721Metadata implementation that enables +/// storage-based token URI management. #[starknet::component] pub mod ERC721URIStorageComponent { use openzeppelin::introspection::src5::SRC5Component; @@ -64,12 +62,12 @@ pub mod ERC721URIStorageComponent { let base_uri: ByteArray = ERC721Impl::_base_uri(erc721_component); let token_uri: ByteArray = self.ERC721URIStorage_token_uris.read(token_id); - // If there is no base_uri, return the token_uri. + // If there is no base_uri, return the token_uri if base_uri.len() == 0 { return token_uri; } - // If both are set, concatenate the base_uri and token_uri. + // If both are set, concatenate the base_uri and token_uri if token_uri.len() > 0 { return format!("{}{}", base_uri, token_uri); } @@ -91,7 +89,7 @@ pub mod ERC721URIStorageComponent { +ERC721Component::HasComponent, +Drop > of InternalTrait { - /// Sets or updates the `token_uri` for the respective `token_id` + /// Sets or updates the `token_uri` for the respective `token_id`. /// /// Emits `MetadataUpdated` event. fn set_token_uri( diff --git a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo index a74df3e5e..fb977b45e 100644 --- a/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo +++ b/packages/token/src/tests/erc721/test_erc721_uri_storage.cairo @@ -27,7 +27,6 @@ fn COMPONENT_STATE() -> ComponentState { ERC721URIStorageComponent::component_state_for_testing() } -//constructor is inside only fn setup() -> ComponentState { let mut state = COMPONENT_STATE(); let mut mock_state = CONTRACT_STATE(); @@ -73,7 +72,7 @@ fn test_set_token_uri_nonexistent() { assert_only_event_metadata_update(ZERO(), TOKEN_ID_2); let mut mock_contract_state = CONTRACT_STATE(); - //check accessible after minting + // Check that the URI is accessible after minting mock_contract_state.erc721.mint(RECIPIENT(), TOKEN_ID_2); let expected = SAMPLE_URI(); @@ -142,7 +141,7 @@ fn test_token_uri_persists_when_burned_and_minted() { // Helpers // -pub fn assert_event_metadata_update(contract: ContractAddress, token_id: u256) { +fn assert_event_metadata_update(contract: ContractAddress, token_id: u256) { let event = utils::pop_log::(contract).unwrap(); let expected = ERC721URIStorageComponent::Event::MetadataUpdated(MetadataUpdated { token_id }); assert!(event == expected); diff --git a/src/tests/utils/constants.cairo b/src/tests/utils/constants.cairo index 5659e526b..d91ec7f73 100644 --- a/src/tests/utils/constants.cairo +++ b/src/tests/utils/constants.cairo @@ -45,7 +45,6 @@ pub(crate) fn BASE_URI_2() -> ByteArray { "https://api.example.com/v2/" } - pub(crate) fn SAMPLE_URI() -> ByteArray { "mock://mytoken" } From c52b60d72a1140898cf515a4566b519a76c8ee7f Mon Sep 17 00:00:00 2001 From: gerceboss Date: Tue, 16 Jul 2024 01:44:56 +0530 Subject: [PATCH 19/22] updated cairo version --- CHANGELOG.md | 7 ++++--- docs/modules/ROOT/pages/api/erc721.adoc | 4 ++-- .../token/src/erc721/extensions/erc721_uri_storage.cairo | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3772ec8a2..dd3fe1fe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +<<<<<<< HEAD - TimelockController component (#996) - HashCall implementation (#996) - Separated package for each submodule (#1065) @@ -22,6 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `openzeppelin_token` - `openzeppelin_upgrades` - `openzeppelin_utils` +======= +- ERC721URIStorage (#1031) +>>>>>>> e4e7368 (updated cairo version) ### Changed @@ -40,9 +44,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed `num_checkpoints` and `checkpoints` from `ERC20VotesABI`. -### Added (2024-07-10) - -- ERC721URIStorage (#1031) ## 0.14.0 (2024-06-14) diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 0ed8bca74..7162bf8be 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -693,7 +693,7 @@ See <>. [.contract] [[ERC721URIStorageComponent]] -=== `++ERC721URIStorageComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc721/extensions/erc721_uri_storage.cairo[{github-icon},role=heading-link] +=== `++ERC721URIStorageComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc721/extensions/erc721_uri_storage.cairo[{github-icon},role=heading-link] ```cairo use openzeppelin::token::extensions::ERC721URIStorageComponent; @@ -708,7 +708,7 @@ NOTE: Implementing xref:#ERC721Component[ERC721Component] is a requirement for t The ERC721URIStorage component provides a flexible IERC721Metadata implementation that enables storage-based token URI management. The URI of any `token_id` can be set by calling the internal function, xref:#ERC721URIStorageComponent-set_token_uri[set_token_uri]. -Updated `token_uri` can be queried through the external function xref:#ERC721URIStorageComponent-token_uri[token_uri]. +The updated URI can be queried through the external function xref:#ERC721URIStorageComponent-token_uri[token_uri]. [.contract-index#ERC721URIStorageComponent-Embeddable-Impls] .Embeddable Implementations diff --git a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo index 1784ddddd..43554a8c3 100644 --- a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo +++ b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc721/extensions/erc721_uri_storage.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc721/extensions/erc721_uri_storage.cairo) /// # ERC721URIStorage Component /// @@ -15,7 +15,7 @@ pub mod ERC721URIStorageComponent { #[storage] struct Storage { - ERC721URIStorage_token_uris: LegacyMap, + ERC721URIStorage_token_uris: Map, } #[event] From 980488216b6edfe8907d2a9812e2830fb9ea5112 Mon Sep 17 00:00:00 2001 From: gerceboss Date: Tue, 16 Jul 2024 01:49:15 +0530 Subject: [PATCH 20/22] fixed linting --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd3fe1fe2..5bb9333d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,7 +44,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed `num_checkpoints` and `checkpoints` from `ERC20VotesABI`. - ## 0.14.0 (2024-06-14) ### Changed (Breaking) From ca1eddc974e9f336ca5b96f7b3f2e9c3158c3fc6 Mon Sep 17 00:00:00 2001 From: gerceboss Date: Tue, 16 Jul 2024 14:13:21 +0530 Subject: [PATCH 21/22] removed LegacyMap added Map --- packages/token/src/erc721/extensions/erc721_uri_storage.cairo | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo index 43554a8c3..84e1ed280 100644 --- a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo +++ b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo @@ -11,6 +11,7 @@ pub mod ERC721URIStorageComponent { use openzeppelin::token::erc721::ERC721Component::InternalImpl as ERC721Impl; use openzeppelin::token::erc721::interface::IERC721Metadata; use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; + use starknet::storage::Map; use starknet::ContractAddress; #[storage] From 5f2a7de1724d739ab7e90a85eff83d4c49193750 Mon Sep 17 00:00:00 2001 From: gerceboss Date: Tue, 16 Jul 2024 14:16:21 +0530 Subject: [PATCH 22/22] fixed linting --- packages/token/src/erc721/extensions/erc721_uri_storage.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo index 84e1ed280..07de94c59 100644 --- a/packages/token/src/erc721/extensions/erc721_uri_storage.cairo +++ b/packages/token/src/erc721/extensions/erc721_uri_storage.cairo @@ -11,8 +11,8 @@ pub mod ERC721URIStorageComponent { use openzeppelin::token::erc721::ERC721Component::InternalImpl as ERC721Impl; use openzeppelin::token::erc721::interface::IERC721Metadata; use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; - use starknet::storage::Map; use starknet::ContractAddress; + use starknet::storage::Map; #[storage] struct Storage {