From b9121ddbc76463cd57ed86df6732f1163b2b3d5a Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Fri, 23 Aug 2024 12:59:01 +0700 Subject: [PATCH 01/18] Add contract hooks and add new tests --- .../nextjs/contracts/deployedContracts.ts | 878 +++++++++++++++++- .../contracts/src/YourCollectible.cairo | 84 +- .../contracts/src/test/TestContract.cairo | 112 ++- 3 files changed, 1067 insertions(+), 7 deletions(-) diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index b03ae578..fec7ea70 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -7,7 +7,883 @@ const deployedContracts = { devnet: { YourCollectible: { address: - "0x7e2708a9e40f854e3fc0a484a7ba566d85c3968834c24577a6a44702aedca8a", + "0x1e1965ee2f17bd26cbc12a602469a3784f97933da3f37cd7f46161dd9d11005", + abi: [ + { + type: "impl", + name: "YourCollectibleImpl", + interface_name: "contracts::YourCollectible::IYourCollectible", + }, + { + type: "struct", + name: "core::byte_array::ByteArray", + members: [ + { + name: "data", + type: "core::array::Array::", + }, + { + name: "pending_word", + type: "core::felt252", + }, + { + name: "pending_word_len", + type: "core::integer::u32", + }, + ], + }, + { + type: "struct", + name: "core::integer::u256", + members: [ + { + name: "low", + type: "core::integer::u128", + }, + { + name: "high", + type: "core::integer::u128", + }, + ], + }, + { + type: "interface", + name: "contracts::YourCollectible::IYourCollectible", + items: [ + { + type: "function", + name: "mint_item", + inputs: [ + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "uri", + type: "core::byte_array::ByteArray", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "external", + }, + ], + }, + { + type: "impl", + name: "WrappedIERC721MetadataImpl", + interface_name: + "openzeppelin::token::erc721::interface::IERC721Metadata", + }, + { + type: "interface", + name: "openzeppelin::token::erc721::interface::IERC721Metadata", + items: [ + { + type: "function", + name: "name", + inputs: [], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "symbol", + inputs: [], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "token_uri", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "WrappedIERC721Impl", + interface_name: "openzeppelin::token::erc721::interface::IERC721", + }, + { + type: "struct", + name: "core::array::Span::", + members: [ + { + name: "snapshot", + type: "@core::array::Array::", + }, + ], + }, + { + type: "enum", + name: "core::bool", + variants: [ + { + name: "False", + type: "()", + }, + { + name: "True", + type: "()", + }, + ], + }, + { + type: "interface", + name: "openzeppelin::token::erc721::interface::IERC721", + items: [ + { + type: "function", + name: "balance_of", + inputs: [ + { + name: "account", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "owner_of", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "safe_transfer_from", + inputs: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + { + name: "data", + type: "core::array::Span::", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "transfer_from", + inputs: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "approve", + inputs: [ + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "set_approval_for_all", + inputs: [ + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "approved", + type: "core::bool", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "get_approved", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "is_approved_for_all", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "IERC721EnumerableImpl", + interface_name: "contracts::YourCollectible::IERC721Enumerable", + }, + { + type: "interface", + name: "contracts::YourCollectible::IERC721Enumerable", + items: [ + { + type: "function", + name: "token_of_owner_by_index", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "index", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "total_supply", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "OwnableImpl", + interface_name: "openzeppelin::access::ownable::interface::IOwnable", + }, + { + type: "interface", + name: "openzeppelin::access::ownable::interface::IOwnable", + items: [ + { + type: "function", + name: "owner", + inputs: [], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "transfer_ownership", + inputs: [ + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "renounce_ownership", + inputs: [], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "impl", + name: "CounterImpl", + interface_name: "contracts::Counter::ICounter", + }, + { + type: "interface", + name: "contracts::Counter::ICounter", + items: [ + { + type: "function", + name: "current", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "increment", + inputs: [], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "decrement", + inputs: [], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "constructor", + name: "constructor", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", + kind: "struct", + members: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "token_id", + type: "core::integer::u256", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", + kind: "struct", + members: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "approved", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "token_id", + type: "core::integer::u256", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", + kind: "struct", + members: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "approved", + type: "core::bool", + kind: "data", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Event", + kind: "enum", + variants: [ + { + name: "Transfer", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", + kind: "nested", + }, + { + name: "Approval", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", + kind: "nested", + }, + { + name: "ApprovalForAll", + type: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", + kind: "nested", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "openzeppelin::introspection::src5::SRC5Component::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "enum", + variants: [ + { + name: "OwnershipTransferred", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "nested", + }, + { + name: "OwnershipTransferStarted", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "nested", + }, + ], + }, + { + type: "event", + name: "contracts::Counter::CounterComponent::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "contracts::YourCollectible::YourCollectible::Event", + kind: "enum", + variants: [ + { + name: "ERC721Event", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Event", + kind: "flat", + }, + { + name: "ERC721ReceiverEvent", + type: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", + kind: "flat", + }, + { + name: "SRC5Event", + type: "openzeppelin::introspection::src5::SRC5Component::Event", + kind: "flat", + }, + { + name: "OwnableEvent", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "flat", + }, + { + name: "CounterEvent", + type: "contracts::Counter::CounterComponent::Event", + kind: "nested", + }, + ], + }, + ], + classHash: + "0x604a5408288ed85d4220cfa7bddf6db718feec9f79455e7391ea81c063023f5", + }, + YourContract: { + address: + "0xb0fd0844236d9476db0380885b452b878131f18169874d8764b3356e36005a", + abi: [ + { + type: "impl", + name: "YourContractImpl", + interface_name: "contracts::YourContract::IYourContract", + }, + { + type: "struct", + name: "core::byte_array::ByteArray", + members: [ + { + name: "data", + type: "core::array::Array::", + }, + { + name: "pending_word", + type: "core::felt252", + }, + { + name: "pending_word_len", + type: "core::integer::u32", + }, + ], + }, + { + type: "struct", + name: "core::integer::u256", + members: [ + { + name: "low", + type: "core::integer::u128", + }, + { + name: "high", + type: "core::integer::u128", + }, + ], + }, + { + type: "enum", + name: "core::bool", + variants: [ + { + name: "False", + type: "()", + }, + { + name: "True", + type: "()", + }, + ], + }, + { + type: "interface", + name: "contracts::YourContract::IYourContract", + items: [ + { + type: "function", + name: "gretting", + inputs: [], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "set_gretting", + inputs: [ + { + name: "new_greeting", + type: "core::byte_array::ByteArray", + }, + { + name: "amount_eth", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "withdraw", + inputs: [], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "premium", + inputs: [], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "OwnableImpl", + interface_name: "openzeppelin::access::ownable::interface::IOwnable", + }, + { + type: "interface", + name: "openzeppelin::access::ownable::interface::IOwnable", + items: [ + { + type: "function", + name: "owner", + inputs: [], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "transfer_ownership", + inputs: [ + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "renounce_ownership", + inputs: [], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "constructor", + name: "constructor", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "enum", + variants: [ + { + name: "OwnershipTransferred", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "nested", + }, + { + name: "OwnershipTransferStarted", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "nested", + }, + ], + }, + { + type: "event", + name: "contracts::YourContract::YourContract::GreetingChanged", + kind: "struct", + members: [ + { + name: "greeting_setter", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_greeting", + type: "core::byte_array::ByteArray", + kind: "key", + }, + { + name: "premium", + type: "core::bool", + kind: "data", + }, + { + name: "value", + type: "core::integer::u256", + kind: "data", + }, + ], + }, + { + type: "event", + name: "contracts::YourContract::YourContract::Event", + kind: "enum", + variants: [ + { + name: "OwnableEvent", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "flat", + }, + { + name: "GreetingChanged", + type: "contracts::YourContract::YourContract::GreetingChanged", + kind: "nested", + }, + ], + }, + ], + classHash: + "0x63f027df0f2faa7a7dd3ff7ddd301ac67dfe18f947aad36b490ac4fac788b73", + }, + }, + sepolia: { + YourCollectible: { + address: + "0x45305e8c94f02c8ade9c6df459a1c84e98bbbf2ba9d0ad7047652c310d9f5e8", abi: [ { type: "impl", diff --git a/packages/snfoundry/contracts/src/YourCollectible.cairo b/packages/snfoundry/contracts/src/YourCollectible.cairo index bd9b645e..6ed8793a 100644 --- a/packages/snfoundry/contracts/src/YourCollectible.cairo +++ b/packages/snfoundry/contracts/src/YourCollectible.cairo @@ -14,6 +14,13 @@ pub trait IERC721Enumerable { #[starknet::interface] pub trait IERC721 { fn balance_of(self: @T, account: ContractAddress) -> u256; + fn transfer_from(ref self: T, from: ContractAddress, to: ContractAddress, token_id: u256); + fn owner_of(self: @T, token_id: u256) -> ContractAddress; +} + +#[starknet::interface] +pub trait IERC721Metadata { + fn token_uri(self: @T, token_id: u256) -> ByteArray; } #[starknet::contract] @@ -30,8 +37,7 @@ mod YourCollectible { IERC721_ID, IERC721_METADATA_ID, IERC721_RECEIVER_ID, }; use openzeppelin::token::erc721::{ - ERC721ReceiverComponent, ERC721Component, ERC721HooksEmptyImpl, - interface::{IERC721, IERC721Metadata} + ERC721ReceiverComponent, ERC721Component, interface::{IERC721, IERC721Metadata} }; use starknet::get_caller_address; @@ -192,8 +198,10 @@ mod YourCollectible { fn mint(ref self: ContractState, recipient: ContractAddress, token_id: u256) { assert(!recipient.is_zero(), ERC721Component::Errors::INVALID_RECEIVER); assert(!self.erc721.exists(token_id), ERC721Component::Errors::ALREADY_MINTED); - self._before_token_transfer(Zero::zero(), recipient, token_id, 1); + //self._before_token_transfer(Zero::zero(), recipient, token_id, 1); + println!("Minting token: {:?}", token_id); self.erc721.mint(recipient, token_id); + println!("Token minted: {:?}", token_id); } fn _transfer( @@ -203,7 +211,7 @@ mod YourCollectible { let owner = self.erc721._owner_of(token_id); assert(from == owner, ERC721Component::Errors::INVALID_SENDER); - self._before_token_transfer(from, to, token_id, 1); + //self._before_token_transfer(from, to, token_id, 1); assert(from == owner, ERC721Component::Errors::INVALID_SENDER); @@ -300,4 +308,72 @@ mod YourCollectible { } } } + pub impl ERC721HooksEmptyImpl of ERC721Component::ERC721HooksTrait { + fn before_update( + ref self: ERC721Component::ComponentState, + to: ContractAddress, + token_id: u256, + auth: ContractAddress + ) { + let mut contract_state = ERC721Component::HasComponent::get_contract_mut(ref self); + let token_id_counter = contract_state.token_id_counter.current(); + if (token_id == token_id_counter) { // self._add_token_to_all_tokens_enumeration(first_token_id); + let length = contract_state.all_tokens_counter.read(); + contract_state.all_tokens_index.write(token_id, length); + contract_state.all_tokens.write(length, token_id); + contract_state.all_tokens_counter.write(length + 1); + println!("auth == Zero::zero()"); + } else if (token_id < token_id_counter + + 1) { //self._remove_token_from_owner_enumeration(auth, first_token_id); + // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and + // then delete the last slot (swap and pop). + println!("auth != to"); + let owner = self.owner_of(token_id); + println!("owner {:?}", owner); + let last_token_index = self.balance_of(owner) - 1; + println!("last_token_index {:?}", last_token_index); + let token_index = contract_state.owned_tokens_index.read(token_id); + + // When the token to delete is the last token, the swap operation is unnecessary + if (token_index != last_token_index) { + let last_token_id = contract_state.owned_tokens.read((owner, last_token_index)); + // Move the last token to the slot of the to-delete token + contract_state.owned_tokens.write((owner, token_index), last_token_id); + // Update the moved token's index + println!("token_index != last_token_index"); + contract_state.owned_tokens_index.write(last_token_id, token_index); + } + + // Clear the last slot + contract_state.owned_tokens.write((owner, last_token_index), 0); + contract_state.owned_tokens_index.write(token_id, 0); + } + if (to == Zero::zero()) { //self._remove_token_from_all_tokens_enumeration(first_token_id); + let last_token_index = contract_state.all_tokens_counter.read() - 1; + let token_index = contract_state.all_tokens_index.read(token_id); + + let last_token_id = contract_state.all_tokens.read(last_token_index); + + contract_state.all_tokens.write(token_index, last_token_id); + contract_state.all_tokens_index.write(last_token_id, token_index); + + contract_state.all_tokens_index.write(token_id, 0); + contract_state.all_tokens.write(last_token_index, 0); + contract_state.all_tokens_counter.write(last_token_index); + println!("to == Zero::zero()"); + } else if (to != auth) { //self._add_token_to_owner_enumeration(to, first_token_id); + let length = self.balance_of(to); + contract_state.owned_tokens.write((to, length), token_id); + contract_state.owned_tokens_index.write(token_id, length); + println!("to != auth"); + } + } + + fn after_update( + ref self: ERC721Component::ComponentState, + to: ContractAddress, + token_id: u256, + auth: ContractAddress + ) {} + } } diff --git a/packages/snfoundry/contracts/src/test/TestContract.cairo b/packages/snfoundry/contracts/src/test/TestContract.cairo index f3a95df7..325b978f 100644 --- a/packages/snfoundry/contracts/src/test/TestContract.cairo +++ b/packages/snfoundry/contracts/src/test/TestContract.cairo @@ -1,6 +1,8 @@ +use core::clone::Clone; use contracts::YourCollectible::{ IYourCollectibleDispatcher, IYourCollectibleDispatcherTrait, IERC721Dispatcher, - IERC721DispatcherTrait, IERC721EnumerableDispatcher, IERC721EnumerableDispatcherTrait + IERC721DispatcherTrait, IERC721EnumerableDispatcher, IERC721EnumerableDispatcherTrait, + IERC721MetadataDispatcher, IERC721MetadataDispatcherTrait }; use contracts::mock_contracts::Receiver; @@ -24,6 +26,9 @@ fn OWNER() -> ContractAddress { contract_address_const::<'OWNER'>() } +fn NEW_OWNER() -> ContractAddress { + contract_address_const::<'NEW_OWNER'>() +} fn deploy_receiver() -> ContractAddress { let contract = declare("Receiver").unwrap(); let mut calldata = array![]; @@ -33,6 +38,7 @@ fn deploy_receiver() -> ContractAddress { } #[test] +// Test mint two items and transfer the last item fn test_mint_item() { // Should be able to mint an NFT let contract_address = deploy_contract("YourCollectible"); @@ -44,7 +50,7 @@ fn test_mint_item() { println!("Starting balance: {:?}", starting_balance); println!("Minting..."); let url: ByteArray = "QmfVMAmNM1kDEBYrC2TPzQDoCRFH6F5tE1e9Mr4FkkR5Xr"; - let token_id = dispatcher.mint_item(tester_address, url); + let token_id = dispatcher.mint_item(tester_address, url.clone()); let expected_token_id = 1; assert(token_id == expected_token_id, 'Token ID must be 1'); println!("Item minted! Token ID: {:?}", token_id); @@ -59,4 +65,106 @@ fn test_mint_item() { println!("token of owner by index {:?}", token); assert(token > 0, 'Token must be greater than zero'); println!("Token of owner({:?}) by index({:?}): {:?}", tester_address, index, token); + + // mint another item + let url2: ByteArray = "QmVHi3c4qkZcH3cJynzDXRm5n7dzc9R9TUtUcfnWQvhdcw"; + let token_id = dispatcher.mint_item(tester_address, url2); + let expected_token_id = 2; + assert(token_id == expected_token_id, 'Token ID must be 2'); + println!("Item minted! Token ID: {:?}", token_id); + let new_balance = erc721.balance_of(tester_address); + assert(new_balance == starting_balance + 2, 'Balance must be increased by 2'); + println!("New balance: {:?}", new_balance); + + // transfer item + let new_owner = NEW_OWNER(); + println!("new_owner address: {:?}", new_owner); + let starting_balance = erc721.balance_of(new_owner); + println!("Starting balance new_owner: {:?}", starting_balance); + + erc721.transfer_from(tester_address, new_owner, 1); + let balance_new_owner = erc721.balance_of(new_owner); + assert(balance_new_owner == starting_balance + 1, 'Balance must be increased by 1'); + println!("New balance new_owner: {:?}", balance_new_owner); + + let balance_tester = erc721.balance_of(tester_address); + assert(balance_tester == new_balance - 1, 'Balance must be decreased by 1'); + println!("New balance tester: {:?}", balance_tester); + + let erc721Metadata = IERC721MetadataDispatcher { contract_address }; + let token_uri = erc721Metadata.token_uri(1); + println!("Token URI: {:?}", token_uri); + + // owner_of + let owner = erc721.owner_of(1); + assert(owner == new_owner, 'Owner must be new_owner'); + + let owner = erc721.owner_of(2); + assert(owner == tester_address, 'Owner must be tester_address'); + + let index = erc721Enumerable.token_of_owner_by_index(new_owner, 0); + println!("token of owner by index {:?}", index); + assert(index == 1, 'Token must be 1'); + + let index = erc721Enumerable.token_of_owner_by_index(tester_address, 0); + println!("token of owner by index {:?}", index); + assert(index == 2, 'Token must be 2'); +} + + +#[test] +// Test: mint one item and transfer it to another account +fn test_mint_item2() { + // Should be able to mint an NFT + let contract_address = deploy_contract("YourCollectible"); + let dispatcher = IYourCollectibleDispatcher { contract_address }; + let erc721 = IERC721Dispatcher { contract_address }; + let tester_address = deploy_receiver(); + println!("Tester address: {:?}", tester_address); + let starting_balance = erc721.balance_of(tester_address); + println!("Starting balance: {:?}", starting_balance); + println!("Minting..."); + let url: ByteArray = "QmfVMAmNM1kDEBYrC2TPzQDoCRFH6F5tE1e9Mr4FkkR5Xr"; + let token_id = dispatcher.mint_item(tester_address, url.clone()); + let expected_token_id = 1; + assert(token_id == expected_token_id, 'Token ID must be 1'); + println!("Item minted! Token ID: {:?}", token_id); + let new_balance = erc721.balance_of(tester_address); + assert(new_balance == starting_balance + 1, 'Balance must be increased by 1'); + println!("New balance: {:?}", new_balance); + + // Should track tokens of owner by index + let erc721Enumerable = IERC721EnumerableDispatcher { contract_address }; + let index = new_balance - 1; + let token = erc721Enumerable.token_of_owner_by_index(tester_address, index); + println!("token of owner by index {:?}", token); + assert(token > 0, 'Token must be greater than zero'); + println!("Token of owner({:?}) by index({:?}): {:?}", tester_address, index, token); + + // transfer item + let new_owner = NEW_OWNER(); + println!("new_owner address: {:?}", new_owner); + let starting_balance = erc721.balance_of(new_owner); + println!("Starting balance new_owner: {:?}", starting_balance); + + erc721.transfer_from(tester_address, new_owner, 1); + let balance_new_owner = erc721.balance_of(new_owner); + assert(balance_new_owner == starting_balance + 1, 'Balance must be increased by 1'); + println!("New balance new_owner: {:?}", balance_new_owner); + + let balance_tester = erc721.balance_of(tester_address); + assert(balance_tester == new_balance - 1, 'Balance must be decreased by 1'); + println!("New balance tester: {:?}", balance_tester); + + let erc721Metadata = IERC721MetadataDispatcher { contract_address }; + let token_uri = erc721Metadata.token_uri(1); + println!("Token URI: {:?}", token_uri); + + // owner_of + let owner = erc721.owner_of(1); + assert(owner == new_owner, 'Owner must be new_owner'); + + let index = erc721Enumerable.token_of_owner_by_index(new_owner, 0); + println!("token of owner by index {:?}", index); + assert(index == 1, 'Token must be 1'); } From 4e71be51bc5dec0fffccf1f263c8e9d257a9bc40 Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Fri, 23 Aug 2024 13:01:10 +0700 Subject: [PATCH 02/18] clean files --- .../nextjs/contracts/deployedContracts.ts | 1500 +---------------- 1 file changed, 1 insertion(+), 1499 deletions(-) diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index fec7ea70..428130a3 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -3,1503 +3,5 @@ * You should not edit it manually or your changes might be overwritten. */ -const deployedContracts = { - devnet: { - YourCollectible: { - address: - "0x1e1965ee2f17bd26cbc12a602469a3784f97933da3f37cd7f46161dd9d11005", - abi: [ - { - type: "impl", - name: "YourCollectibleImpl", - interface_name: "contracts::YourCollectible::IYourCollectible", - }, - { - type: "struct", - name: "core::byte_array::ByteArray", - members: [ - { - name: "data", - type: "core::array::Array::", - }, - { - name: "pending_word", - type: "core::felt252", - }, - { - name: "pending_word_len", - type: "core::integer::u32", - }, - ], - }, - { - type: "struct", - name: "core::integer::u256", - members: [ - { - name: "low", - type: "core::integer::u128", - }, - { - name: "high", - type: "core::integer::u128", - }, - ], - }, - { - type: "interface", - name: "contracts::YourCollectible::IYourCollectible", - items: [ - { - type: "function", - name: "mint_item", - inputs: [ - { - name: "recipient", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "uri", - type: "core::byte_array::ByteArray", - }, - ], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "external", - }, - ], - }, - { - type: "impl", - name: "WrappedIERC721MetadataImpl", - interface_name: - "openzeppelin::token::erc721::interface::IERC721Metadata", - }, - { - type: "interface", - name: "openzeppelin::token::erc721::interface::IERC721Metadata", - items: [ - { - type: "function", - name: "name", - inputs: [], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "symbol", - inputs: [], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "token_uri", - inputs: [ - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "WrappedIERC721Impl", - interface_name: "openzeppelin::token::erc721::interface::IERC721", - }, - { - type: "struct", - name: "core::array::Span::", - members: [ - { - name: "snapshot", - type: "@core::array::Array::", - }, - ], - }, - { - type: "enum", - name: "core::bool", - variants: [ - { - name: "False", - type: "()", - }, - { - name: "True", - type: "()", - }, - ], - }, - { - type: "interface", - name: "openzeppelin::token::erc721::interface::IERC721", - items: [ - { - type: "function", - name: "balance_of", - inputs: [ - { - name: "account", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "owner_of", - inputs: [ - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "safe_transfer_from", - inputs: [ - { - name: "from", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "token_id", - type: "core::integer::u256", - }, - { - name: "data", - type: "core::array::Span::", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "transfer_from", - inputs: [ - { - name: "from", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "approve", - inputs: [ - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "set_approval_for_all", - inputs: [ - { - name: "operator", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "approved", - type: "core::bool", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "get_approved", - inputs: [ - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "is_approved_for_all", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "operator", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [ - { - type: "core::bool", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "IERC721EnumerableImpl", - interface_name: "contracts::YourCollectible::IERC721Enumerable", - }, - { - type: "interface", - name: "contracts::YourCollectible::IERC721Enumerable", - items: [ - { - type: "function", - name: "token_of_owner_by_index", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "index", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "total_supply", - inputs: [], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "OwnableImpl", - interface_name: "openzeppelin::access::ownable::interface::IOwnable", - }, - { - type: "interface", - name: "openzeppelin::access::ownable::interface::IOwnable", - items: [ - { - type: "function", - name: "owner", - inputs: [], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "transfer_ownership", - inputs: [ - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "renounce_ownership", - inputs: [], - outputs: [], - state_mutability: "external", - }, - ], - }, - { - type: "impl", - name: "CounterImpl", - interface_name: "contracts::Counter::ICounter", - }, - { - type: "interface", - name: "contracts::Counter::ICounter", - items: [ - { - type: "function", - name: "current", - inputs: [], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "increment", - inputs: [], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "decrement", - inputs: [], - outputs: [], - state_mutability: "external", - }, - ], - }, - { - type: "constructor", - name: "constructor", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", - kind: "struct", - members: [ - { - name: "from", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "token_id", - type: "core::integer::u256", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", - kind: "struct", - members: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "approved", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "token_id", - type: "core::integer::u256", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", - kind: "struct", - members: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "operator", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "approved", - type: "core::bool", - kind: "data", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::Event", - kind: "enum", - variants: [ - { - name: "Transfer", - type: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", - kind: "nested", - }, - { - name: "Approval", - type: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", - kind: "nested", - }, - { - name: "ApprovalForAll", - type: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", - kind: "nested", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", - kind: "enum", - variants: [], - }, - { - type: "event", - name: "openzeppelin::introspection::src5::SRC5Component::Event", - kind: "enum", - variants: [], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", - kind: "enum", - variants: [ - { - name: "OwnershipTransferred", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "nested", - }, - { - name: "OwnershipTransferStarted", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "nested", - }, - ], - }, - { - type: "event", - name: "contracts::Counter::CounterComponent::Event", - kind: "enum", - variants: [], - }, - { - type: "event", - name: "contracts::YourCollectible::YourCollectible::Event", - kind: "enum", - variants: [ - { - name: "ERC721Event", - type: "openzeppelin::token::erc721::erc721::ERC721Component::Event", - kind: "flat", - }, - { - name: "ERC721ReceiverEvent", - type: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", - kind: "flat", - }, - { - name: "SRC5Event", - type: "openzeppelin::introspection::src5::SRC5Component::Event", - kind: "flat", - }, - { - name: "OwnableEvent", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", - kind: "flat", - }, - { - name: "CounterEvent", - type: "contracts::Counter::CounterComponent::Event", - kind: "nested", - }, - ], - }, - ], - classHash: - "0x604a5408288ed85d4220cfa7bddf6db718feec9f79455e7391ea81c063023f5", - }, - YourContract: { - address: - "0xb0fd0844236d9476db0380885b452b878131f18169874d8764b3356e36005a", - abi: [ - { - type: "impl", - name: "YourContractImpl", - interface_name: "contracts::YourContract::IYourContract", - }, - { - type: "struct", - name: "core::byte_array::ByteArray", - members: [ - { - name: "data", - type: "core::array::Array::", - }, - { - name: "pending_word", - type: "core::felt252", - }, - { - name: "pending_word_len", - type: "core::integer::u32", - }, - ], - }, - { - type: "struct", - name: "core::integer::u256", - members: [ - { - name: "low", - type: "core::integer::u128", - }, - { - name: "high", - type: "core::integer::u128", - }, - ], - }, - { - type: "enum", - name: "core::bool", - variants: [ - { - name: "False", - type: "()", - }, - { - name: "True", - type: "()", - }, - ], - }, - { - type: "interface", - name: "contracts::YourContract::IYourContract", - items: [ - { - type: "function", - name: "gretting", - inputs: [], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "set_gretting", - inputs: [ - { - name: "new_greeting", - type: "core::byte_array::ByteArray", - }, - { - name: "amount_eth", - type: "core::integer::u256", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "withdraw", - inputs: [], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "premium", - inputs: [], - outputs: [ - { - type: "core::bool", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "OwnableImpl", - interface_name: "openzeppelin::access::ownable::interface::IOwnable", - }, - { - type: "interface", - name: "openzeppelin::access::ownable::interface::IOwnable", - items: [ - { - type: "function", - name: "owner", - inputs: [], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "transfer_ownership", - inputs: [ - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "renounce_ownership", - inputs: [], - outputs: [], - state_mutability: "external", - }, - ], - }, - { - type: "constructor", - name: "constructor", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", - kind: "enum", - variants: [ - { - name: "OwnershipTransferred", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "nested", - }, - { - name: "OwnershipTransferStarted", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "nested", - }, - ], - }, - { - type: "event", - name: "contracts::YourContract::YourContract::GreetingChanged", - kind: "struct", - members: [ - { - name: "greeting_setter", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_greeting", - type: "core::byte_array::ByteArray", - kind: "key", - }, - { - name: "premium", - type: "core::bool", - kind: "data", - }, - { - name: "value", - type: "core::integer::u256", - kind: "data", - }, - ], - }, - { - type: "event", - name: "contracts::YourContract::YourContract::Event", - kind: "enum", - variants: [ - { - name: "OwnableEvent", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", - kind: "flat", - }, - { - name: "GreetingChanged", - type: "contracts::YourContract::YourContract::GreetingChanged", - kind: "nested", - }, - ], - }, - ], - classHash: - "0x63f027df0f2faa7a7dd3ff7ddd301ac67dfe18f947aad36b490ac4fac788b73", - }, - }, - sepolia: { - YourCollectible: { - address: - "0x45305e8c94f02c8ade9c6df459a1c84e98bbbf2ba9d0ad7047652c310d9f5e8", - abi: [ - { - type: "impl", - name: "YourCollectibleImpl", - interface_name: "contracts::YourCollectible::IYourCollectible", - }, - { - type: "struct", - name: "core::byte_array::ByteArray", - members: [ - { - name: "data", - type: "core::array::Array::", - }, - { - name: "pending_word", - type: "core::felt252", - }, - { - name: "pending_word_len", - type: "core::integer::u32", - }, - ], - }, - { - type: "struct", - name: "core::integer::u256", - members: [ - { - name: "low", - type: "core::integer::u128", - }, - { - name: "high", - type: "core::integer::u128", - }, - ], - }, - { - type: "interface", - name: "contracts::YourCollectible::IYourCollectible", - items: [ - { - type: "function", - name: "mint_item", - inputs: [ - { - name: "recipient", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "uri", - type: "core::byte_array::ByteArray", - }, - ], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "external", - }, - ], - }, - { - type: "impl", - name: "WrappedIERC721MetadataImpl", - interface_name: - "openzeppelin::token::erc721::interface::IERC721Metadata", - }, - { - type: "interface", - name: "openzeppelin::token::erc721::interface::IERC721Metadata", - items: [ - { - type: "function", - name: "name", - inputs: [], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "symbol", - inputs: [], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "token_uri", - inputs: [ - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "WrappedIERC721Impl", - interface_name: "openzeppelin::token::erc721::interface::IERC721", - }, - { - type: "struct", - name: "core::array::Span::", - members: [ - { - name: "snapshot", - type: "@core::array::Array::", - }, - ], - }, - { - type: "enum", - name: "core::bool", - variants: [ - { - name: "False", - type: "()", - }, - { - name: "True", - type: "()", - }, - ], - }, - { - type: "interface", - name: "openzeppelin::token::erc721::interface::IERC721", - items: [ - { - type: "function", - name: "balance_of", - inputs: [ - { - name: "account", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "owner_of", - inputs: [ - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "safe_transfer_from", - inputs: [ - { - name: "from", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "token_id", - type: "core::integer::u256", - }, - { - name: "data", - type: "core::array::Span::", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "transfer_from", - inputs: [ - { - name: "from", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "approve", - inputs: [ - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "set_approval_for_all", - inputs: [ - { - name: "operator", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "approved", - type: "core::bool", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "get_approved", - inputs: [ - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "is_approved_for_all", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "operator", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [ - { - type: "core::bool", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "IERC721EnumerableImpl", - interface_name: "contracts::YourCollectible::IERC721Enumerable", - }, - { - type: "interface", - name: "contracts::YourCollectible::IERC721Enumerable", - items: [ - { - type: "function", - name: "token_of_owner_by_index", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "index", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "total_supply", - inputs: [], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "OwnableImpl", - interface_name: "openzeppelin::access::ownable::interface::IOwnable", - }, - { - type: "interface", - name: "openzeppelin::access::ownable::interface::IOwnable", - items: [ - { - type: "function", - name: "owner", - inputs: [], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "transfer_ownership", - inputs: [ - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "renounce_ownership", - inputs: [], - outputs: [], - state_mutability: "external", - }, - ], - }, - { - type: "impl", - name: "CounterImpl", - interface_name: "contracts::Counter::ICounter", - }, - { - type: "interface", - name: "contracts::Counter::ICounter", - items: [ - { - type: "function", - name: "current", - inputs: [], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "increment", - inputs: [], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "decrement", - inputs: [], - outputs: [], - state_mutability: "external", - }, - ], - }, - { - type: "constructor", - name: "constructor", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", - kind: "struct", - members: [ - { - name: "from", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "token_id", - type: "core::integer::u256", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", - kind: "struct", - members: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "approved", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "token_id", - type: "core::integer::u256", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", - kind: "struct", - members: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "operator", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "approved", - type: "core::bool", - kind: "data", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::Event", - kind: "enum", - variants: [ - { - name: "Transfer", - type: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", - kind: "nested", - }, - { - name: "Approval", - type: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", - kind: "nested", - }, - { - name: "ApprovalForAll", - type: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", - kind: "nested", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", - kind: "enum", - variants: [], - }, - { - type: "event", - name: "openzeppelin::introspection::src5::SRC5Component::Event", - kind: "enum", - variants: [], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", - kind: "enum", - variants: [ - { - name: "OwnershipTransferred", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "nested", - }, - { - name: "OwnershipTransferStarted", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "nested", - }, - ], - }, - { - type: "event", - name: "contracts::Counter::CounterComponent::Event", - kind: "enum", - variants: [], - }, - { - type: "event", - name: "contracts::YourCollectible::YourCollectible::Event", - kind: "enum", - variants: [ - { - name: "ERC721Event", - type: "openzeppelin::token::erc721::erc721::ERC721Component::Event", - kind: "flat", - }, - { - name: "ERC721ReceiverEvent", - type: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", - kind: "flat", - }, - { - name: "SRC5Event", - type: "openzeppelin::introspection::src5::SRC5Component::Event", - kind: "flat", - }, - { - name: "OwnableEvent", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", - kind: "flat", - }, - { - name: "CounterEvent", - type: "contracts::Counter::CounterComponent::Event", - kind: "nested", - }, - ], - }, - ], - classHash: - "0x604a5408288ed85d4220cfa7bddf6db718feec9f79455e7391ea81c063023f5", - }, - }, -} as const; - +const deployedContracts = {} as const; export default deployedContracts; From fe8b46e7260e74ca779935e7167fd23a5efc6974 Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Sat, 24 Aug 2024 11:00:37 +0700 Subject: [PATCH 03/18] Update tests --- .../nextjs/contracts/deployedContracts.ts | 1500 ++++++++++++++++- .../contracts/src/YourCollectible.cairo | 105 +- .../contracts/src/test/TestContract.cairo | 121 +- 3 files changed, 1576 insertions(+), 150 deletions(-) diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index 428130a3..d582036e 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -3,5 +3,1503 @@ * You should not edit it manually or your changes might be overwritten. */ -const deployedContracts = {} as const; +const deployedContracts = { + devnet: { + YourCollectible: { + address: + "0x5896b3947965c8f08628e59b008bf25eacbb577975a2bf05daf1e5644557afc", + abi: [ + { + type: "impl", + name: "YourCollectibleImpl", + interface_name: "contracts::YourCollectible::IYourCollectible", + }, + { + type: "struct", + name: "core::byte_array::ByteArray", + members: [ + { + name: "data", + type: "core::array::Array::", + }, + { + name: "pending_word", + type: "core::felt252", + }, + { + name: "pending_word_len", + type: "core::integer::u32", + }, + ], + }, + { + type: "struct", + name: "core::integer::u256", + members: [ + { + name: "low", + type: "core::integer::u128", + }, + { + name: "high", + type: "core::integer::u128", + }, + ], + }, + { + type: "interface", + name: "contracts::YourCollectible::IYourCollectible", + items: [ + { + type: "function", + name: "mint_item", + inputs: [ + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "uri", + type: "core::byte_array::ByteArray", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "external", + }, + ], + }, + { + type: "impl", + name: "WrappedIERC721MetadataImpl", + interface_name: + "openzeppelin::token::erc721::interface::IERC721Metadata", + }, + { + type: "interface", + name: "openzeppelin::token::erc721::interface::IERC721Metadata", + items: [ + { + type: "function", + name: "name", + inputs: [], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "symbol", + inputs: [], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "token_uri", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "WrappedIERC721Impl", + interface_name: "openzeppelin::token::erc721::interface::IERC721", + }, + { + type: "struct", + name: "core::array::Span::", + members: [ + { + name: "snapshot", + type: "@core::array::Array::", + }, + ], + }, + { + type: "enum", + name: "core::bool", + variants: [ + { + name: "False", + type: "()", + }, + { + name: "True", + type: "()", + }, + ], + }, + { + type: "interface", + name: "openzeppelin::token::erc721::interface::IERC721", + items: [ + { + type: "function", + name: "balance_of", + inputs: [ + { + name: "account", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "owner_of", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "safe_transfer_from", + inputs: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + { + name: "data", + type: "core::array::Span::", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "transfer_from", + inputs: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "approve", + inputs: [ + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "set_approval_for_all", + inputs: [ + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "approved", + type: "core::bool", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "get_approved", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "is_approved_for_all", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "IERC721EnumerableImpl", + interface_name: "contracts::YourCollectible::IERC721Enumerable", + }, + { + type: "interface", + name: "contracts::YourCollectible::IERC721Enumerable", + items: [ + { + type: "function", + name: "token_of_owner_by_index", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "index", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "total_supply", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "OwnableImpl", + interface_name: "openzeppelin::access::ownable::interface::IOwnable", + }, + { + type: "interface", + name: "openzeppelin::access::ownable::interface::IOwnable", + items: [ + { + type: "function", + name: "owner", + inputs: [], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "transfer_ownership", + inputs: [ + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "renounce_ownership", + inputs: [], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "impl", + name: "CounterImpl", + interface_name: "contracts::Counter::ICounter", + }, + { + type: "interface", + name: "contracts::Counter::ICounter", + items: [ + { + type: "function", + name: "current", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "increment", + inputs: [], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "decrement", + inputs: [], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "constructor", + name: "constructor", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", + kind: "struct", + members: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "token_id", + type: "core::integer::u256", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", + kind: "struct", + members: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "approved", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "token_id", + type: "core::integer::u256", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", + kind: "struct", + members: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "approved", + type: "core::bool", + kind: "data", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Event", + kind: "enum", + variants: [ + { + name: "Transfer", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", + kind: "nested", + }, + { + name: "Approval", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", + kind: "nested", + }, + { + name: "ApprovalForAll", + type: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", + kind: "nested", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "openzeppelin::introspection::src5::SRC5Component::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "enum", + variants: [ + { + name: "OwnershipTransferred", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "nested", + }, + { + name: "OwnershipTransferStarted", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "nested", + }, + ], + }, + { + type: "event", + name: "contracts::Counter::CounterComponent::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "contracts::YourCollectible::YourCollectible::Event", + kind: "enum", + variants: [ + { + name: "ERC721Event", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Event", + kind: "flat", + }, + { + name: "ERC721ReceiverEvent", + type: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", + kind: "flat", + }, + { + name: "SRC5Event", + type: "openzeppelin::introspection::src5::SRC5Component::Event", + kind: "flat", + }, + { + name: "OwnableEvent", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "flat", + }, + { + name: "CounterEvent", + type: "contracts::Counter::CounterComponent::Event", + kind: "nested", + }, + ], + }, + ], + classHash: + "0x5aa1731b70f23cb84560425832ce17fecf266d2f3bcfadd2900dea4e69092b8", + }, + YourContract: { + address: + "0xb0fd0844236d9476db0380885b452b878131f18169874d8764b3356e36005a", + abi: [ + { + type: "impl", + name: "YourContractImpl", + interface_name: "contracts::YourContract::IYourContract", + }, + { + type: "struct", + name: "core::byte_array::ByteArray", + members: [ + { + name: "data", + type: "core::array::Array::", + }, + { + name: "pending_word", + type: "core::felt252", + }, + { + name: "pending_word_len", + type: "core::integer::u32", + }, + ], + }, + { + type: "struct", + name: "core::integer::u256", + members: [ + { + name: "low", + type: "core::integer::u128", + }, + { + name: "high", + type: "core::integer::u128", + }, + ], + }, + { + type: "enum", + name: "core::bool", + variants: [ + { + name: "False", + type: "()", + }, + { + name: "True", + type: "()", + }, + ], + }, + { + type: "interface", + name: "contracts::YourContract::IYourContract", + items: [ + { + type: "function", + name: "gretting", + inputs: [], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "set_gretting", + inputs: [ + { + name: "new_greeting", + type: "core::byte_array::ByteArray", + }, + { + name: "amount_eth", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "withdraw", + inputs: [], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "premium", + inputs: [], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "OwnableImpl", + interface_name: "openzeppelin::access::ownable::interface::IOwnable", + }, + { + type: "interface", + name: "openzeppelin::access::ownable::interface::IOwnable", + items: [ + { + type: "function", + name: "owner", + inputs: [], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "transfer_ownership", + inputs: [ + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "renounce_ownership", + inputs: [], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "constructor", + name: "constructor", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "enum", + variants: [ + { + name: "OwnershipTransferred", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "nested", + }, + { + name: "OwnershipTransferStarted", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "nested", + }, + ], + }, + { + type: "event", + name: "contracts::YourContract::YourContract::GreetingChanged", + kind: "struct", + members: [ + { + name: "greeting_setter", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_greeting", + type: "core::byte_array::ByteArray", + kind: "key", + }, + { + name: "premium", + type: "core::bool", + kind: "data", + }, + { + name: "value", + type: "core::integer::u256", + kind: "data", + }, + ], + }, + { + type: "event", + name: "contracts::YourContract::YourContract::Event", + kind: "enum", + variants: [ + { + name: "OwnableEvent", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "flat", + }, + { + name: "GreetingChanged", + type: "contracts::YourContract::YourContract::GreetingChanged", + kind: "nested", + }, + ], + }, + ], + classHash: + "0x63f027df0f2faa7a7dd3ff7ddd301ac67dfe18f947aad36b490ac4fac788b73", + }, + }, + sepolia: { + YourCollectible: { + address: + "0x45305e8c94f02c8ade9c6df459a1c84e98bbbf2ba9d0ad7047652c310d9f5e8", + abi: [ + { + type: "impl", + name: "YourCollectibleImpl", + interface_name: "contracts::YourCollectible::IYourCollectible", + }, + { + type: "struct", + name: "core::byte_array::ByteArray", + members: [ + { + name: "data", + type: "core::array::Array::", + }, + { + name: "pending_word", + type: "core::felt252", + }, + { + name: "pending_word_len", + type: "core::integer::u32", + }, + ], + }, + { + type: "struct", + name: "core::integer::u256", + members: [ + { + name: "low", + type: "core::integer::u128", + }, + { + name: "high", + type: "core::integer::u128", + }, + ], + }, + { + type: "interface", + name: "contracts::YourCollectible::IYourCollectible", + items: [ + { + type: "function", + name: "mint_item", + inputs: [ + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "uri", + type: "core::byte_array::ByteArray", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "external", + }, + ], + }, + { + type: "impl", + name: "WrappedIERC721MetadataImpl", + interface_name: + "openzeppelin::token::erc721::interface::IERC721Metadata", + }, + { + type: "interface", + name: "openzeppelin::token::erc721::interface::IERC721Metadata", + items: [ + { + type: "function", + name: "name", + inputs: [], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "symbol", + inputs: [], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "token_uri", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "WrappedIERC721Impl", + interface_name: "openzeppelin::token::erc721::interface::IERC721", + }, + { + type: "struct", + name: "core::array::Span::", + members: [ + { + name: "snapshot", + type: "@core::array::Array::", + }, + ], + }, + { + type: "enum", + name: "core::bool", + variants: [ + { + name: "False", + type: "()", + }, + { + name: "True", + type: "()", + }, + ], + }, + { + type: "interface", + name: "openzeppelin::token::erc721::interface::IERC721", + items: [ + { + type: "function", + name: "balance_of", + inputs: [ + { + name: "account", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "owner_of", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "safe_transfer_from", + inputs: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + { + name: "data", + type: "core::array::Span::", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "transfer_from", + inputs: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "approve", + inputs: [ + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "set_approval_for_all", + inputs: [ + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "approved", + type: "core::bool", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "get_approved", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "is_approved_for_all", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "IERC721EnumerableImpl", + interface_name: "contracts::YourCollectible::IERC721Enumerable", + }, + { + type: "interface", + name: "contracts::YourCollectible::IERC721Enumerable", + items: [ + { + type: "function", + name: "token_of_owner_by_index", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "index", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "total_supply", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "OwnableImpl", + interface_name: "openzeppelin::access::ownable::interface::IOwnable", + }, + { + type: "interface", + name: "openzeppelin::access::ownable::interface::IOwnable", + items: [ + { + type: "function", + name: "owner", + inputs: [], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "transfer_ownership", + inputs: [ + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "renounce_ownership", + inputs: [], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "impl", + name: "CounterImpl", + interface_name: "contracts::Counter::ICounter", + }, + { + type: "interface", + name: "contracts::Counter::ICounter", + items: [ + { + type: "function", + name: "current", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "increment", + inputs: [], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "decrement", + inputs: [], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "constructor", + name: "constructor", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", + kind: "struct", + members: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "token_id", + type: "core::integer::u256", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", + kind: "struct", + members: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "approved", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "token_id", + type: "core::integer::u256", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", + kind: "struct", + members: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "approved", + type: "core::bool", + kind: "data", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Event", + kind: "enum", + variants: [ + { + name: "Transfer", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", + kind: "nested", + }, + { + name: "Approval", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", + kind: "nested", + }, + { + name: "ApprovalForAll", + type: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", + kind: "nested", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "openzeppelin::introspection::src5::SRC5Component::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "enum", + variants: [ + { + name: "OwnershipTransferred", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "nested", + }, + { + name: "OwnershipTransferStarted", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "nested", + }, + ], + }, + { + type: "event", + name: "contracts::Counter::CounterComponent::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "contracts::YourCollectible::YourCollectible::Event", + kind: "enum", + variants: [ + { + name: "ERC721Event", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Event", + kind: "flat", + }, + { + name: "ERC721ReceiverEvent", + type: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", + kind: "flat", + }, + { + name: "SRC5Event", + type: "openzeppelin::introspection::src5::SRC5Component::Event", + kind: "flat", + }, + { + name: "OwnableEvent", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "flat", + }, + { + name: "CounterEvent", + type: "contracts::Counter::CounterComponent::Event", + kind: "nested", + }, + ], + }, + ], + classHash: + "0x604a5408288ed85d4220cfa7bddf6db718feec9f79455e7391ea81c063023f5", + }, + }, +} as const; + export default deployedContracts; diff --git a/packages/snfoundry/contracts/src/YourCollectible.cairo b/packages/snfoundry/contracts/src/YourCollectible.cairo index 6ed8793a..98b8572f 100644 --- a/packages/snfoundry/contracts/src/YourCollectible.cairo +++ b/packages/snfoundry/contracts/src/YourCollectible.cairo @@ -80,8 +80,8 @@ mod YourCollectible { owned_tokens_index: LegacyMap, // Mapping with all token ids, all_tokens: LegacyMap, - // Counter for all tokens, used for enumeration - all_tokens_counter: u256, + // Helper to get the length of `all_tokens` + all_tokens_length: u256, // Mapping from token id to position in the allTokens array all_tokens_index: LegacyMap } @@ -188,7 +188,7 @@ mod YourCollectible { self.owned_tokens.read((owner, index)) } fn total_supply(self: @ContractState) -> u256 { - self.all_tokens_counter.read() + self.all_tokens_length.read() } } @@ -198,10 +198,7 @@ mod YourCollectible { fn mint(ref self: ContractState, recipient: ContractAddress, token_id: u256) { assert(!recipient.is_zero(), ERC721Component::Errors::INVALID_RECEIVER); assert(!self.erc721.exists(token_id), ERC721Component::Errors::ALREADY_MINTED); - //self._before_token_transfer(Zero::zero(), recipient, token_id, 1); - println!("Minting token: {:?}", token_id); self.erc721.mint(recipient, token_id); - println!("Token minted: {:?}", token_id); } fn _transfer( @@ -210,11 +207,6 @@ mod YourCollectible { assert(!to.is_zero(), ERC721Component::Errors::INVALID_RECEIVER); let owner = self.erc721._owner_of(token_id); assert(from == owner, ERC721Component::Errors::INVALID_SENDER); - - //self._before_token_transfer(from, to, token_id, 1); - - assert(from == owner, ERC721Component::Errors::INVALID_SENDER); - self.erc721.transfer(from, to, token_id); } @@ -239,76 +231,9 @@ mod YourCollectible { assert(self.erc721.exists(token_id), ERC721Component::Errors::INVALID_TOKEN_ID); self.token_uris.write(token_id, uri); } - - // IERC721Enumerable internal functions - fn _add_token_to_owner_enumeration( - ref self: ContractState, recipient: ContractAddress, token_id: u256 - ) { - let length = self.erc721.balance_of(recipient); - self.owned_tokens.write((recipient, length), token_id); - self.owned_tokens_index.write(token_id, length); - } - fn _remove_token_from_owner_enumeration( - ref self: ContractState, from: ContractAddress, token_id: u256 - ) { - // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and - // then delete the last slot (swap and pop). - let last_token_index = self.erc721.balance_of(from) - 1; - let token_index = self.owned_tokens_index.read(token_id); - - // When the token to delete is the last token, the swap operation is unnecessary - if (token_index != last_token_index) { - let last_token_id = self.owned_tokens.read((from, last_token_index)); - // Move the last token to the slot of the to-delete token - self.owned_tokens.write((from, token_index), last_token_id); - // Update the moved token's index - self.owned_tokens_index.write(last_token_id, token_index); - } - - // Clear the last slot - self.owned_tokens.write((from, last_token_index), 0); - self.owned_tokens_index.write(token_id, 0); - } - fn _remove_token_from_all_tokens_enumeration(ref self: ContractState, token_id: u256) { - let last_token_index = self.all_tokens_counter.read() - 1; - let token_index = self.all_tokens_index.read(token_id); - - let last_token_id = self.all_tokens.read(last_token_index); - - self.all_tokens.write(token_index, last_token_id); - self.all_tokens_index.write(last_token_id, token_index); - - self.all_tokens_index.write(token_id, 0); - self.all_tokens.write(last_token_index, 0); - self.all_tokens_counter.write(last_token_index); - } - fn _add_token_to_all_tokens_enumeration(ref self: ContractState, token_id: u256) { - let length = self.all_tokens_counter.read(); - self.all_tokens_index.write(token_id, length); - self.all_tokens.write(length, token_id); - self.all_tokens_counter.write(length + 1); - } - fn _before_token_transfer( - ref self: ContractState, - from: ContractAddress, - to: ContractAddress, - first_token_id: u256, - batch_size: u256 - ) { - assert(batch_size <= 1, 'Consecutive transfers error'); - if (from == Zero::zero()) { - self._add_token_to_all_tokens_enumeration(first_token_id); - } else if (from != to) { - self._remove_token_from_owner_enumeration(from, first_token_id); - } - if (to == Zero::zero()) { - self._remove_token_from_all_tokens_enumeration(first_token_id); - } else if (to != from) { - self._add_token_to_owner_enumeration(to, first_token_id); - } - } } - pub impl ERC721HooksEmptyImpl of ERC721Component::ERC721HooksTrait { + + impl ERC721HooksEmptyImpl of ERC721Component::ERC721HooksTrait { fn before_update( ref self: ERC721Component::ComponentState, to: ContractAddress, @@ -317,21 +242,16 @@ mod YourCollectible { ) { let mut contract_state = ERC721Component::HasComponent::get_contract_mut(ref self); let token_id_counter = contract_state.token_id_counter.current(); - if (token_id == token_id_counter) { // self._add_token_to_all_tokens_enumeration(first_token_id); - let length = contract_state.all_tokens_counter.read(); + if (token_id == token_id_counter) { // Mint case: self._add_token_to_all_tokens_enumeration(first_token_id); + let length = contract_state.all_tokens_length.read(); contract_state.all_tokens_index.write(token_id, length); contract_state.all_tokens.write(length, token_id); - contract_state.all_tokens_counter.write(length + 1); - println!("auth == Zero::zero()"); } else if (token_id < token_id_counter - + 1) { //self._remove_token_from_owner_enumeration(auth, first_token_id); + + 1) { // Transfer Case: self._remove_token_from_owner_enumeration(auth, first_token_id); // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and // then delete the last slot (swap and pop). - println!("auth != to"); let owner = self.owner_of(token_id); - println!("owner {:?}", owner); let last_token_index = self.balance_of(owner) - 1; - println!("last_token_index {:?}", last_token_index); let token_index = contract_state.owned_tokens_index.read(token_id); // When the token to delete is the last token, the swap operation is unnecessary @@ -340,7 +260,6 @@ mod YourCollectible { // Move the last token to the slot of the to-delete token contract_state.owned_tokens.write((owner, token_index), last_token_id); // Update the moved token's index - println!("token_index != last_token_index"); contract_state.owned_tokens_index.write(last_token_id, token_index); } @@ -348,8 +267,8 @@ mod YourCollectible { contract_state.owned_tokens.write((owner, last_token_index), 0); contract_state.owned_tokens_index.write(token_id, 0); } - if (to == Zero::zero()) { //self._remove_token_from_all_tokens_enumeration(first_token_id); - let last_token_index = contract_state.all_tokens_counter.read() - 1; + if (to == Zero::zero()) { // Burn case: self._remove_token_from_all_tokens_enumeration(first_token_id); + let last_token_index = contract_state.all_tokens_length.read() - 1; let token_index = contract_state.all_tokens_index.read(token_id); let last_token_id = contract_state.all_tokens.read(last_token_index); @@ -359,13 +278,11 @@ mod YourCollectible { contract_state.all_tokens_index.write(token_id, 0); contract_state.all_tokens.write(last_token_index, 0); - contract_state.all_tokens_counter.write(last_token_index); - println!("to == Zero::zero()"); + contract_state.all_tokens_length.write(last_token_index); } else if (to != auth) { //self._add_token_to_owner_enumeration(to, first_token_id); let length = self.balance_of(to); contract_state.owned_tokens.write((to, length), token_id); contract_state.owned_tokens_index.write(token_id, length); - println!("to != auth"); } } diff --git a/packages/snfoundry/contracts/src/test/TestContract.cairo b/packages/snfoundry/contracts/src/test/TestContract.cairo index 325b978f..aa963762 100644 --- a/packages/snfoundry/contracts/src/test/TestContract.cairo +++ b/packages/snfoundry/contracts/src/test/TestContract.cairo @@ -38,133 +38,144 @@ fn deploy_receiver() -> ContractAddress { } #[test] -// Test mint two items and transfer the last item +// Test mint two items and transfer the first item fn test_mint_item() { // Should be able to mint an NFT - let contract_address = deploy_contract("YourCollectible"); - let dispatcher = IYourCollectibleDispatcher { contract_address }; - let erc721 = IERC721Dispatcher { contract_address }; + let your_collectible_contract_address = deploy_contract("YourCollectible"); + let your_collectible_dispatcher = IYourCollectibleDispatcher { + contract_address: your_collectible_contract_address + }; + let erc721 = IERC721Dispatcher { contract_address: your_collectible_contract_address }; let tester_address = deploy_receiver(); println!("Tester address: {:?}", tester_address); let starting_balance = erc721.balance_of(tester_address); println!("Starting balance: {:?}", starting_balance); println!("Minting..."); let url: ByteArray = "QmfVMAmNM1kDEBYrC2TPzQDoCRFH6F5tE1e9Mr4FkkR5Xr"; - let token_id = dispatcher.mint_item(tester_address, url.clone()); + let first_token_id = your_collectible_dispatcher.mint_item(tester_address, url.clone()); let expected_token_id = 1; - assert(token_id == expected_token_id, 'Token ID must be 1'); - println!("Item minted! Token ID: {:?}", token_id); + assert(first_token_id == expected_token_id, 'Token ID must be 1'); + println!("Item minted! Token ID: {:?}", first_token_id); let new_balance = erc721.balance_of(tester_address); - assert(new_balance == starting_balance + 1, 'Balance must be increased by 1'); - println!("New balance: {:?}", new_balance); + assert_eq!(new_balance, starting_balance + 1, "Starting Balance must be increased by 1"); + println!("Tester address new balance: {:?}", new_balance); // Should track tokens of owner by index - let erc721Enumerable = IERC721EnumerableDispatcher { contract_address }; + let erc721Enumerable = IERC721EnumerableDispatcher { + contract_address: your_collectible_contract_address + }; let index = new_balance - 1; - let token = erc721Enumerable.token_of_owner_by_index(tester_address, index); - println!("token of owner by index {:?}", token); - assert(token > 0, 'Token must be greater than zero'); - println!("Token of owner({:?}) by index({:?}): {:?}", tester_address, index, token); + let first_token_id = erc721Enumerable.token_of_owner_by_index(tester_address, index); + println!("Token of owner({:?}) by index({:?}): {:?}", tester_address, index, first_token_id); + assert_eq!(first_token_id, expected_token_id, "Token must be 1"); // mint another item let url2: ByteArray = "QmVHi3c4qkZcH3cJynzDXRm5n7dzc9R9TUtUcfnWQvhdcw"; - let token_id = dispatcher.mint_item(tester_address, url2); + let second_token_id = your_collectible_dispatcher.mint_item(tester_address, url2); let expected_token_id = 2; - assert(token_id == expected_token_id, 'Token ID must be 2'); - println!("Item minted! Token ID: {:?}", token_id); + assert(second_token_id == expected_token_id, 'Token ID must be 2'); + println!("Item minted! Token ID: {:?}", second_token_id); let new_balance = erc721.balance_of(tester_address); - assert(new_balance == starting_balance + 2, 'Balance must be increased by 2'); - println!("New balance: {:?}", new_balance); + assert_eq!(new_balance, starting_balance + 2, "Starting Balance must be increased by 2"); + println!("Tester address New balance: {:?}", new_balance); // transfer item let new_owner = NEW_OWNER(); println!("new_owner address: {:?}", new_owner); - let starting_balance = erc721.balance_of(new_owner); - println!("Starting balance new_owner: {:?}", starting_balance); + let new_owner_starting_balance = erc721.balance_of(new_owner); + println!("Starting balance new_owner: {:?}", new_owner_starting_balance); - erc721.transfer_from(tester_address, new_owner, 1); + println!("Transfering first item..."); + erc721.transfer_from(tester_address, new_owner, first_token_id); // first_token_id = 1 let balance_new_owner = erc721.balance_of(new_owner); - assert(balance_new_owner == starting_balance + 1, 'Balance must be increased by 1'); + assert(balance_new_owner == new_owner_starting_balance + 1, 'Balance must be increased by 1'); println!("New balance new_owner: {:?}", balance_new_owner); let balance_tester = erc721.balance_of(tester_address); assert(balance_tester == new_balance - 1, 'Balance must be decreased by 1'); println!("New balance tester: {:?}", balance_tester); - let erc721Metadata = IERC721MetadataDispatcher { contract_address }; - let token_uri = erc721Metadata.token_uri(1); + let erc721Metadata = IERC721MetadataDispatcher { + contract_address: your_collectible_contract_address + }; + let token_uri = erc721Metadata.token_uri(first_token_id); // first_token_id = 1 println!("Token URI: {:?}", token_uri); // owner_of - let owner = erc721.owner_of(1); + let owner = erc721.owner_of(first_token_id); // first_token_id = 1 assert(owner == new_owner, 'Owner must be new_owner'); - let owner = erc721.owner_of(2); + let owner = erc721.owner_of(second_token_id); // second_token_id = 2 assert(owner == tester_address, 'Owner must be tester_address'); - let index = erc721Enumerable.token_of_owner_by_index(new_owner, 0); - println!("token of owner by index {:?}", index); + let index = erc721Enumerable.token_of_owner_by_index(new_owner, balance_new_owner - 1); + println!("token of owner by index: {:?}", index); assert(index == 1, 'Token must be 1'); - let index = erc721Enumerable.token_of_owner_by_index(tester_address, 0); - println!("token of owner by index {:?}", index); + let index = erc721Enumerable.token_of_owner_by_index(tester_address, balance_tester - 1); + println!("token of owner by index: {:?}", index); assert(index == 2, 'Token must be 2'); } #[test] -// Test: mint one item and transfer it to another account +// Test: Should be able to mint a NFT and transfer it to another account fn test_mint_item2() { - // Should be able to mint an NFT - let contract_address = deploy_contract("YourCollectible"); - let dispatcher = IYourCollectibleDispatcher { contract_address }; - let erc721 = IERC721Dispatcher { contract_address }; + let your_collectible_contract_address = deploy_contract("YourCollectible"); + let your_collectible_dispatcher = IYourCollectibleDispatcher { + contract_address: your_collectible_contract_address + }; + let erc721 = IERC721Dispatcher { contract_address: your_collectible_contract_address }; let tester_address = deploy_receiver(); println!("Tester address: {:?}", tester_address); let starting_balance = erc721.balance_of(tester_address); println!("Starting balance: {:?}", starting_balance); println!("Minting..."); let url: ByteArray = "QmfVMAmNM1kDEBYrC2TPzQDoCRFH6F5tE1e9Mr4FkkR5Xr"; - let token_id = dispatcher.mint_item(tester_address, url.clone()); + let first_token_id = your_collectible_dispatcher.mint_item(tester_address, url.clone()); let expected_token_id = 1; - assert(token_id == expected_token_id, 'Token ID must be 1'); - println!("Item minted! Token ID: {:?}", token_id); + assert(first_token_id == expected_token_id, 'Token ID must be 1'); + println!("Item minted! Token ID: {:?}", first_token_id); let new_balance = erc721.balance_of(tester_address); - assert(new_balance == starting_balance + 1, 'Balance must be increased by 1'); - println!("New balance: {:?}", new_balance); + assert_eq!(new_balance, starting_balance + 1, "Starting Balance must be increased by 1"); + println!("Tester address new balance: {:?}", new_balance); // Should track tokens of owner by index - let erc721Enumerable = IERC721EnumerableDispatcher { contract_address }; + let erc721Enumerable = IERC721EnumerableDispatcher { + contract_address: your_collectible_contract_address + }; let index = new_balance - 1; - let token = erc721Enumerable.token_of_owner_by_index(tester_address, index); - println!("token of owner by index {:?}", token); - assert(token > 0, 'Token must be greater than zero'); - println!("Token of owner({:?}) by index({:?}): {:?}", tester_address, index, token); + let first_token_id = erc721Enumerable.token_of_owner_by_index(tester_address, index); + println!("Token of owner({:?}) by index({:?}): {:?}", tester_address, index, first_token_id); + assert_eq!(first_token_id, expected_token_id, "Token must be 1"); // transfer item let new_owner = NEW_OWNER(); println!("new_owner address: {:?}", new_owner); - let starting_balance = erc721.balance_of(new_owner); - println!("Starting balance new_owner: {:?}", starting_balance); + let new_owner_starting_balance = erc721.balance_of(new_owner); + println!("Starting balance new_owner: {:?}", new_owner_starting_balance); - erc721.transfer_from(tester_address, new_owner, 1); + println!("Transfering first item..."); + erc721.transfer_from(tester_address, new_owner, first_token_id); // first_token_id = 1 let balance_new_owner = erc721.balance_of(new_owner); - assert(balance_new_owner == starting_balance + 1, 'Balance must be increased by 1'); + assert(balance_new_owner == new_owner_starting_balance + 1, 'Balance must be increased by 1'); println!("New balance new_owner: {:?}", balance_new_owner); let balance_tester = erc721.balance_of(tester_address); assert(balance_tester == new_balance - 1, 'Balance must be decreased by 1'); println!("New balance tester: {:?}", balance_tester); - let erc721Metadata = IERC721MetadataDispatcher { contract_address }; - let token_uri = erc721Metadata.token_uri(1); + let erc721Metadata = IERC721MetadataDispatcher { + contract_address: your_collectible_contract_address + }; + let token_uri = erc721Metadata.token_uri(first_token_id); // first_token_id = 1 println!("Token URI: {:?}", token_uri); // owner_of - let owner = erc721.owner_of(1); + let owner = erc721.owner_of(first_token_id); assert(owner == new_owner, 'Owner must be new_owner'); - let index = erc721Enumerable.token_of_owner_by_index(new_owner, 0); - println!("token of owner by index {:?}", index); + let index = erc721Enumerable.token_of_owner_by_index(new_owner, balance_new_owner - 1); + println!("token of owner by index: {:?}", index); assert(index == 1, 'Token must be 1'); } From 353f6093c4b50a8eb7a26de0574646dc1a700d58 Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Sat, 24 Aug 2024 11:02:17 +0700 Subject: [PATCH 04/18] Clean files --- .../nextjs/contracts/deployedContracts.ts | 1499 +---------------- 1 file changed, 1 insertion(+), 1498 deletions(-) diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index d582036e..25751cee 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -3,1503 +3,6 @@ * You should not edit it manually or your changes might be overwritten. */ -const deployedContracts = { - devnet: { - YourCollectible: { - address: - "0x5896b3947965c8f08628e59b008bf25eacbb577975a2bf05daf1e5644557afc", - abi: [ - { - type: "impl", - name: "YourCollectibleImpl", - interface_name: "contracts::YourCollectible::IYourCollectible", - }, - { - type: "struct", - name: "core::byte_array::ByteArray", - members: [ - { - name: "data", - type: "core::array::Array::", - }, - { - name: "pending_word", - type: "core::felt252", - }, - { - name: "pending_word_len", - type: "core::integer::u32", - }, - ], - }, - { - type: "struct", - name: "core::integer::u256", - members: [ - { - name: "low", - type: "core::integer::u128", - }, - { - name: "high", - type: "core::integer::u128", - }, - ], - }, - { - type: "interface", - name: "contracts::YourCollectible::IYourCollectible", - items: [ - { - type: "function", - name: "mint_item", - inputs: [ - { - name: "recipient", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "uri", - type: "core::byte_array::ByteArray", - }, - ], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "external", - }, - ], - }, - { - type: "impl", - name: "WrappedIERC721MetadataImpl", - interface_name: - "openzeppelin::token::erc721::interface::IERC721Metadata", - }, - { - type: "interface", - name: "openzeppelin::token::erc721::interface::IERC721Metadata", - items: [ - { - type: "function", - name: "name", - inputs: [], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "symbol", - inputs: [], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "token_uri", - inputs: [ - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "WrappedIERC721Impl", - interface_name: "openzeppelin::token::erc721::interface::IERC721", - }, - { - type: "struct", - name: "core::array::Span::", - members: [ - { - name: "snapshot", - type: "@core::array::Array::", - }, - ], - }, - { - type: "enum", - name: "core::bool", - variants: [ - { - name: "False", - type: "()", - }, - { - name: "True", - type: "()", - }, - ], - }, - { - type: "interface", - name: "openzeppelin::token::erc721::interface::IERC721", - items: [ - { - type: "function", - name: "balance_of", - inputs: [ - { - name: "account", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "owner_of", - inputs: [ - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "safe_transfer_from", - inputs: [ - { - name: "from", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "token_id", - type: "core::integer::u256", - }, - { - name: "data", - type: "core::array::Span::", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "transfer_from", - inputs: [ - { - name: "from", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "approve", - inputs: [ - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "set_approval_for_all", - inputs: [ - { - name: "operator", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "approved", - type: "core::bool", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "get_approved", - inputs: [ - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "is_approved_for_all", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "operator", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [ - { - type: "core::bool", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "IERC721EnumerableImpl", - interface_name: "contracts::YourCollectible::IERC721Enumerable", - }, - { - type: "interface", - name: "contracts::YourCollectible::IERC721Enumerable", - items: [ - { - type: "function", - name: "token_of_owner_by_index", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "index", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "total_supply", - inputs: [], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "OwnableImpl", - interface_name: "openzeppelin::access::ownable::interface::IOwnable", - }, - { - type: "interface", - name: "openzeppelin::access::ownable::interface::IOwnable", - items: [ - { - type: "function", - name: "owner", - inputs: [], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "transfer_ownership", - inputs: [ - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "renounce_ownership", - inputs: [], - outputs: [], - state_mutability: "external", - }, - ], - }, - { - type: "impl", - name: "CounterImpl", - interface_name: "contracts::Counter::ICounter", - }, - { - type: "interface", - name: "contracts::Counter::ICounter", - items: [ - { - type: "function", - name: "current", - inputs: [], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "increment", - inputs: [], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "decrement", - inputs: [], - outputs: [], - state_mutability: "external", - }, - ], - }, - { - type: "constructor", - name: "constructor", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", - kind: "struct", - members: [ - { - name: "from", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "token_id", - type: "core::integer::u256", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", - kind: "struct", - members: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "approved", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "token_id", - type: "core::integer::u256", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", - kind: "struct", - members: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "operator", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "approved", - type: "core::bool", - kind: "data", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::Event", - kind: "enum", - variants: [ - { - name: "Transfer", - type: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", - kind: "nested", - }, - { - name: "Approval", - type: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", - kind: "nested", - }, - { - name: "ApprovalForAll", - type: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", - kind: "nested", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", - kind: "enum", - variants: [], - }, - { - type: "event", - name: "openzeppelin::introspection::src5::SRC5Component::Event", - kind: "enum", - variants: [], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", - kind: "enum", - variants: [ - { - name: "OwnershipTransferred", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "nested", - }, - { - name: "OwnershipTransferStarted", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "nested", - }, - ], - }, - { - type: "event", - name: "contracts::Counter::CounterComponent::Event", - kind: "enum", - variants: [], - }, - { - type: "event", - name: "contracts::YourCollectible::YourCollectible::Event", - kind: "enum", - variants: [ - { - name: "ERC721Event", - type: "openzeppelin::token::erc721::erc721::ERC721Component::Event", - kind: "flat", - }, - { - name: "ERC721ReceiverEvent", - type: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", - kind: "flat", - }, - { - name: "SRC5Event", - type: "openzeppelin::introspection::src5::SRC5Component::Event", - kind: "flat", - }, - { - name: "OwnableEvent", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", - kind: "flat", - }, - { - name: "CounterEvent", - type: "contracts::Counter::CounterComponent::Event", - kind: "nested", - }, - ], - }, - ], - classHash: - "0x5aa1731b70f23cb84560425832ce17fecf266d2f3bcfadd2900dea4e69092b8", - }, - YourContract: { - address: - "0xb0fd0844236d9476db0380885b452b878131f18169874d8764b3356e36005a", - abi: [ - { - type: "impl", - name: "YourContractImpl", - interface_name: "contracts::YourContract::IYourContract", - }, - { - type: "struct", - name: "core::byte_array::ByteArray", - members: [ - { - name: "data", - type: "core::array::Array::", - }, - { - name: "pending_word", - type: "core::felt252", - }, - { - name: "pending_word_len", - type: "core::integer::u32", - }, - ], - }, - { - type: "struct", - name: "core::integer::u256", - members: [ - { - name: "low", - type: "core::integer::u128", - }, - { - name: "high", - type: "core::integer::u128", - }, - ], - }, - { - type: "enum", - name: "core::bool", - variants: [ - { - name: "False", - type: "()", - }, - { - name: "True", - type: "()", - }, - ], - }, - { - type: "interface", - name: "contracts::YourContract::IYourContract", - items: [ - { - type: "function", - name: "gretting", - inputs: [], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "set_gretting", - inputs: [ - { - name: "new_greeting", - type: "core::byte_array::ByteArray", - }, - { - name: "amount_eth", - type: "core::integer::u256", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "withdraw", - inputs: [], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "premium", - inputs: [], - outputs: [ - { - type: "core::bool", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "OwnableImpl", - interface_name: "openzeppelin::access::ownable::interface::IOwnable", - }, - { - type: "interface", - name: "openzeppelin::access::ownable::interface::IOwnable", - items: [ - { - type: "function", - name: "owner", - inputs: [], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "transfer_ownership", - inputs: [ - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "renounce_ownership", - inputs: [], - outputs: [], - state_mutability: "external", - }, - ], - }, - { - type: "constructor", - name: "constructor", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", - kind: "enum", - variants: [ - { - name: "OwnershipTransferred", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "nested", - }, - { - name: "OwnershipTransferStarted", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "nested", - }, - ], - }, - { - type: "event", - name: "contracts::YourContract::YourContract::GreetingChanged", - kind: "struct", - members: [ - { - name: "greeting_setter", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_greeting", - type: "core::byte_array::ByteArray", - kind: "key", - }, - { - name: "premium", - type: "core::bool", - kind: "data", - }, - { - name: "value", - type: "core::integer::u256", - kind: "data", - }, - ], - }, - { - type: "event", - name: "contracts::YourContract::YourContract::Event", - kind: "enum", - variants: [ - { - name: "OwnableEvent", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", - kind: "flat", - }, - { - name: "GreetingChanged", - type: "contracts::YourContract::YourContract::GreetingChanged", - kind: "nested", - }, - ], - }, - ], - classHash: - "0x63f027df0f2faa7a7dd3ff7ddd301ac67dfe18f947aad36b490ac4fac788b73", - }, - }, - sepolia: { - YourCollectible: { - address: - "0x45305e8c94f02c8ade9c6df459a1c84e98bbbf2ba9d0ad7047652c310d9f5e8", - abi: [ - { - type: "impl", - name: "YourCollectibleImpl", - interface_name: "contracts::YourCollectible::IYourCollectible", - }, - { - type: "struct", - name: "core::byte_array::ByteArray", - members: [ - { - name: "data", - type: "core::array::Array::", - }, - { - name: "pending_word", - type: "core::felt252", - }, - { - name: "pending_word_len", - type: "core::integer::u32", - }, - ], - }, - { - type: "struct", - name: "core::integer::u256", - members: [ - { - name: "low", - type: "core::integer::u128", - }, - { - name: "high", - type: "core::integer::u128", - }, - ], - }, - { - type: "interface", - name: "contracts::YourCollectible::IYourCollectible", - items: [ - { - type: "function", - name: "mint_item", - inputs: [ - { - name: "recipient", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "uri", - type: "core::byte_array::ByteArray", - }, - ], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "external", - }, - ], - }, - { - type: "impl", - name: "WrappedIERC721MetadataImpl", - interface_name: - "openzeppelin::token::erc721::interface::IERC721Metadata", - }, - { - type: "interface", - name: "openzeppelin::token::erc721::interface::IERC721Metadata", - items: [ - { - type: "function", - name: "name", - inputs: [], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "symbol", - inputs: [], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "token_uri", - inputs: [ - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "WrappedIERC721Impl", - interface_name: "openzeppelin::token::erc721::interface::IERC721", - }, - { - type: "struct", - name: "core::array::Span::", - members: [ - { - name: "snapshot", - type: "@core::array::Array::", - }, - ], - }, - { - type: "enum", - name: "core::bool", - variants: [ - { - name: "False", - type: "()", - }, - { - name: "True", - type: "()", - }, - ], - }, - { - type: "interface", - name: "openzeppelin::token::erc721::interface::IERC721", - items: [ - { - type: "function", - name: "balance_of", - inputs: [ - { - name: "account", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "owner_of", - inputs: [ - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "safe_transfer_from", - inputs: [ - { - name: "from", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "token_id", - type: "core::integer::u256", - }, - { - name: "data", - type: "core::array::Span::", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "transfer_from", - inputs: [ - { - name: "from", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "approve", - inputs: [ - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "set_approval_for_all", - inputs: [ - { - name: "operator", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "approved", - type: "core::bool", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "get_approved", - inputs: [ - { - name: "token_id", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "is_approved_for_all", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "operator", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [ - { - type: "core::bool", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "IERC721EnumerableImpl", - interface_name: "contracts::YourCollectible::IERC721Enumerable", - }, - { - type: "interface", - name: "contracts::YourCollectible::IERC721Enumerable", - items: [ - { - type: "function", - name: "token_of_owner_by_index", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "index", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "total_supply", - inputs: [], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "OwnableImpl", - interface_name: "openzeppelin::access::ownable::interface::IOwnable", - }, - { - type: "interface", - name: "openzeppelin::access::ownable::interface::IOwnable", - items: [ - { - type: "function", - name: "owner", - inputs: [], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "transfer_ownership", - inputs: [ - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "renounce_ownership", - inputs: [], - outputs: [], - state_mutability: "external", - }, - ], - }, - { - type: "impl", - name: "CounterImpl", - interface_name: "contracts::Counter::ICounter", - }, - { - type: "interface", - name: "contracts::Counter::ICounter", - items: [ - { - type: "function", - name: "current", - inputs: [], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "increment", - inputs: [], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "decrement", - inputs: [], - outputs: [], - state_mutability: "external", - }, - ], - }, - { - type: "constructor", - name: "constructor", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", - kind: "struct", - members: [ - { - name: "from", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "to", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "token_id", - type: "core::integer::u256", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", - kind: "struct", - members: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "approved", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "token_id", - type: "core::integer::u256", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", - kind: "struct", - members: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "operator", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "approved", - type: "core::bool", - kind: "data", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721::ERC721Component::Event", - kind: "enum", - variants: [ - { - name: "Transfer", - type: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", - kind: "nested", - }, - { - name: "Approval", - type: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", - kind: "nested", - }, - { - name: "ApprovalForAll", - type: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", - kind: "nested", - }, - ], - }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", - kind: "enum", - variants: [], - }, - { - type: "event", - name: "openzeppelin::introspection::src5::SRC5Component::Event", - kind: "enum", - variants: [], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", - kind: "enum", - variants: [ - { - name: "OwnershipTransferred", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "nested", - }, - { - name: "OwnershipTransferStarted", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "nested", - }, - ], - }, - { - type: "event", - name: "contracts::Counter::CounterComponent::Event", - kind: "enum", - variants: [], - }, - { - type: "event", - name: "contracts::YourCollectible::YourCollectible::Event", - kind: "enum", - variants: [ - { - name: "ERC721Event", - type: "openzeppelin::token::erc721::erc721::ERC721Component::Event", - kind: "flat", - }, - { - name: "ERC721ReceiverEvent", - type: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", - kind: "flat", - }, - { - name: "SRC5Event", - type: "openzeppelin::introspection::src5::SRC5Component::Event", - kind: "flat", - }, - { - name: "OwnableEvent", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", - kind: "flat", - }, - { - name: "CounterEvent", - type: "contracts::Counter::CounterComponent::Event", - kind: "nested", - }, - ], - }, - ], - classHash: - "0x604a5408288ed85d4220cfa7bddf6db718feec9f79455e7391ea81c063023f5", - }, - }, -} as const; +const deployedContracts = {} as const; export default deployedContracts; From b3474562472972ecea9b3b662d00852a402b9316 Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Sat, 24 Aug 2024 11:28:25 +0700 Subject: [PATCH 05/18] Remove custom transfer_from and use the provided one from OZ --- .../nextjs/contracts/deployedContracts.ts | 1499 ++++++++++++++++- .../contracts/src/YourCollectible.cairo | 60 +- .../contracts/src/test/TestContract.cairo | 13 +- 3 files changed, 1511 insertions(+), 61 deletions(-) diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index 25751cee..260bd29f 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -3,6 +3,1503 @@ * You should not edit it manually or your changes might be overwritten. */ -const deployedContracts = {} as const; +const deployedContracts = { + devnet: { + YourCollectible: { + address: + "0x4a8e24db7ae69d128fea40785e393ebbe39d8662b5f1c412f53bdc59d6f0ec2", + abi: [ + { + type: "impl", + name: "YourCollectibleImpl", + interface_name: "contracts::YourCollectible::IYourCollectible", + }, + { + type: "struct", + name: "core::byte_array::ByteArray", + members: [ + { + name: "data", + type: "core::array::Array::", + }, + { + name: "pending_word", + type: "core::felt252", + }, + { + name: "pending_word_len", + type: "core::integer::u32", + }, + ], + }, + { + type: "struct", + name: "core::integer::u256", + members: [ + { + name: "low", + type: "core::integer::u128", + }, + { + name: "high", + type: "core::integer::u128", + }, + ], + }, + { + type: "interface", + name: "contracts::YourCollectible::IYourCollectible", + items: [ + { + type: "function", + name: "mint_item", + inputs: [ + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "uri", + type: "core::byte_array::ByteArray", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "external", + }, + ], + }, + { + type: "impl", + name: "WrappedIERC721MetadataImpl", + interface_name: + "openzeppelin::token::erc721::interface::IERC721Metadata", + }, + { + type: "interface", + name: "openzeppelin::token::erc721::interface::IERC721Metadata", + items: [ + { + type: "function", + name: "name", + inputs: [], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "symbol", + inputs: [], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "token_uri", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "IERC721EnumerableImpl", + interface_name: "contracts::YourCollectible::IERC721Enumerable", + }, + { + type: "interface", + name: "contracts::YourCollectible::IERC721Enumerable", + items: [ + { + type: "function", + name: "token_of_owner_by_index", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "index", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "total_supply", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "OwnableImpl", + interface_name: "openzeppelin::access::ownable::interface::IOwnable", + }, + { + type: "interface", + name: "openzeppelin::access::ownable::interface::IOwnable", + items: [ + { + type: "function", + name: "owner", + inputs: [], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "transfer_ownership", + inputs: [ + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "renounce_ownership", + inputs: [], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "impl", + name: "CounterImpl", + interface_name: "contracts::Counter::ICounter", + }, + { + type: "interface", + name: "contracts::Counter::ICounter", + items: [ + { + type: "function", + name: "current", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "increment", + inputs: [], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "decrement", + inputs: [], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "impl", + name: "ERC721Impl", + interface_name: "openzeppelin::token::erc721::interface::IERC721", + }, + { + type: "struct", + name: "core::array::Span::", + members: [ + { + name: "snapshot", + type: "@core::array::Array::", + }, + ], + }, + { + type: "enum", + name: "core::bool", + variants: [ + { + name: "False", + type: "()", + }, + { + name: "True", + type: "()", + }, + ], + }, + { + type: "interface", + name: "openzeppelin::token::erc721::interface::IERC721", + items: [ + { + type: "function", + name: "balance_of", + inputs: [ + { + name: "account", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "owner_of", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "safe_transfer_from", + inputs: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + { + name: "data", + type: "core::array::Span::", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "transfer_from", + inputs: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "approve", + inputs: [ + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "set_approval_for_all", + inputs: [ + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "approved", + type: "core::bool", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "get_approved", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "is_approved_for_all", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "constructor", + name: "constructor", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", + kind: "struct", + members: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "token_id", + type: "core::integer::u256", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", + kind: "struct", + members: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "approved", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "token_id", + type: "core::integer::u256", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", + kind: "struct", + members: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "approved", + type: "core::bool", + kind: "data", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Event", + kind: "enum", + variants: [ + { + name: "Transfer", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", + kind: "nested", + }, + { + name: "Approval", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", + kind: "nested", + }, + { + name: "ApprovalForAll", + type: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", + kind: "nested", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "openzeppelin::introspection::src5::SRC5Component::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "enum", + variants: [ + { + name: "OwnershipTransferred", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "nested", + }, + { + name: "OwnershipTransferStarted", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "nested", + }, + ], + }, + { + type: "event", + name: "contracts::Counter::CounterComponent::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "contracts::YourCollectible::YourCollectible::Event", + kind: "enum", + variants: [ + { + name: "ERC721Event", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Event", + kind: "flat", + }, + { + name: "ERC721ReceiverEvent", + type: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", + kind: "flat", + }, + { + name: "SRC5Event", + type: "openzeppelin::introspection::src5::SRC5Component::Event", + kind: "flat", + }, + { + name: "OwnableEvent", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "flat", + }, + { + name: "CounterEvent", + type: "contracts::Counter::CounterComponent::Event", + kind: "nested", + }, + ], + }, + ], + classHash: + "0x62a190b0d435daeff798be8dc65d56c5ac236d50e92757600f5e5a1761a217f", + }, + YourContract: { + address: + "0xb0fd0844236d9476db0380885b452b878131f18169874d8764b3356e36005a", + abi: [ + { + type: "impl", + name: "YourContractImpl", + interface_name: "contracts::YourContract::IYourContract", + }, + { + type: "struct", + name: "core::byte_array::ByteArray", + members: [ + { + name: "data", + type: "core::array::Array::", + }, + { + name: "pending_word", + type: "core::felt252", + }, + { + name: "pending_word_len", + type: "core::integer::u32", + }, + ], + }, + { + type: "struct", + name: "core::integer::u256", + members: [ + { + name: "low", + type: "core::integer::u128", + }, + { + name: "high", + type: "core::integer::u128", + }, + ], + }, + { + type: "enum", + name: "core::bool", + variants: [ + { + name: "False", + type: "()", + }, + { + name: "True", + type: "()", + }, + ], + }, + { + type: "interface", + name: "contracts::YourContract::IYourContract", + items: [ + { + type: "function", + name: "gretting", + inputs: [], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "set_gretting", + inputs: [ + { + name: "new_greeting", + type: "core::byte_array::ByteArray", + }, + { + name: "amount_eth", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "withdraw", + inputs: [], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "premium", + inputs: [], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "OwnableImpl", + interface_name: "openzeppelin::access::ownable::interface::IOwnable", + }, + { + type: "interface", + name: "openzeppelin::access::ownable::interface::IOwnable", + items: [ + { + type: "function", + name: "owner", + inputs: [], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "transfer_ownership", + inputs: [ + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "renounce_ownership", + inputs: [], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "constructor", + name: "constructor", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "enum", + variants: [ + { + name: "OwnershipTransferred", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "nested", + }, + { + name: "OwnershipTransferStarted", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "nested", + }, + ], + }, + { + type: "event", + name: "contracts::YourContract::YourContract::GreetingChanged", + kind: "struct", + members: [ + { + name: "greeting_setter", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_greeting", + type: "core::byte_array::ByteArray", + kind: "key", + }, + { + name: "premium", + type: "core::bool", + kind: "data", + }, + { + name: "value", + type: "core::integer::u256", + kind: "data", + }, + ], + }, + { + type: "event", + name: "contracts::YourContract::YourContract::Event", + kind: "enum", + variants: [ + { + name: "OwnableEvent", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "flat", + }, + { + name: "GreetingChanged", + type: "contracts::YourContract::YourContract::GreetingChanged", + kind: "nested", + }, + ], + }, + ], + classHash: + "0x63f027df0f2faa7a7dd3ff7ddd301ac67dfe18f947aad36b490ac4fac788b73", + }, + }, + sepolia: { + YourCollectible: { + address: + "0x45305e8c94f02c8ade9c6df459a1c84e98bbbf2ba9d0ad7047652c310d9f5e8", + abi: [ + { + type: "impl", + name: "YourCollectibleImpl", + interface_name: "contracts::YourCollectible::IYourCollectible", + }, + { + type: "struct", + name: "core::byte_array::ByteArray", + members: [ + { + name: "data", + type: "core::array::Array::", + }, + { + name: "pending_word", + type: "core::felt252", + }, + { + name: "pending_word_len", + type: "core::integer::u32", + }, + ], + }, + { + type: "struct", + name: "core::integer::u256", + members: [ + { + name: "low", + type: "core::integer::u128", + }, + { + name: "high", + type: "core::integer::u128", + }, + ], + }, + { + type: "interface", + name: "contracts::YourCollectible::IYourCollectible", + items: [ + { + type: "function", + name: "mint_item", + inputs: [ + { + name: "recipient", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "uri", + type: "core::byte_array::ByteArray", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "external", + }, + ], + }, + { + type: "impl", + name: "WrappedIERC721MetadataImpl", + interface_name: + "openzeppelin::token::erc721::interface::IERC721Metadata", + }, + { + type: "interface", + name: "openzeppelin::token::erc721::interface::IERC721Metadata", + items: [ + { + type: "function", + name: "name", + inputs: [], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "symbol", + inputs: [], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "token_uri", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "IERC721EnumerableImpl", + interface_name: "contracts::YourCollectible::IERC721Enumerable", + }, + { + type: "interface", + name: "contracts::YourCollectible::IERC721Enumerable", + items: [ + { + type: "function", + name: "token_of_owner_by_index", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "index", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "total_supply", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "OwnableImpl", + interface_name: "openzeppelin::access::ownable::interface::IOwnable", + }, + { + type: "interface", + name: "openzeppelin::access::ownable::interface::IOwnable", + items: [ + { + type: "function", + name: "owner", + inputs: [], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "transfer_ownership", + inputs: [ + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "renounce_ownership", + inputs: [], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "impl", + name: "CounterImpl", + interface_name: "contracts::Counter::ICounter", + }, + { + type: "interface", + name: "contracts::Counter::ICounter", + items: [ + { + type: "function", + name: "current", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "increment", + inputs: [], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "decrement", + inputs: [], + outputs: [], + state_mutability: "external", + }, + ], + }, + { + type: "impl", + name: "ERC721Impl", + interface_name: "openzeppelin::token::erc721::interface::IERC721", + }, + { + type: "struct", + name: "core::array::Span::", + members: [ + { + name: "snapshot", + type: "@core::array::Array::", + }, + ], + }, + { + type: "enum", + name: "core::bool", + variants: [ + { + name: "False", + type: "()", + }, + { + name: "True", + type: "()", + }, + ], + }, + { + type: "interface", + name: "openzeppelin::token::erc721::interface::IERC721", + items: [ + { + type: "function", + name: "balance_of", + inputs: [ + { + name: "account", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "owner_of", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "safe_transfer_from", + inputs: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + { + name: "data", + type: "core::array::Span::", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "transfer_from", + inputs: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "approve", + inputs: [ + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "set_approval_for_all", + inputs: [ + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "approved", + type: "core::bool", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "get_approved", + inputs: [ + { + name: "token_id", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "is_approved_for_all", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "constructor", + name: "constructor", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", + kind: "struct", + members: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "token_id", + type: "core::integer::u256", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", + kind: "struct", + members: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "approved", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "token_id", + type: "core::integer::u256", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", + kind: "struct", + members: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "approved", + type: "core::bool", + kind: "data", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721::ERC721Component::Event", + kind: "enum", + variants: [ + { + name: "Transfer", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Transfer", + kind: "nested", + }, + { + name: "Approval", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Approval", + kind: "nested", + }, + { + name: "ApprovalForAll", + type: "openzeppelin::token::erc721::erc721::ERC721Component::ApprovalForAll", + kind: "nested", + }, + ], + }, + { + type: "event", + name: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "openzeppelin::introspection::src5::SRC5Component::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "struct", + members: [ + { + name: "previous_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + { + name: "new_owner", + type: "core::starknet::contract_address::ContractAddress", + kind: "key", + }, + ], + }, + { + type: "event", + name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "enum", + variants: [ + { + name: "OwnershipTransferred", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + kind: "nested", + }, + { + name: "OwnershipTransferStarted", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + kind: "nested", + }, + ], + }, + { + type: "event", + name: "contracts::Counter::CounterComponent::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "contracts::YourCollectible::YourCollectible::Event", + kind: "enum", + variants: [ + { + name: "ERC721Event", + type: "openzeppelin::token::erc721::erc721::ERC721Component::Event", + kind: "flat", + }, + { + name: "ERC721ReceiverEvent", + type: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", + kind: "flat", + }, + { + name: "SRC5Event", + type: "openzeppelin::introspection::src5::SRC5Component::Event", + kind: "flat", + }, + { + name: "OwnableEvent", + type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + kind: "flat", + }, + { + name: "CounterEvent", + type: "contracts::Counter::CounterComponent::Event", + kind: "nested", + }, + ], + }, + ], + classHash: + "0x604a5408288ed85d4220cfa7bddf6db718feec9f79455e7391ea81c063023f5", + }, + }, +} as const; export default deployedContracts; diff --git a/packages/snfoundry/contracts/src/YourCollectible.cairo b/packages/snfoundry/contracts/src/YourCollectible.cairo index 98b8572f..6c8c8005 100644 --- a/packages/snfoundry/contracts/src/YourCollectible.cairo +++ b/packages/snfoundry/contracts/src/YourCollectible.cairo @@ -53,6 +53,9 @@ mod YourCollectible { impl OwnableImpl = OwnableComponent::OwnableImpl; #[abi(embed_v0)] impl CounterImpl = CounterComponent::CounterImpl; + #[abi(embed_v0)] + impl ERC721Impl = ERC721Component::ERC721Impl; + impl ERC721InternalImpl = ERC721Component::InternalImpl; impl OwnableInternalImpl = OwnableComponent::InternalImpl; impl ERC721ReceiverInternalImpl = ERC721ReceiverComponent::InternalImpl; @@ -136,48 +139,6 @@ mod YourCollectible { } } - #[abi(embed_v0)] - impl WrappedIERC721Impl of IERC721 { - fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { - self.erc721.balance_of(account) - } - fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress { - self.erc721.owner_of(token_id) - } - fn safe_transfer_from( - ref self: ContractState, - from: ContractAddress, - to: ContractAddress, - token_id: u256, - data: Span - ) { - self.erc721.safe_transfer_from(from, to, token_id, data) - } - // Override transfer_from to use the internal fn _before_token_transfer() - fn transfer_from( - ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 - ) { - self._transfer_from(from, to, token_id) - } - - fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { - self.erc721.approve(to, token_id) - } - fn set_approval_for_all( - ref self: ContractState, operator: ContractAddress, approved: bool - ) { - self.erc721.set_approval_for_all(operator, approved) - } - fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { - self.erc721.get_approved(token_id) - } - fn is_approved_for_all( - self: @ContractState, owner: ContractAddress, operator: ContractAddress - ) -> bool { - self.erc721.is_approved_for_all(owner, operator) - } - } - #[abi(embed_v0)] impl IERC721EnumerableImpl of IERC721Enumerable { @@ -201,21 +162,6 @@ mod YourCollectible { self.erc721.mint(recipient, token_id); } - fn _transfer( - ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 - ) { - assert(!to.is_zero(), ERC721Component::Errors::INVALID_RECEIVER); - let owner = self.erc721._owner_of(token_id); - assert(from == owner, ERC721Component::Errors::INVALID_SENDER); - self.erc721.transfer(from, to, token_id); - } - - fn _transfer_from( - ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 - ) { - self._transfer(from, to, token_id); - } - // ERC721URIStorage internal functions fn _token_uri(self: @ContractState, token_id: u256) -> ByteArray { assert(self.erc721.exists(token_id), ERC721Component::Errors::INVALID_TOKEN_ID); diff --git a/packages/snfoundry/contracts/src/test/TestContract.cairo b/packages/snfoundry/contracts/src/test/TestContract.cairo index aa963762..dd0c4294 100644 --- a/packages/snfoundry/contracts/src/test/TestContract.cairo +++ b/packages/snfoundry/contracts/src/test/TestContract.cairo @@ -8,7 +8,7 @@ use contracts::YourCollectible::{ use contracts::mock_contracts::Receiver; use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; -use snforge_std::{declare, ContractClassTrait}; +use snforge_std::{declare, ContractClassTrait, cheat_caller_address, CheatSpan}; use starknet::ContractAddress; use starknet::contract_address_const; @@ -38,9 +38,8 @@ fn deploy_receiver() -> ContractAddress { } #[test] -// Test mint two items and transfer the first item +// Test: Should be able to mint two NFT and transfer the frist item to another account fn test_mint_item() { - // Should be able to mint an NFT let your_collectible_contract_address = deploy_contract("YourCollectible"); let your_collectible_dispatcher = IYourCollectibleDispatcher { contract_address: your_collectible_contract_address @@ -86,6 +85,10 @@ fn test_mint_item() { println!("Starting balance new_owner: {:?}", new_owner_starting_balance); println!("Transfering first item..."); + // Change the caller address of the YourCollectible to the tester_address + cheat_caller_address( + your_collectible_contract_address, tester_address, CheatSpan::TargetCalls(1) + ); erc721.transfer_from(tester_address, new_owner, first_token_id); // first_token_id = 1 let balance_new_owner = erc721.balance_of(new_owner); assert(balance_new_owner == new_owner_starting_balance + 1, 'Balance must be increased by 1'); @@ -156,6 +159,10 @@ fn test_mint_item2() { println!("Starting balance new_owner: {:?}", new_owner_starting_balance); println!("Transfering first item..."); + // Change the caller address of the YourCollectible to the tester_address + cheat_caller_address( + your_collectible_contract_address, tester_address, CheatSpan::TargetCalls(1) + ); erc721.transfer_from(tester_address, new_owner, first_token_id); // first_token_id = 1 let balance_new_owner = erc721.balance_of(new_owner); assert(balance_new_owner == new_owner_starting_balance + 1, 'Balance must be increased by 1'); From 9f4f3ae823a40bd3569fd6ae2c0b403e7610dc93 Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Sat, 24 Aug 2024 12:01:28 +0700 Subject: [PATCH 06/18] Remove custom mint() and use the provided one from OZ --- .../contracts/src/YourCollectible.cairo | 31 ++++++------------- .../contracts/src/test/TestContract.cairo | 10 +++--- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/packages/snfoundry/contracts/src/YourCollectible.cairo b/packages/snfoundry/contracts/src/YourCollectible.cairo index 6c8c8005..e8e26b2d 100644 --- a/packages/snfoundry/contracts/src/YourCollectible.cairo +++ b/packages/snfoundry/contracts/src/YourCollectible.cairo @@ -11,22 +11,15 @@ pub trait IERC721Enumerable { fn total_supply(self: @T) -> u256; } -#[starknet::interface] -pub trait IERC721 { - fn balance_of(self: @T, account: ContractAddress) -> u256; - fn transfer_from(ref self: T, from: ContractAddress, to: ContractAddress, token_id: u256); - fn owner_of(self: @T, token_id: u256) -> ContractAddress; -} - #[starknet::interface] pub trait IERC721Metadata { + // Define custom `token_uri` function fn token_uri(self: @T, token_id: u256) -> ByteArray; } #[starknet::contract] mod YourCollectible { use contracts::Counter::CounterComponent; - use core::array::ArrayTrait; use core::num::traits::zero::Zero; use openzeppelin::access::ownable::OwnableComponent; @@ -119,8 +112,8 @@ mod YourCollectible { fn mint_item(ref self: ContractState, recipient: ContractAddress, uri: ByteArray) -> u256 { self.token_id_counter.increment(); let token_id = self.token_id_counter.current(); - self.mint(recipient, token_id); - self._set_token_uri(token_id, uri); + self.erc721.mint(recipient, token_id); + self.set_token_uri(token_id, uri); token_id } } @@ -155,14 +148,7 @@ mod YourCollectible { #[generate_trait] impl InternalImpl of InternalTrait { - // IYourCollectible internal functions - fn mint(ref self: ContractState, recipient: ContractAddress, token_id: u256) { - assert(!recipient.is_zero(), ERC721Component::Errors::INVALID_RECEIVER); - assert(!self.erc721.exists(token_id), ERC721Component::Errors::ALREADY_MINTED); - self.erc721.mint(recipient, token_id); - } - - // ERC721URIStorage internal functions + // token_uri custom implementation fn _token_uri(self: @ContractState, token_id: u256) -> ByteArray { assert(self.erc721.exists(token_id), ERC721Component::Errors::INVALID_TOKEN_ID); let base_uri = self.erc721._base_uri(); @@ -173,7 +159,8 @@ mod YourCollectible { format!("{}{}", base_uri, uri) } } - fn _set_token_uri(ref self: ContractState, token_id: u256, uri: ByteArray) { + // ERC721URIStorage internal functions, + fn set_token_uri(ref self: ContractState, token_id: u256, uri: ByteArray) { assert(self.erc721.exists(token_id), ERC721Component::Errors::INVALID_TOKEN_ID); self.token_uris.write(token_id, uri); } @@ -188,12 +175,12 @@ mod YourCollectible { ) { let mut contract_state = ERC721Component::HasComponent::get_contract_mut(ref self); let token_id_counter = contract_state.token_id_counter.current(); - if (token_id == token_id_counter) { // Mint case: self._add_token_to_all_tokens_enumeration(first_token_id); + if (token_id == token_id_counter) { // Mint Token case: self._add_token_to_all_tokens_enumeration(first_token_id); let length = contract_state.all_tokens_length.read(); contract_state.all_tokens_index.write(token_id, length); contract_state.all_tokens.write(length, token_id); } else if (token_id < token_id_counter - + 1) { // Transfer Case: self._remove_token_from_owner_enumeration(auth, first_token_id); + + 1) { // Transfer Token Case: self._remove_token_from_owner_enumeration(auth, first_token_id); // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and // then delete the last slot (swap and pop). let owner = self.owner_of(token_id); @@ -213,7 +200,7 @@ mod YourCollectible { contract_state.owned_tokens.write((owner, last_token_index), 0); contract_state.owned_tokens_index.write(token_id, 0); } - if (to == Zero::zero()) { // Burn case: self._remove_token_from_all_tokens_enumeration(first_token_id); + if (to == Zero::zero()) { // Burn Token case: self._remove_token_from_all_tokens_enumeration(first_token_id); let last_token_index = contract_state.all_tokens_length.read() - 1; let token_index = contract_state.all_tokens_index.read(token_id); diff --git a/packages/snfoundry/contracts/src/test/TestContract.cairo b/packages/snfoundry/contracts/src/test/TestContract.cairo index dd0c4294..3a3fb4dc 100644 --- a/packages/snfoundry/contracts/src/test/TestContract.cairo +++ b/packages/snfoundry/contracts/src/test/TestContract.cairo @@ -1,16 +1,14 @@ use core::clone::Clone; use contracts::YourCollectible::{ - IYourCollectibleDispatcher, IYourCollectibleDispatcherTrait, IERC721Dispatcher, - IERC721DispatcherTrait, IERC721EnumerableDispatcher, IERC721EnumerableDispatcherTrait, - IERC721MetadataDispatcher, IERC721MetadataDispatcherTrait + IYourCollectibleDispatcher, IYourCollectibleDispatcherTrait, IERC721EnumerableDispatcher, + IERC721EnumerableDispatcherTrait, IERC721MetadataDispatcher, IERC721MetadataDispatcherTrait }; use contracts::mock_contracts::Receiver; -use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; +use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait}; use snforge_std::{declare, ContractClassTrait, cheat_caller_address, CheatSpan}; -use starknet::ContractAddress; -use starknet::contract_address_const; +use starknet::{ContractAddress, contract_address_const}; // Should deploy the contract fn deploy_contract(name: ByteArray) -> ContractAddress { From 958b0c883305acdff358ac354ad152389350b49e Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Sat, 24 Aug 2024 13:35:16 +0700 Subject: [PATCH 07/18] fmt --- packages/snfoundry/contracts/src/test/TestContract.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/snfoundry/contracts/src/test/TestContract.cairo b/packages/snfoundry/contracts/src/test/TestContract.cairo index 3a3fb4dc..24a668d3 100644 --- a/packages/snfoundry/contracts/src/test/TestContract.cairo +++ b/packages/snfoundry/contracts/src/test/TestContract.cairo @@ -1,12 +1,12 @@ -use core::clone::Clone; use contracts::YourCollectible::{ IYourCollectibleDispatcher, IYourCollectibleDispatcherTrait, IERC721EnumerableDispatcher, IERC721EnumerableDispatcherTrait, IERC721MetadataDispatcher, IERC721MetadataDispatcherTrait }; use contracts::mock_contracts::Receiver; -use openzeppelin::utils::serde::SerializedAppend; +use core::clone::Clone; use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait}; +use openzeppelin::utils::serde::SerializedAppend; use snforge_std::{declare, ContractClassTrait, cheat_caller_address, CheatSpan}; use starknet::{ContractAddress, contract_address_const}; From 25970fe8e0a150b35999d0eec09dba4bc04c5e78 Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Sun, 25 Aug 2024 11:42:36 +0700 Subject: [PATCH 08/18] Feat: Add ERC721Enumerable component --- .../snfoundry/contracts/src/Counter.cairo | 1 - .../contracts/src/ERC721Enumerable.cairo | 42 ++++++++++ .../contracts/src/YourCollectible.cairo | 82 +++++++------------ packages/snfoundry/contracts/src/lib.cairo | 1 + .../contracts/src/test/TestContract.cairo | 5 +- 5 files changed, 76 insertions(+), 55 deletions(-) create mode 100644 packages/snfoundry/contracts/src/ERC721Enumerable.cairo diff --git a/packages/snfoundry/contracts/src/Counter.cairo b/packages/snfoundry/contracts/src/Counter.cairo index 2309014f..522a9e36 100644 --- a/packages/snfoundry/contracts/src/Counter.cairo +++ b/packages/snfoundry/contracts/src/Counter.cairo @@ -8,7 +8,6 @@ pub trait ICounter { #[starknet::component] pub mod CounterComponent { use starknet::ContractAddress; - use starknet::get_caller_address; use super::{ICounter}; #[storage] diff --git a/packages/snfoundry/contracts/src/ERC721Enumerable.cairo b/packages/snfoundry/contracts/src/ERC721Enumerable.cairo new file mode 100644 index 00000000..4f7f6833 --- /dev/null +++ b/packages/snfoundry/contracts/src/ERC721Enumerable.cairo @@ -0,0 +1,42 @@ +use starknet::ContractAddress; + +#[starknet::interface] +pub trait IERC721Enumerable { + fn token_of_owner_by_index(self: @T, owner: ContractAddress, index: u256) -> u256; + fn total_supply(self: @T) -> u256; +} + +#[starknet::component] +pub mod ERC721EnumerableComponent { + use super::{IERC721Enumerable, ContractAddress}; + + #[storage] + struct Storage { + // Mapping from owner to list of owned token IDs + owned_tokens: LegacyMap<(ContractAddress, u256), u256>, + // Mapping from token ID to index of the owner tokens list + owned_tokens_index: LegacyMap, + // Mapping with all token ids, + all_tokens: LegacyMap, + // Helper to get the length of `all_tokens` + all_tokens_length: u256, + // Mapping from token id to position in the allTokens array + all_tokens_index: LegacyMap + } + + #[embeddable_as(ERC721EnumerableImpl)] + impl ERC721Enumerable< + TContractState, +HasComponent + > of IERC721Enumerable> { + fn token_of_owner_by_index( + self: @ComponentState, owner: ContractAddress, index: u256 + ) -> u256 { + // TODO: Add this check back in + //assert(index < self.erc721.balance_of(owner), 'Owner index out of bounds'); + self.owned_tokens.read((owner, index)) + } + fn total_supply(self: @ComponentState) -> u256 { + self.all_tokens_length.read() + } + } +} diff --git a/packages/snfoundry/contracts/src/YourCollectible.cairo b/packages/snfoundry/contracts/src/YourCollectible.cairo index e8e26b2d..dbafc3b5 100644 --- a/packages/snfoundry/contracts/src/YourCollectible.cairo +++ b/packages/snfoundry/contracts/src/YourCollectible.cairo @@ -5,12 +5,6 @@ pub trait IYourCollectible { fn mint_item(ref self: T, recipient: ContractAddress, uri: ByteArray) -> u256; } -#[starknet::interface] -pub trait IERC721Enumerable { - fn token_of_owner_by_index(self: @T, owner: ContractAddress, index: u256) -> u256; - fn total_supply(self: @T) -> u256; -} - #[starknet::interface] pub trait IERC721Metadata { // Define custom `token_uri` function @@ -20,6 +14,7 @@ pub trait IERC721Metadata { #[starknet::contract] mod YourCollectible { use contracts::Counter::CounterComponent; + use contracts::ERC721Enumerable::ERC721EnumerableComponent; use core::num::traits::zero::Zero; use openzeppelin::access::ownable::OwnableComponent; @@ -34,13 +29,14 @@ mod YourCollectible { }; use starknet::get_caller_address; - use super::{IYourCollectible, ContractAddress, IERC721Enumerable}; + use super::{IYourCollectible, ContractAddress}; component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); component!(path: CounterComponent, storage: token_id_counter, event: CounterEvent); component!(path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent); + component!(path: ERC721EnumerableComponent, storage: enumerable, event: EnumerableEvent); #[abi(embed_v0)] impl OwnableImpl = OwnableComponent::OwnableImpl; @@ -48,6 +44,9 @@ mod YourCollectible { impl CounterImpl = CounterComponent::CounterImpl; #[abi(embed_v0)] impl ERC721Impl = ERC721Component::ERC721Impl; + #[abi(embed_v0)] + impl ERC721EnumerableImpl = + ERC721EnumerableComponent::ERC721EnumerableImpl; impl ERC721InternalImpl = ERC721Component::InternalImpl; impl OwnableInternalImpl = OwnableComponent::InternalImpl; @@ -66,20 +65,11 @@ mod YourCollectible { ownable: OwnableComponent::Storage, #[substorage(v0)] token_id_counter: CounterComponent::Storage, + #[substorage(v0)] + enumerable: ERC721EnumerableComponent::Storage, // ERC721URIStorage variables // Mapping for token URIs token_uris: LegacyMap, - // IERC721Enumerable variables - // Mapping from owner to list of owned token IDs - owned_tokens: LegacyMap<(ContractAddress, u256), u256>, - // Mapping from token ID to index of the owner tokens list - owned_tokens_index: LegacyMap, - // Mapping with all token ids, - all_tokens: LegacyMap, - // Helper to get the length of `all_tokens` - all_tokens_length: u256, - // Mapping from token id to position in the allTokens array - all_tokens_index: LegacyMap } #[event] @@ -93,7 +83,8 @@ mod YourCollectible { SRC5Event: SRC5Component::Event, #[flat] OwnableEvent: OwnableComponent::Event, - CounterEvent: CounterComponent::Event + CounterEvent: CounterComponent::Event, + EnumerableEvent: ERC721EnumerableComponent::Event, } #[constructor] @@ -132,20 +123,6 @@ mod YourCollectible { } } - - #[abi(embed_v0)] - impl IERC721EnumerableImpl of IERC721Enumerable { - fn token_of_owner_by_index( - self: @ContractState, owner: ContractAddress, index: u256 - ) -> u256 { - assert(index < self.erc721.balance_of(owner), 'Owner index out of bounds'); - self.owned_tokens.read((owner, index)) - } - fn total_supply(self: @ContractState) -> u256 { - self.all_tokens_length.read() - } - } - #[generate_trait] impl InternalImpl of InternalTrait { // token_uri custom implementation @@ -175,47 +152,48 @@ mod YourCollectible { ) { let mut contract_state = ERC721Component::HasComponent::get_contract_mut(ref self); let token_id_counter = contract_state.token_id_counter.current(); + let mut enumerable = contract_state.enumerable; if (token_id == token_id_counter) { // Mint Token case: self._add_token_to_all_tokens_enumeration(first_token_id); - let length = contract_state.all_tokens_length.read(); - contract_state.all_tokens_index.write(token_id, length); - contract_state.all_tokens.write(length, token_id); + let length = enumerable.all_tokens_length.read(); + enumerable.all_tokens_index.write(token_id, length); + enumerable.all_tokens.write(length, token_id); } else if (token_id < token_id_counter + 1) { // Transfer Token Case: self._remove_token_from_owner_enumeration(auth, first_token_id); // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and // then delete the last slot (swap and pop). let owner = self.owner_of(token_id); let last_token_index = self.balance_of(owner) - 1; - let token_index = contract_state.owned_tokens_index.read(token_id); + let token_index = enumerable.owned_tokens_index.read(token_id); // When the token to delete is the last token, the swap operation is unnecessary if (token_index != last_token_index) { - let last_token_id = contract_state.owned_tokens.read((owner, last_token_index)); + let last_token_id = enumerable.owned_tokens.read((owner, last_token_index)); // Move the last token to the slot of the to-delete token - contract_state.owned_tokens.write((owner, token_index), last_token_id); + enumerable.owned_tokens.write((owner, token_index), last_token_id); // Update the moved token's index - contract_state.owned_tokens_index.write(last_token_id, token_index); + enumerable.owned_tokens_index.write(last_token_id, token_index); } // Clear the last slot - contract_state.owned_tokens.write((owner, last_token_index), 0); - contract_state.owned_tokens_index.write(token_id, 0); + enumerable.owned_tokens.write((owner, last_token_index), 0); + enumerable.owned_tokens_index.write(token_id, 0); } if (to == Zero::zero()) { // Burn Token case: self._remove_token_from_all_tokens_enumeration(first_token_id); - let last_token_index = contract_state.all_tokens_length.read() - 1; - let token_index = contract_state.all_tokens_index.read(token_id); + let last_token_index = enumerable.all_tokens_length.read() - 1; + let token_index = enumerable.all_tokens_index.read(token_id); - let last_token_id = contract_state.all_tokens.read(last_token_index); + let last_token_id = enumerable.all_tokens.read(last_token_index); - contract_state.all_tokens.write(token_index, last_token_id); - contract_state.all_tokens_index.write(last_token_id, token_index); + enumerable.all_tokens.write(token_index, last_token_id); + enumerable.all_tokens_index.write(last_token_id, token_index); - contract_state.all_tokens_index.write(token_id, 0); - contract_state.all_tokens.write(last_token_index, 0); - contract_state.all_tokens_length.write(last_token_index); + enumerable.all_tokens_index.write(token_id, 0); + enumerable.all_tokens.write(last_token_index, 0); + enumerable.all_tokens_length.write(last_token_index); } else if (to != auth) { //self._add_token_to_owner_enumeration(to, first_token_id); let length = self.balance_of(to); - contract_state.owned_tokens.write((to, length), token_id); - contract_state.owned_tokens_index.write(token_id, length); + enumerable.owned_tokens.write((to, length), token_id); + enumerable.owned_tokens_index.write(token_id, length); } } diff --git a/packages/snfoundry/contracts/src/lib.cairo b/packages/snfoundry/contracts/src/lib.cairo index 7d3c3627..b66666f0 100644 --- a/packages/snfoundry/contracts/src/lib.cairo +++ b/packages/snfoundry/contracts/src/lib.cairo @@ -1,4 +1,5 @@ mod Counter; +mod ERC721Enumerable; mod YourCollectible; mod mock_contracts { pub mod Receiver; diff --git a/packages/snfoundry/contracts/src/test/TestContract.cairo b/packages/snfoundry/contracts/src/test/TestContract.cairo index 24a668d3..ad2279a3 100644 --- a/packages/snfoundry/contracts/src/test/TestContract.cairo +++ b/packages/snfoundry/contracts/src/test/TestContract.cairo @@ -1,9 +1,10 @@ use contracts::YourCollectible::{ - IYourCollectibleDispatcher, IYourCollectibleDispatcherTrait, IERC721EnumerableDispatcher, - IERC721EnumerableDispatcherTrait, IERC721MetadataDispatcher, IERC721MetadataDispatcherTrait + IYourCollectibleDispatcher, IYourCollectibleDispatcherTrait, IERC721MetadataDispatcher, + IERC721MetadataDispatcherTrait }; use contracts::mock_contracts::Receiver; +use contracts::ERC721Enumerable::{IERC721EnumerableDispatcher, IERC721EnumerableDispatcherTrait}; use core::clone::Clone; use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait}; use openzeppelin::utils::serde::SerializedAppend; From 3f73b6cb236a54d7736954b1635149f8072cc4d2 Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Sun, 25 Aug 2024 12:09:11 +0700 Subject: [PATCH 09/18] fmt --- packages/snfoundry/contracts/src/test/TestContract.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/snfoundry/contracts/src/test/TestContract.cairo b/packages/snfoundry/contracts/src/test/TestContract.cairo index ad2279a3..1b04206b 100644 --- a/packages/snfoundry/contracts/src/test/TestContract.cairo +++ b/packages/snfoundry/contracts/src/test/TestContract.cairo @@ -1,10 +1,10 @@ +use contracts::ERC721Enumerable::{IERC721EnumerableDispatcher, IERC721EnumerableDispatcherTrait}; use contracts::YourCollectible::{ IYourCollectibleDispatcher, IYourCollectibleDispatcherTrait, IERC721MetadataDispatcher, IERC721MetadataDispatcherTrait }; use contracts::mock_contracts::Receiver; -use contracts::ERC721Enumerable::{IERC721EnumerableDispatcher, IERC721EnumerableDispatcherTrait}; use core::clone::Clone; use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait}; use openzeppelin::utils::serde::SerializedAppend; From 71b24aaf8c009f2f96562d947d0a450764a8bfc5 Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Sun, 25 Aug 2024 15:04:16 +0700 Subject: [PATCH 10/18] Feat: Create components mod --- .../nextjs/contracts/deployedContracts.ts | 462 +++++------------- .../contracts/src/YourCollectible.cairo | 23 +- .../src/{ => components}/Counter.cairo | 0 .../{ => components}/ERC721Enumerable.cairo | 0 packages/snfoundry/contracts/src/lib.cairo | 11 +- .../contracts/src/test/TestContract.cairo | 13 +- 6 files changed, 137 insertions(+), 372 deletions(-) rename packages/snfoundry/contracts/src/{ => components}/Counter.cairo (100%) rename packages/snfoundry/contracts/src/{ => components}/ERC721Enumerable.cairo (100%) diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index 260bd29f..3247bf82 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -7,7 +7,7 @@ const deployedContracts = { devnet: { YourCollectible: { address: - "0x4a8e24db7ae69d128fea40785e393ebbe39d8662b5f1c412f53bdc59d6f0ec2", + "0x7e0a62ddae222f9fe2b542ec71b2ae8e85bb44c138cd74695978f6d9999cb85", abi: [ { type: "impl", @@ -122,48 +122,6 @@ const deployedContracts = { }, ], }, - { - type: "impl", - name: "IERC721EnumerableImpl", - interface_name: "contracts::YourCollectible::IERC721Enumerable", - }, - { - type: "interface", - name: "contracts::YourCollectible::IERC721Enumerable", - items: [ - { - type: "function", - name: "token_of_owner_by_index", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "index", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "total_supply", - inputs: [], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - ], - }, { type: "impl", name: "OwnableImpl", @@ -208,11 +166,11 @@ const deployedContracts = { { type: "impl", name: "CounterImpl", - interface_name: "contracts::Counter::ICounter", + interface_name: "contracts::components::Counter::ICounter", }, { type: "interface", - name: "contracts::Counter::ICounter", + name: "contracts::components::Counter::ICounter", items: [ { type: "function", @@ -420,6 +378,49 @@ const deployedContracts = { }, ], }, + { + type: "impl", + name: "ERC721EnumerableImpl", + interface_name: + "contracts::components::ERC721Enumerable::IERC721Enumerable", + }, + { + type: "interface", + name: "contracts::components::ERC721Enumerable::IERC721Enumerable", + items: [ + { + type: "function", + name: "token_of_owner_by_index", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "index", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "total_supply", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + ], + }, { type: "constructor", name: "constructor", @@ -583,7 +584,13 @@ const deployedContracts = { }, { type: "event", - name: "contracts::Counter::CounterComponent::Event", + name: "contracts::components::Counter::CounterComponent::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "contracts::components::ERC721Enumerable::ERC721EnumerableComponent::Event", kind: "enum", variants: [], }, @@ -614,270 +621,19 @@ const deployedContracts = { }, { name: "CounterEvent", - type: "contracts::Counter::CounterComponent::Event", + type: "contracts::components::Counter::CounterComponent::Event", kind: "nested", }, - ], - }, - ], - classHash: - "0x62a190b0d435daeff798be8dc65d56c5ac236d50e92757600f5e5a1761a217f", - }, - YourContract: { - address: - "0xb0fd0844236d9476db0380885b452b878131f18169874d8764b3356e36005a", - abi: [ - { - type: "impl", - name: "YourContractImpl", - interface_name: "contracts::YourContract::IYourContract", - }, - { - type: "struct", - name: "core::byte_array::ByteArray", - members: [ - { - name: "data", - type: "core::array::Array::", - }, - { - name: "pending_word", - type: "core::felt252", - }, - { - name: "pending_word_len", - type: "core::integer::u32", - }, - ], - }, - { - type: "struct", - name: "core::integer::u256", - members: [ - { - name: "low", - type: "core::integer::u128", - }, { - name: "high", - type: "core::integer::u128", - }, - ], - }, - { - type: "enum", - name: "core::bool", - variants: [ - { - name: "False", - type: "()", - }, - { - name: "True", - type: "()", - }, - ], - }, - { - type: "interface", - name: "contracts::YourContract::IYourContract", - items: [ - { - type: "function", - name: "gretting", - inputs: [], - outputs: [ - { - type: "core::byte_array::ByteArray", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "set_gretting", - inputs: [ - { - name: "new_greeting", - type: "core::byte_array::ByteArray", - }, - { - name: "amount_eth", - type: "core::integer::u256", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "withdraw", - inputs: [], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "premium", - inputs: [], - outputs: [ - { - type: "core::bool", - }, - ], - state_mutability: "view", - }, - ], - }, - { - type: "impl", - name: "OwnableImpl", - interface_name: "openzeppelin::access::ownable::interface::IOwnable", - }, - { - type: "interface", - name: "openzeppelin::access::ownable::interface::IOwnable", - items: [ - { - type: "function", - name: "owner", - inputs: [], - outputs: [ - { - type: "core::starknet::contract_address::ContractAddress", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "transfer_ownership", - inputs: [ - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - outputs: [], - state_mutability: "external", - }, - { - type: "function", - name: "renounce_ownership", - inputs: [], - outputs: [], - state_mutability: "external", - }, - ], - }, - { - type: "constructor", - name: "constructor", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "struct", - members: [ - { - name: "previous_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_owner", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - ], - }, - { - type: "event", - name: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", - kind: "enum", - variants: [ - { - name: "OwnershipTransferred", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", - kind: "nested", - }, - { - name: "OwnershipTransferStarted", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", - kind: "nested", - }, - ], - }, - { - type: "event", - name: "contracts::YourContract::YourContract::GreetingChanged", - kind: "struct", - members: [ - { - name: "greeting_setter", - type: "core::starknet::contract_address::ContractAddress", - kind: "key", - }, - { - name: "new_greeting", - type: "core::byte_array::ByteArray", - kind: "key", - }, - { - name: "premium", - type: "core::bool", - kind: "data", - }, - { - name: "value", - type: "core::integer::u256", - kind: "data", - }, - ], - }, - { - type: "event", - name: "contracts::YourContract::YourContract::Event", - kind: "enum", - variants: [ - { - name: "OwnableEvent", - type: "openzeppelin::access::ownable::ownable::OwnableComponent::Event", - kind: "flat", - }, - { - name: "GreetingChanged", - type: "contracts::YourContract::YourContract::GreetingChanged", + name: "EnumerableEvent", + type: "contracts::components::ERC721Enumerable::ERC721EnumerableComponent::Event", kind: "nested", }, ], }, ], classHash: - "0x63f027df0f2faa7a7dd3ff7ddd301ac67dfe18f947aad36b490ac4fac788b73", + "0x3f6160b2e5a3d6a7105d0748e608f0c44a3166c6b9bd6390be82b6f3fcf68b4", }, }, sepolia: { @@ -998,48 +754,6 @@ const deployedContracts = { }, ], }, - { - type: "impl", - name: "IERC721EnumerableImpl", - interface_name: "contracts::YourCollectible::IERC721Enumerable", - }, - { - type: "interface", - name: "contracts::YourCollectible::IERC721Enumerable", - items: [ - { - type: "function", - name: "token_of_owner_by_index", - inputs: [ - { - name: "owner", - type: "core::starknet::contract_address::ContractAddress", - }, - { - name: "index", - type: "core::integer::u256", - }, - ], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - { - type: "function", - name: "total_supply", - inputs: [], - outputs: [ - { - type: "core::integer::u256", - }, - ], - state_mutability: "view", - }, - ], - }, { type: "impl", name: "OwnableImpl", @@ -1084,11 +798,11 @@ const deployedContracts = { { type: "impl", name: "CounterImpl", - interface_name: "contracts::Counter::ICounter", + interface_name: "contracts::components::Counter::ICounter", }, { type: "interface", - name: "contracts::Counter::ICounter", + name: "contracts::components::Counter::ICounter", items: [ { type: "function", @@ -1296,6 +1010,49 @@ const deployedContracts = { }, ], }, + { + type: "impl", + name: "ERC721EnumerableImpl", + interface_name: + "contracts::components::ERC721Enumerable::IERC721Enumerable", + }, + { + type: "interface", + name: "contracts::components::ERC721Enumerable::IERC721Enumerable", + items: [ + { + type: "function", + name: "token_of_owner_by_index", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "index", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "total_supply", + inputs: [], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + ], + }, { type: "constructor", name: "constructor", @@ -1459,7 +1216,13 @@ const deployedContracts = { }, { type: "event", - name: "contracts::Counter::CounterComponent::Event", + name: "contracts::components::Counter::CounterComponent::Event", + kind: "enum", + variants: [], + }, + { + type: "event", + name: "contracts::components::ERC721Enumerable::ERC721EnumerableComponent::Event", kind: "enum", variants: [], }, @@ -1490,7 +1253,12 @@ const deployedContracts = { }, { name: "CounterEvent", - type: "contracts::Counter::CounterComponent::Event", + type: "contracts::components::Counter::CounterComponent::Event", + kind: "nested", + }, + { + name: "EnumerableEvent", + type: "contracts::components::ERC721Enumerable::ERC721EnumerableComponent::Event", kind: "nested", }, ], diff --git a/packages/snfoundry/contracts/src/YourCollectible.cairo b/packages/snfoundry/contracts/src/YourCollectible.cairo index dbafc3b5..ce4a5054 100644 --- a/packages/snfoundry/contracts/src/YourCollectible.cairo +++ b/packages/snfoundry/contracts/src/YourCollectible.cairo @@ -5,19 +5,13 @@ pub trait IYourCollectible { fn mint_item(ref self: T, recipient: ContractAddress, uri: ByteArray) -> u256; } -#[starknet::interface] -pub trait IERC721Metadata { - // Define custom `token_uri` function - fn token_uri(self: @T, token_id: u256) -> ByteArray; -} - #[starknet::contract] mod YourCollectible { - use contracts::Counter::CounterComponent; - use contracts::ERC721Enumerable::ERC721EnumerableComponent; + use contracts::components::Counter::CounterComponent; + use contracts::components::ERC721Enumerable::ERC721EnumerableComponent; use core::num::traits::zero::Zero; - use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::access::ownable::OwnableComponent; use openzeppelin::account::interface::ISRC6_ID; use openzeppelin::introspection::interface::{ISRC5, ISRC5_ID}; use openzeppelin::introspection::src5::SRC5Component; @@ -25,10 +19,9 @@ mod YourCollectible { IERC721_ID, IERC721_METADATA_ID, IERC721_RECEIVER_ID, }; use openzeppelin::token::erc721::{ - ERC721ReceiverComponent, ERC721Component, interface::{IERC721, IERC721Metadata} + ERC721ReceiverComponent, ERC721Component, interface::{IERC721Metadata} }; - use starknet::get_caller_address; use super::{IYourCollectible, ContractAddress}; component!(path: ERC721Component, storage: erc721, event: ERC721Event); @@ -153,12 +146,12 @@ mod YourCollectible { let mut contract_state = ERC721Component::HasComponent::get_contract_mut(ref self); let token_id_counter = contract_state.token_id_counter.current(); let mut enumerable = contract_state.enumerable; - if (token_id == token_id_counter) { // Mint Token case: self._add_token_to_all_tokens_enumeration(first_token_id); + if (token_id == token_id_counter) { // `Mint Token` case: Add token to `all_tokens` enumerable component let length = enumerable.all_tokens_length.read(); enumerable.all_tokens_index.write(token_id, length); enumerable.all_tokens.write(length, token_id); } else if (token_id < token_id_counter - + 1) { // Transfer Token Case: self._remove_token_from_owner_enumeration(auth, first_token_id); + + 1) { // `Transfer Token` Case: Remove token from owner and update enumerable component // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and // then delete the last slot (swap and pop). let owner = self.owner_of(token_id); @@ -178,7 +171,7 @@ mod YourCollectible { enumerable.owned_tokens.write((owner, last_token_index), 0); enumerable.owned_tokens_index.write(token_id, 0); } - if (to == Zero::zero()) { // Burn Token case: self._remove_token_from_all_tokens_enumeration(first_token_id); + if (to == Zero::zero()) { // `Burn Token` case: Remove token from `all_tokens` enumerable component let last_token_index = enumerable.all_tokens_length.read() - 1; let token_index = enumerable.all_tokens_index.read(token_id); @@ -190,7 +183,7 @@ mod YourCollectible { enumerable.all_tokens_index.write(token_id, 0); enumerable.all_tokens.write(last_token_index, 0); enumerable.all_tokens_length.write(last_token_index); - } else if (to != auth) { //self._add_token_to_owner_enumeration(to, first_token_id); + } else if (to != auth) { // `Mint Token` and `Transfer Token` case: Add token owner to `owned_tokens` enumerable component let length = self.balance_of(to); enumerable.owned_tokens.write((to, length), token_id); enumerable.owned_tokens_index.write(token_id, length); diff --git a/packages/snfoundry/contracts/src/Counter.cairo b/packages/snfoundry/contracts/src/components/Counter.cairo similarity index 100% rename from packages/snfoundry/contracts/src/Counter.cairo rename to packages/snfoundry/contracts/src/components/Counter.cairo diff --git a/packages/snfoundry/contracts/src/ERC721Enumerable.cairo b/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo similarity index 100% rename from packages/snfoundry/contracts/src/ERC721Enumerable.cairo rename to packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo diff --git a/packages/snfoundry/contracts/src/lib.cairo b/packages/snfoundry/contracts/src/lib.cairo index b66666f0..9bc65605 100644 --- a/packages/snfoundry/contracts/src/lib.cairo +++ b/packages/snfoundry/contracts/src/lib.cairo @@ -1,10 +1,13 @@ -mod Counter; -mod ERC721Enumerable; mod YourCollectible; -mod mock_contracts { - pub mod Receiver; +mod components { + pub mod Counter; + pub mod ERC721Enumerable; } + #[cfg(test)] mod test { mod TestContract; } +mod mock_contracts { + pub mod Receiver; +} diff --git a/packages/snfoundry/contracts/src/test/TestContract.cairo b/packages/snfoundry/contracts/src/test/TestContract.cairo index 1b04206b..ab6d450d 100644 --- a/packages/snfoundry/contracts/src/test/TestContract.cairo +++ b/packages/snfoundry/contracts/src/test/TestContract.cairo @@ -1,12 +1,13 @@ -use contracts::ERC721Enumerable::{IERC721EnumerableDispatcher, IERC721EnumerableDispatcherTrait}; -use contracts::YourCollectible::{ - IYourCollectibleDispatcher, IYourCollectibleDispatcherTrait, IERC721MetadataDispatcher, - IERC721MetadataDispatcherTrait +use contracts::YourCollectible::{IYourCollectibleDispatcher, IYourCollectibleDispatcherTrait}; +use contracts::components::ERC721Enumerable::{ + IERC721EnumerableDispatcher, IERC721EnumerableDispatcherTrait }; use contracts::mock_contracts::Receiver; -use core::clone::Clone; -use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait}; +use openzeppelin::token::erc721::interface::{ + IERC721Dispatcher, IERC721DispatcherTrait, IERC721MetadataDispatcher, + IERC721MetadataDispatcherTrait +}; use openzeppelin::utils::serde::SerializedAppend; use snforge_std::{declare, ContractClassTrait, cheat_caller_address, CheatSpan}; use starknet::{ContractAddress, contract_address_const}; From 84fec27b1889362f78abfae8a0a120fd3238e8df Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Sun, 25 Aug 2024 18:20:45 +0700 Subject: [PATCH 11/18] Fix: Add comments --- packages/snfoundry/contracts/src/YourCollectible.cairo | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/snfoundry/contracts/src/YourCollectible.cairo b/packages/snfoundry/contracts/src/YourCollectible.cairo index ce4a5054..9b36862a 100644 --- a/packages/snfoundry/contracts/src/YourCollectible.cairo +++ b/packages/snfoundry/contracts/src/YourCollectible.cairo @@ -137,6 +137,8 @@ mod YourCollectible { } impl ERC721HooksEmptyImpl of ERC721Component::ERC721HooksTrait { + // Implement this to add custom logic to the ERC721 hooks + // Similat to _beforeTokenTransfer in OpenZeppelin ERC721.sol fn before_update( ref self: ERC721Component::ComponentState, to: ContractAddress, From 8f73e2716ec9a5def9b20b71dc3667c8482a492b Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Sun, 25 Aug 2024 21:59:04 +0700 Subject: [PATCH 12/18] Clean files --- .../nextjs/contracts/deployedContracts.ts | 4 ++-- .../contracts/src/YourCollectible.cairo | 20 ++++--------------- .../src/mock_contracts/Receiver.cairo | 5 +---- 3 files changed, 7 insertions(+), 22 deletions(-) diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index 3247bf82..b9bb8c86 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -7,7 +7,7 @@ const deployedContracts = { devnet: { YourCollectible: { address: - "0x7e0a62ddae222f9fe2b542ec71b2ae8e85bb44c138cd74695978f6d9999cb85", + "0x13c3ea96efff6befddacdfd84cb0b88eaaf7a69285e757cb757767588b76ce6", abi: [ { type: "impl", @@ -633,7 +633,7 @@ const deployedContracts = { }, ], classHash: - "0x3f6160b2e5a3d6a7105d0748e608f0c44a3166c6b9bd6390be82b6f3fcf68b4", + "0x22705c0b225ca32b8694b10a4893371470267aabe20876e1a02cac52088be75", }, }, sepolia: { diff --git a/packages/snfoundry/contracts/src/YourCollectible.cairo b/packages/snfoundry/contracts/src/YourCollectible.cairo index 9b36862a..9e8649a0 100644 --- a/packages/snfoundry/contracts/src/YourCollectible.cairo +++ b/packages/snfoundry/contracts/src/YourCollectible.cairo @@ -12,15 +12,8 @@ mod YourCollectible { use core::num::traits::zero::Zero; use openzeppelin::access::ownable::OwnableComponent; - use openzeppelin::account::interface::ISRC6_ID; - use openzeppelin::introspection::interface::{ISRC5, ISRC5_ID}; use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc721::interface::{ - IERC721_ID, IERC721_METADATA_ID, IERC721_RECEIVER_ID, - }; - use openzeppelin::token::erc721::{ - ERC721ReceiverComponent, ERC721Component, interface::{IERC721Metadata} - }; + use openzeppelin::token::erc721::{ERC721Component, interface::{IERC721Metadata}}; use super::{IYourCollectible, ContractAddress}; @@ -28,7 +21,6 @@ mod YourCollectible { component!(path: SRC5Component, storage: src5, event: SRC5Event); component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); component!(path: CounterComponent, storage: token_id_counter, event: CounterEvent); - component!(path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent); component!(path: ERC721EnumerableComponent, storage: enumerable, event: EnumerableEvent); #[abi(embed_v0)] @@ -43,7 +35,6 @@ mod YourCollectible { impl ERC721InternalImpl = ERC721Component::InternalImpl; impl OwnableInternalImpl = OwnableComponent::InternalImpl; - impl ERC721ReceiverInternalImpl = ERC721ReceiverComponent::InternalImpl; #[storage] @@ -51,8 +42,6 @@ mod YourCollectible { #[substorage(v0)] erc721: ERC721Component::Storage, #[substorage(v0)] - erc721_receiver: ERC721ReceiverComponent::Storage, - #[substorage(v0)] src5: SRC5Component::Storage, #[substorage(v0)] ownable: OwnableComponent::Storage, @@ -71,8 +60,6 @@ mod YourCollectible { #[flat] ERC721Event: ERC721Component::Event, #[flat] - ERC721ReceiverEvent: ERC721ReceiverComponent::Event, - #[flat] SRC5Event: SRC5Component::Event, #[flat] OwnableEvent: OwnableComponent::Event, @@ -88,7 +75,6 @@ mod YourCollectible { self.erc721.initializer(name, symbol, base_uri); self.ownable.initializer(owner); - self.erc721_receiver.initializer(); } #[abi(embed_v0)] @@ -96,6 +82,8 @@ mod YourCollectible { fn mint_item(ref self: ContractState, recipient: ContractAddress, uri: ByteArray) -> u256 { self.token_id_counter.increment(); let token_id = self.token_id_counter.current(); + // let data_sucess = array!['SUCCESS'].span(); // ERC721Receiver data + // self.erc721.safe_mint(recipient, token_id, data_sucess); self.erc721.mint(recipient, token_id); self.set_token_uri(token_id, uri); token_id @@ -138,7 +126,7 @@ mod YourCollectible { impl ERC721HooksEmptyImpl of ERC721Component::ERC721HooksTrait { // Implement this to add custom logic to the ERC721 hooks - // Similat to _beforeTokenTransfer in OpenZeppelin ERC721.sol + // Similar to _beforeTokenTransfer in OpenZeppelin ERC721.sol fn before_update( ref self: ERC721Component::ComponentState, to: ContractAddress, diff --git a/packages/snfoundry/contracts/src/mock_contracts/Receiver.cairo b/packages/snfoundry/contracts/src/mock_contracts/Receiver.cairo index 2027475a..3eb12ecf 100644 --- a/packages/snfoundry/contracts/src/mock_contracts/Receiver.cairo +++ b/packages/snfoundry/contracts/src/mock_contracts/Receiver.cairo @@ -4,9 +4,6 @@ pub mod Receiver { use openzeppelin::token::erc721::ERC721ReceiverComponent; use starknet::ContractAddress; - pub const SUCCESS: felt252 = 123123; - - component!(path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent); component!(path: SRC5Component, storage: src5, event: SRC5Event); @@ -51,7 +48,7 @@ pub mod Receiver { token_id: u256, data: Span ) -> felt252 { - if *data.at(0) == SUCCESS { + if *data.at(0) == 'SUCCESS' { self.erc721_receiver.on_erc721_received(operator, from, token_id, data) } else { 0 From bdd7d822d942595c1f664eeaa6da050d0a3d9cb9 Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Sun, 25 Aug 2024 22:23:28 +0700 Subject: [PATCH 13/18] Fix: hook Update --- .../nextjs/contracts/deployedContracts.ts | 26 ++----------------- .../contracts/src/YourCollectible.cairo | 1 + 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index b9bb8c86..eeb0dde7 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -7,7 +7,7 @@ const deployedContracts = { devnet: { YourCollectible: { address: - "0x13c3ea96efff6befddacdfd84cb0b88eaaf7a69285e757cb757767588b76ce6", + "0x418f946934444bc3281c8dc919d1d636e64cccb9489600dc26c61cc1a714145", abi: [ { type: "impl", @@ -519,12 +519,6 @@ const deployedContracts = { }, ], }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", - kind: "enum", - variants: [], - }, { type: "event", name: "openzeppelin::introspection::src5::SRC5Component::Event", @@ -604,11 +598,6 @@ const deployedContracts = { type: "openzeppelin::token::erc721::erc721::ERC721Component::Event", kind: "flat", }, - { - name: "ERC721ReceiverEvent", - type: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", - kind: "flat", - }, { name: "SRC5Event", type: "openzeppelin::introspection::src5::SRC5Component::Event", @@ -633,7 +622,7 @@ const deployedContracts = { }, ], classHash: - "0x22705c0b225ca32b8694b10a4893371470267aabe20876e1a02cac52088be75", + "0x527200e23a98acbca6116d4ce2366fe27199f78c146fefc921ec9a2c0d77dc", }, }, sepolia: { @@ -1151,12 +1140,6 @@ const deployedContracts = { }, ], }, - { - type: "event", - name: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", - kind: "enum", - variants: [], - }, { type: "event", name: "openzeppelin::introspection::src5::SRC5Component::Event", @@ -1236,11 +1219,6 @@ const deployedContracts = { type: "openzeppelin::token::erc721::erc721::ERC721Component::Event", kind: "flat", }, - { - name: "ERC721ReceiverEvent", - type: "openzeppelin::token::erc721::erc721_receiver::ERC721ReceiverComponent::Event", - kind: "flat", - }, { name: "SRC5Event", type: "openzeppelin::introspection::src5::SRC5Component::Event", diff --git a/packages/snfoundry/contracts/src/YourCollectible.cairo b/packages/snfoundry/contracts/src/YourCollectible.cairo index 9e8649a0..ec14a9c2 100644 --- a/packages/snfoundry/contracts/src/YourCollectible.cairo +++ b/packages/snfoundry/contracts/src/YourCollectible.cairo @@ -140,6 +140,7 @@ mod YourCollectible { let length = enumerable.all_tokens_length.read(); enumerable.all_tokens_index.write(token_id, length); enumerable.all_tokens.write(length, token_id); + enumerable.all_tokens_length.write(length + 1); } else if (token_id < token_id_counter + 1) { // `Transfer Token` Case: Remove token from owner and update enumerable component // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and From 76d9a13ff85ff0ce99b79478df9a21dd21b14949 Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Mon, 26 Aug 2024 13:34:14 +0700 Subject: [PATCH 14/18] Feat: Add custum WrappedIERC721MetadataCamelOnlyImpl --- .../nextjs/contracts/deployedContracts.ts | 398 +++++++++++++++++- packages/nextjs/scaffold.config.ts | 2 +- .../contracts/src/YourCollectible.cairo | 16 +- 3 files changed, 410 insertions(+), 6 deletions(-) diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index eeb0dde7..2f2e0f0e 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -7,7 +7,7 @@ const deployedContracts = { devnet: { YourCollectible: { address: - "0x418f946934444bc3281c8dc919d1d636e64cccb9489600dc26c61cc1a714145", + "0x7bf2dc3695e421c31e7acf3d1498c14688a5922df268da9afc5ba9bc8e1ebda", abi: [ { type: "impl", @@ -122,6 +122,34 @@ const deployedContracts = { }, ], }, + { + type: "impl", + name: "WrappedIERC721MetadataCamelOnlyImpl", + interface_name: + "openzeppelin::token::erc721::interface::IERC721MetadataCamelOnly", + }, + { + type: "interface", + name: "openzeppelin::token::erc721::interface::IERC721MetadataCamelOnly", + items: [ + { + type: "function", + name: "tokenURI", + inputs: [ + { + name: "tokenId", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + ], + }, { type: "impl", name: "OwnableImpl", @@ -378,6 +406,173 @@ const deployedContracts = { }, ], }, + { + type: "impl", + name: "ERC721CamelOnlyImpl", + interface_name: + "openzeppelin::token::erc721::interface::IERC721CamelOnly", + }, + { + type: "interface", + name: "openzeppelin::token::erc721::interface::IERC721CamelOnly", + items: [ + { + type: "function", + name: "balanceOf", + inputs: [ + { + name: "account", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "ownerOf", + inputs: [ + { + name: "tokenId", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "safeTransferFrom", + inputs: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "tokenId", + type: "core::integer::u256", + }, + { + name: "data", + type: "core::array::Span::", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "transferFrom", + inputs: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "tokenId", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "setApprovalForAll", + inputs: [ + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "approved", + type: "core::bool", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "getApproved", + inputs: [ + { + name: "tokenId", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "isApprovedForAll", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "SRC5Impl", + interface_name: "openzeppelin::introspection::interface::ISRC5", + }, + { + type: "interface", + name: "openzeppelin::introspection::interface::ISRC5", + items: [ + { + type: "function", + name: "supports_interface", + inputs: [ + { + name: "interface_id", + type: "core::felt252", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + ], + }, { type: "impl", name: "ERC721EnumerableImpl", @@ -622,13 +817,13 @@ const deployedContracts = { }, ], classHash: - "0x527200e23a98acbca6116d4ce2366fe27199f78c146fefc921ec9a2c0d77dc", + "0x2d7b083a934419eecfa89aabb3d052549a35fb6cba27323e4c54f2c7c558c9b", }, }, sepolia: { YourCollectible: { address: - "0x45305e8c94f02c8ade9c6df459a1c84e98bbbf2ba9d0ad7047652c310d9f5e8", + "0x36aa61b3b40c1071a64d923cec42a358143f70affdaca821ae5e584dc4b8505", abi: [ { type: "impl", @@ -743,6 +938,34 @@ const deployedContracts = { }, ], }, + { + type: "impl", + name: "WrappedIERC721MetadataCamelOnlyImpl", + interface_name: + "openzeppelin::token::erc721::interface::IERC721MetadataCamelOnly", + }, + { + type: "interface", + name: "openzeppelin::token::erc721::interface::IERC721MetadataCamelOnly", + items: [ + { + type: "function", + name: "tokenURI", + inputs: [ + { + name: "tokenId", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::byte_array::ByteArray", + }, + ], + state_mutability: "view", + }, + ], + }, { type: "impl", name: "OwnableImpl", @@ -999,6 +1222,173 @@ const deployedContracts = { }, ], }, + { + type: "impl", + name: "ERC721CamelOnlyImpl", + interface_name: + "openzeppelin::token::erc721::interface::IERC721CamelOnly", + }, + { + type: "interface", + name: "openzeppelin::token::erc721::interface::IERC721CamelOnly", + items: [ + { + type: "function", + name: "balanceOf", + inputs: [ + { + name: "account", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::integer::u256", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "ownerOf", + inputs: [ + { + name: "tokenId", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "safeTransferFrom", + inputs: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "tokenId", + type: "core::integer::u256", + }, + { + name: "data", + type: "core::array::Span::", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "transferFrom", + inputs: [ + { + name: "from", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "to", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "tokenId", + type: "core::integer::u256", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "setApprovalForAll", + inputs: [ + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "approved", + type: "core::bool", + }, + ], + outputs: [], + state_mutability: "external", + }, + { + type: "function", + name: "getApproved", + inputs: [ + { + name: "tokenId", + type: "core::integer::u256", + }, + ], + outputs: [ + { + type: "core::starknet::contract_address::ContractAddress", + }, + ], + state_mutability: "view", + }, + { + type: "function", + name: "isApprovedForAll", + inputs: [ + { + name: "owner", + type: "core::starknet::contract_address::ContractAddress", + }, + { + name: "operator", + type: "core::starknet::contract_address::ContractAddress", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + ], + }, + { + type: "impl", + name: "SRC5Impl", + interface_name: "openzeppelin::introspection::interface::ISRC5", + }, + { + type: "interface", + name: "openzeppelin::introspection::interface::ISRC5", + items: [ + { + type: "function", + name: "supports_interface", + inputs: [ + { + name: "interface_id", + type: "core::felt252", + }, + ], + outputs: [ + { + type: "core::bool", + }, + ], + state_mutability: "view", + }, + ], + }, { type: "impl", name: "ERC721EnumerableImpl", @@ -1243,7 +1633,7 @@ const deployedContracts = { }, ], classHash: - "0x604a5408288ed85d4220cfa7bddf6db718feec9f79455e7391ea81c063023f5", + "0x2d7b083a934419eecfa89aabb3d052549a35fb6cba27323e4c54f2c7c558c9b", }, }, } as const; diff --git a/packages/nextjs/scaffold.config.ts b/packages/nextjs/scaffold.config.ts index ee79a8fc..fac39e12 100644 --- a/packages/nextjs/scaffold.config.ts +++ b/packages/nextjs/scaffold.config.ts @@ -9,7 +9,7 @@ export type ScaffoldConfig = { }; const scaffoldConfig = { - targetNetworks: [chains.devnet], + targetNetworks: [chains.sepolia], // Only show the Burner Wallet when running on devnet onlyLocalBurnerWallet: false, rpcProviderUrl: process.env.NEXT_PUBLIC_PROVIDER_URL || "", diff --git a/packages/snfoundry/contracts/src/YourCollectible.cairo b/packages/snfoundry/contracts/src/YourCollectible.cairo index ec14a9c2..17dadeb0 100644 --- a/packages/snfoundry/contracts/src/YourCollectible.cairo +++ b/packages/snfoundry/contracts/src/YourCollectible.cairo @@ -13,7 +13,9 @@ mod YourCollectible { use openzeppelin::access::ownable::OwnableComponent; use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc721::{ERC721Component, interface::{IERC721Metadata}}; + use openzeppelin::token::erc721::{ + ERC721Component, interface::{IERC721Metadata, IERC721MetadataCamelOnly} + }; use super::{IYourCollectible, ContractAddress}; @@ -30,6 +32,10 @@ mod YourCollectible { #[abi(embed_v0)] impl ERC721Impl = ERC721Component::ERC721Impl; #[abi(embed_v0)] + impl ERC721CamelOnlyImpl = ERC721Component::ERC721CamelOnlyImpl; + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + #[abi(embed_v0)] impl ERC721EnumerableImpl = ERC721EnumerableComponent::ERC721EnumerableImpl; @@ -104,6 +110,14 @@ mod YourCollectible { } } + #[abi(embed_v0)] + impl WrappedIERC721MetadataCamelOnlyImpl of IERC721MetadataCamelOnly { + // Override tokenURI to use the internal ERC721URIStorage _token_uri function + fn tokenURI(self: @ContractState, tokenId: u256) -> ByteArray { + self._token_uri(tokenId) + } + } + #[generate_trait] impl InternalImpl of InternalTrait { // token_uri custom implementation From aeccab48008b06d643297b3ce9a0a56f186288fc Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Mon, 26 Aug 2024 16:21:10 +0700 Subject: [PATCH 15/18] Feat: Improve IERC721Enumerable --- .../src/components/ERC721Enumerable.cairo | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo b/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo index 4f7f6833..8a8d39bd 100644 --- a/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo +++ b/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo @@ -9,6 +9,10 @@ pub trait IERC721Enumerable { #[starknet::component] pub mod ERC721EnumerableComponent { use super::{IERC721Enumerable, ContractAddress}; + use openzeppelin::token::erc721::ERC721Component; + use openzeppelin::token::erc721::interface::IERC721; + use core::num::traits::zero::Zero; + #[storage] struct Storage { @@ -26,13 +30,20 @@ pub mod ERC721EnumerableComponent { #[embeddable_as(ERC721EnumerableImpl)] impl ERC721Enumerable< - TContractState, +HasComponent + TContractState, + +HasComponent, + impl ERC721: ERC721Component::HasComponent, + +ERC721Component::ERC721HooksTrait, + +Drop > of IERC721Enumerable> { fn token_of_owner_by_index( self: @ComponentState, owner: ContractAddress, index: u256 ) -> u256 { - // TODO: Add this check back in - //assert(index < self.erc721.balance_of(owner), 'Owner index out of bounds'); + let mut erc721_component = get_dep_component!(self, ERC721); + // ToDo: Improve to use erc721_component.balance_of() + assert(!owner.is_zero(), 'INVALID_ACCOUNT'); + let balance = erc721_component.ERC721_balances.read(owner); + assert(index < balance, 'Owner index out of bounds'); self.owned_tokens.read((owner, index)) } fn total_supply(self: @ComponentState) -> u256 { From ae25610551ae78f6fe6618abbbd681a8d438e8a8 Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Mon, 26 Aug 2024 16:52:10 +0700 Subject: [PATCH 16/18] Feat: Update ERC721Enumerable component --- packages/nextjs/contracts/deployedContracts.ts | 4 ++-- packages/nextjs/scaffold.config.ts | 2 +- packages/snfoundry/contracts/src/YourCollectible.cairo | 2 +- .../contracts/src/components/ERC721Enumerable.cairo | 10 ++++------ 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index 2f2e0f0e..f2498d3b 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -7,7 +7,7 @@ const deployedContracts = { devnet: { YourCollectible: { address: - "0x7bf2dc3695e421c31e7acf3d1498c14688a5922df268da9afc5ba9bc8e1ebda", + "0x6089cf0e1f1537f4ba7ed7f38bd45f454a78ba933cd91e8ca326aeec8187771", abi: [ { type: "impl", @@ -817,7 +817,7 @@ const deployedContracts = { }, ], classHash: - "0x2d7b083a934419eecfa89aabb3d052549a35fb6cba27323e4c54f2c7c558c9b", + "0x5d7a0a9e879cb1fd5c163704ecc6cda835adc46274f80b3178e428510f435e1", }, }, sepolia: { diff --git a/packages/nextjs/scaffold.config.ts b/packages/nextjs/scaffold.config.ts index fac39e12..ee79a8fc 100644 --- a/packages/nextjs/scaffold.config.ts +++ b/packages/nextjs/scaffold.config.ts @@ -9,7 +9,7 @@ export type ScaffoldConfig = { }; const scaffoldConfig = { - targetNetworks: [chains.sepolia], + targetNetworks: [chains.devnet], // Only show the Burner Wallet when running on devnet onlyLocalBurnerWallet: false, rpcProviderUrl: process.env.NEXT_PUBLIC_PROVIDER_URL || "", diff --git a/packages/snfoundry/contracts/src/YourCollectible.cairo b/packages/snfoundry/contracts/src/YourCollectible.cairo index 17dadeb0..e303867d 100644 --- a/packages/snfoundry/contracts/src/YourCollectible.cairo +++ b/packages/snfoundry/contracts/src/YourCollectible.cairo @@ -138,7 +138,7 @@ mod YourCollectible { } } - impl ERC721HooksEmptyImpl of ERC721Component::ERC721HooksTrait { + impl ERC721EnumerableHooksImpl of ERC721Component::ERC721HooksTrait { // Implement this to add custom logic to the ERC721 hooks // Similar to _beforeTokenTransfer in OpenZeppelin ERC721.sol fn before_update( diff --git a/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo b/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo index 8a8d39bd..0b15f687 100644 --- a/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo +++ b/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo @@ -11,8 +11,7 @@ pub mod ERC721EnumerableComponent { use super::{IERC721Enumerable, ContractAddress}; use openzeppelin::token::erc721::ERC721Component; use openzeppelin::token::erc721::interface::IERC721; - use core::num::traits::zero::Zero; - + use openzeppelin::introspection::src5::SRC5Component; #[storage] struct Storage { @@ -33,16 +32,15 @@ pub mod ERC721EnumerableComponent { TContractState, +HasComponent, impl ERC721: ERC721Component::HasComponent, + +SRC5Component::HasComponent, +ERC721Component::ERC721HooksTrait, +Drop > of IERC721Enumerable> { fn token_of_owner_by_index( self: @ComponentState, owner: ContractAddress, index: u256 ) -> u256 { - let mut erc721_component = get_dep_component!(self, ERC721); - // ToDo: Improve to use erc721_component.balance_of() - assert(!owner.is_zero(), 'INVALID_ACCOUNT'); - let balance = erc721_component.ERC721_balances.read(owner); + let erc721_component = get_dep_component!(self, ERC721); + let balance = erc721_component.balance_of(owner); assert(index < balance, 'Owner index out of bounds'); self.owned_tokens.read((owner, index)) } From 6271a7ebbda79e68de7dcdf73a76dc4f33f00a0b Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Mon, 26 Aug 2024 17:36:28 +0700 Subject: [PATCH 17/18] Update contract_hooks to work on enumerable --- .../nextjs/contracts/deployedContracts.ts | 4 +- .../contracts/src/YourCollectible.cairo | 61 +++++++++++-------- .../src/components/ERC721Enumerable.cairo | 4 +- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index f2498d3b..95ad57c5 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -7,7 +7,7 @@ const deployedContracts = { devnet: { YourCollectible: { address: - "0x6089cf0e1f1537f4ba7ed7f38bd45f454a78ba933cd91e8ca326aeec8187771", + "0x53ecac0907f95725ebf6e1f643b9b592df89f6f8ea6058419c300b88b19d2f0", abi: [ { type: "impl", @@ -817,7 +817,7 @@ const deployedContracts = { }, ], classHash: - "0x5d7a0a9e879cb1fd5c163704ecc6cda835adc46274f80b3178e428510f435e1", + "0xe625f63df410d00e04cb843348ce325d331b62d2436850989790bae6383944", }, }, sepolia: { diff --git a/packages/snfoundry/contracts/src/YourCollectible.cairo b/packages/snfoundry/contracts/src/YourCollectible.cairo index e303867d..a0723230 100644 --- a/packages/snfoundry/contracts/src/YourCollectible.cairo +++ b/packages/snfoundry/contracts/src/YourCollectible.cairo @@ -138,65 +138,74 @@ mod YourCollectible { } } - impl ERC721EnumerableHooksImpl of ERC721Component::ERC721HooksTrait { + impl ERC721EnumerableHooksImpl< + T, + impl ERC721Enumerable: ERC721EnumerableComponent::HasComponent, + impl Counter: CounterComponent::HasComponent, + impl HasComponent: ERC721Component::HasComponent, + +SRC5Component::HasComponent, + +Drop + > of ERC721Component::ERC721HooksTrait { // Implement this to add custom logic to the ERC721 hooks // Similar to _beforeTokenTransfer in OpenZeppelin ERC721.sol fn before_update( - ref self: ERC721Component::ComponentState, + ref self: ERC721Component::ComponentState, to: ContractAddress, token_id: u256, auth: ContractAddress ) { - let mut contract_state = ERC721Component::HasComponent::get_contract_mut(ref self); - let token_id_counter = contract_state.token_id_counter.current(); - let mut enumerable = contract_state.enumerable; + let mut counter_component = get_dep_component_mut!(ref self, Counter); + let token_id_counter = counter_component.current(); + let mut enumerable_component = get_dep_component_mut!(ref self, ERC721Enumerable); if (token_id == token_id_counter) { // `Mint Token` case: Add token to `all_tokens` enumerable component - let length = enumerable.all_tokens_length.read(); - enumerable.all_tokens_index.write(token_id, length); - enumerable.all_tokens.write(length, token_id); - enumerable.all_tokens_length.write(length + 1); + let length = enumerable_component.all_tokens_length.read(); + enumerable_component.all_tokens_index.write(token_id, length); + enumerable_component.all_tokens.write(length, token_id); + enumerable_component.all_tokens_length.write(length + 1); } else if (token_id < token_id_counter + 1) { // `Transfer Token` Case: Remove token from owner and update enumerable component // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and // then delete the last slot (swap and pop). let owner = self.owner_of(token_id); let last_token_index = self.balance_of(owner) - 1; - let token_index = enumerable.owned_tokens_index.read(token_id); + let token_index = enumerable_component.owned_tokens_index.read(token_id); // When the token to delete is the last token, the swap operation is unnecessary if (token_index != last_token_index) { - let last_token_id = enumerable.owned_tokens.read((owner, last_token_index)); + let last_token_id = enumerable_component + .owned_tokens + .read((owner, last_token_index)); // Move the last token to the slot of the to-delete token - enumerable.owned_tokens.write((owner, token_index), last_token_id); + enumerable_component.owned_tokens.write((owner, token_index), last_token_id); // Update the moved token's index - enumerable.owned_tokens_index.write(last_token_id, token_index); + enumerable_component.owned_tokens_index.write(last_token_id, token_index); } // Clear the last slot - enumerable.owned_tokens.write((owner, last_token_index), 0); - enumerable.owned_tokens_index.write(token_id, 0); + enumerable_component.owned_tokens.write((owner, last_token_index), 0); + enumerable_component.owned_tokens_index.write(token_id, 0); } if (to == Zero::zero()) { // `Burn Token` case: Remove token from `all_tokens` enumerable component - let last_token_index = enumerable.all_tokens_length.read() - 1; - let token_index = enumerable.all_tokens_index.read(token_id); + let last_token_index = enumerable_component.all_tokens_length.read() - 1; + let token_index = enumerable_component.all_tokens_index.read(token_id); - let last_token_id = enumerable.all_tokens.read(last_token_index); + let last_token_id = enumerable_component.all_tokens.read(last_token_index); - enumerable.all_tokens.write(token_index, last_token_id); - enumerable.all_tokens_index.write(last_token_id, token_index); + enumerable_component.all_tokens.write(token_index, last_token_id); + enumerable_component.all_tokens_index.write(last_token_id, token_index); - enumerable.all_tokens_index.write(token_id, 0); - enumerable.all_tokens.write(last_token_index, 0); - enumerable.all_tokens_length.write(last_token_index); + enumerable_component.all_tokens_index.write(token_id, 0); + enumerable_component.all_tokens.write(last_token_index, 0); + enumerable_component.all_tokens_length.write(last_token_index); } else if (to != auth) { // `Mint Token` and `Transfer Token` case: Add token owner to `owned_tokens` enumerable component let length = self.balance_of(to); - enumerable.owned_tokens.write((to, length), token_id); - enumerable.owned_tokens_index.write(token_id, length); + enumerable_component.owned_tokens.write((to, length), token_id); + enumerable_component.owned_tokens_index.write(token_id, length); } } fn after_update( - ref self: ERC721Component::ComponentState, + ref self: ERC721Component::ComponentState, to: ContractAddress, token_id: u256, auth: ContractAddress diff --git a/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo b/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo index 0b15f687..33bac181 100644 --- a/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo +++ b/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo @@ -8,10 +8,10 @@ pub trait IERC721Enumerable { #[starknet::component] pub mod ERC721EnumerableComponent { - use super::{IERC721Enumerable, ContractAddress}; + use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::token::erc721::ERC721Component; use openzeppelin::token::erc721::interface::IERC721; - use openzeppelin::introspection::src5::SRC5Component; + use super::{IERC721Enumerable, ContractAddress}; #[storage] struct Storage { From eee45b45065126f0e6f9c007c30b131ce8b0e080 Mon Sep 17 00:00:00 2001 From: gianmalarcon Date: Mon, 26 Aug 2024 21:59:15 +0700 Subject: [PATCH 18/18] Improve hooks --- packages/snfoundry/contracts/src/YourCollectible.cairo | 4 +++- packages/snfoundry/contracts/src/components/Counter.cairo | 8 ++++---- .../contracts/src/components/ERC721Enumerable.cairo | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/snfoundry/contracts/src/YourCollectible.cairo b/packages/snfoundry/contracts/src/YourCollectible.cairo index a0723230..6687f3e9 100644 --- a/packages/snfoundry/contracts/src/YourCollectible.cairo +++ b/packages/snfoundry/contracts/src/YourCollectible.cairo @@ -25,6 +25,7 @@ mod YourCollectible { component!(path: CounterComponent, storage: token_id_counter, event: CounterEvent); component!(path: ERC721EnumerableComponent, storage: enumerable, event: EnumerableEvent); + // Expose entrypoints #[abi(embed_v0)] impl OwnableImpl = OwnableComponent::OwnableImpl; #[abi(embed_v0)] @@ -39,6 +40,7 @@ mod YourCollectible { impl ERC721EnumerableImpl = ERC721EnumerableComponent::ERC721EnumerableImpl; + // Use internal implementations but do not expose them impl ERC721InternalImpl = ERC721Component::InternalImpl; impl OwnableInternalImpl = OwnableComponent::InternalImpl; @@ -154,7 +156,7 @@ mod YourCollectible { token_id: u256, auth: ContractAddress ) { - let mut counter_component = get_dep_component_mut!(ref self, Counter); + let counter_component = get_dep_component!(@self, Counter); let token_id_counter = counter_component.current(); let mut enumerable_component = get_dep_component_mut!(ref self, ERC721Enumerable); if (token_id == token_id_counter) { // `Mint Token` case: Add token to `all_tokens` enumerable component diff --git a/packages/snfoundry/contracts/src/components/Counter.cairo b/packages/snfoundry/contracts/src/components/Counter.cairo index 522a9e36..1a7b5ff5 100644 --- a/packages/snfoundry/contracts/src/components/Counter.cairo +++ b/packages/snfoundry/contracts/src/components/Counter.cairo @@ -1,8 +1,8 @@ #[starknet::interface] -pub trait ICounter { - fn current(self: @T) -> u256; - fn increment(ref self: T); - fn decrement(ref self: T); +pub trait ICounter { + fn current(self: @TState) -> u256; + fn increment(ref self: TState); + fn decrement(ref self: TState); } #[starknet::component] diff --git a/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo b/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo index 33bac181..a6f02b47 100644 --- a/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo +++ b/packages/snfoundry/contracts/src/components/ERC721Enumerable.cairo @@ -1,9 +1,9 @@ use starknet::ContractAddress; #[starknet::interface] -pub trait IERC721Enumerable { - fn token_of_owner_by_index(self: @T, owner: ContractAddress, index: u256) -> u256; - fn total_supply(self: @T) -> u256; +pub trait IERC721Enumerable { + fn token_of_owner_by_index(self: @TState, owner: ContractAddress, index: u256) -> u256; + fn total_supply(self: @TState) -> u256; } #[starknet::component]