From 326c84f8ea16876760aaa1916c39569c965859ef Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Mon, 9 Sep 2024 21:42:06 +0100 Subject: [PATCH 01/14] feat: migrate repo to starknet 2.6.4 --- .tool-versions | 1 + Scarb.toml | 7 +- src/extensions/metadata/module.cairo | 6 +- src/extensions/slotapprovable/module.cairo | 18 +- src/lib.cairo | 1 - src/module.cairo | 21 +- src/presets/erc3525_mintable_burnable.cairo | 3 +- .../erc3525_mintable_burnable_metadata.cairo | 6 +- ...le_burnable_metadata_slot_approvable.cairo | 10 +- ...data_slot_approvable_slot_enumerable.cairo | 7 +- src/tests/integration/constants.cairo | 57 --- src/tests/integration/test_base.cairo | 139 -------- src/tests/integration/test_metadata.cairo | 105 ------ src/tests/integration/test_receiver.cairo | 83 ----- .../integration/test_slot_approvable.cairo | 99 ------ .../integration/test_slot_enumerable.cairo | 170 --------- src/tests/mocks/contracts.cairo | 331 ------------------ src/tests/mocks/receiver.cairo | 81 ----- src/tests/unit/constants.cairo | 110 ------ src/tests/unit/test_approvals.cairo | 155 -------- src/tests/unit/test_initialization.cairo | 16 - src/tests/unit/test_metadata.cairo | 60 ---- src/tests/unit/test_mint_burn.cairo | 230 ------------ src/tests/unit/test_slot_approvable.cairo | 119 ------- src/tests/unit/test_slot_enumerable.cairo | 234 ------------- src/tests/unit/test_transfer_to_address.cairo | 187 ---------- src/tests/unit/test_transfer_to_token.cairo | 181 ---------- src/tests/unit/test_views.cairo | 87 ----- src/tests/utils.cairo | 69 ---- 29 files changed, 36 insertions(+), 2557 deletions(-) create mode 100644 .tool-versions delete mode 100644 src/tests/integration/constants.cairo delete mode 100644 src/tests/integration/test_base.cairo delete mode 100644 src/tests/integration/test_metadata.cairo delete mode 100644 src/tests/integration/test_receiver.cairo delete mode 100644 src/tests/integration/test_slot_approvable.cairo delete mode 100644 src/tests/integration/test_slot_enumerable.cairo delete mode 100644 src/tests/mocks/contracts.cairo delete mode 100644 src/tests/mocks/receiver.cairo delete mode 100644 src/tests/unit/constants.cairo delete mode 100644 src/tests/unit/test_approvals.cairo delete mode 100644 src/tests/unit/test_initialization.cairo delete mode 100644 src/tests/unit/test_metadata.cairo delete mode 100644 src/tests/unit/test_mint_burn.cairo delete mode 100644 src/tests/unit/test_slot_approvable.cairo delete mode 100644 src/tests/unit/test_slot_enumerable.cairo delete mode 100644 src/tests/unit/test_transfer_to_address.cairo delete mode 100644 src/tests/unit/test_transfer_to_token.cairo delete mode 100644 src/tests/unit/test_views.cairo delete mode 100644 src/tests/utils.cairo diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..179f2a8 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +scarb 2.6.5 diff --git a/Scarb.toml b/Scarb.toml index 32163e4..d516ea8 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -7,13 +7,14 @@ cairo-version = "2.6.3" [lib] [dependencies] -starknet = ">=2.6.3" -openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.10.0" } -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.19.0" } +starknet = "2.6.4" +openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.14.0" } +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.27.0" } [[target.starknet-contract]] sierra = true casm = true +build-external-contracts = ["openzeppelin::presets::AccountUpgradeable"] [tool.snforge] exit_first = false \ No newline at end of file diff --git a/src/extensions/metadata/module.cairo b/src/extensions/metadata/module.cairo index 2de5d13..e515d01 100644 --- a/src/extensions/metadata/module.cairo +++ b/src/extensions/metadata/module.cairo @@ -4,9 +4,9 @@ mod ERC3525MetadataComponent { use starknet::ContractAddress; // External deps - use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; - use openzeppelin::introspection::src5::SRC5Component::{SRC5, SRC5Camel}; - use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::introspection::src5::{ + SRC5Component, SRC5Component::InternalTrait as SRC5InternalTrait + }; // Local deps use cairo_erc_3525::extensions::metadata::interface::{ diff --git a/src/extensions/slotapprovable/module.cairo b/src/extensions/slotapprovable/module.cairo index 508fe78..3e11d34 100644 --- a/src/extensions/slotapprovable/module.cairo +++ b/src/extensions/slotapprovable/module.cairo @@ -1,6 +1,7 @@ #[starknet::component] mod ERC3525SlotApprovableComponent { // Core deps + use openzeppelin::token::erc721::interface::IERC721; use traits::{Into, TryInto}; use option::OptionTrait; use zeroable::Zeroable; @@ -10,13 +11,12 @@ mod ERC3525SlotApprovableComponent { use starknet::{get_caller_address, ContractAddress}; // External deps - use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; - use openzeppelin::introspection::src5::SRC5Component::{SRC5, SRC5Camel}; - use openzeppelin::introspection::src5::SRC5Component; - - use openzeppelin::token::erc721::erc721::ERC721Component::InternalTrait as ERC721InternalTrait; - use openzeppelin::token::erc721::erc721::ERC721Component::ERC721; - use openzeppelin::token::erc721::erc721::ERC721Component; + use openzeppelin::introspection::src5::{ + SRC5Component, SRC5Component::InternalTrait as SRC5InternalTrait + }; + use openzeppelin::token::erc721::{ + ERC721Component, ERC721HooksEmptyImpl, ERC721Component::InternalTrait as ERC721InternalTrait + }; // Local deps use cairo_erc_3525::module::ERC3525Component::InternalTrait as ERC3525InternalTrait; @@ -143,7 +143,7 @@ mod ERC3525SlotApprovableComponent { // [Effect] Store approval let mut erc721_comp = get_dep_component_mut!(ref self, ERC721Comp); - erc721_comp._approve(to, token_id); + erc721_comp.approve(to, token_id); } fn approve_value( @@ -249,7 +249,7 @@ mod ERC3525SlotApprovableComponent { // [Compute] Operator is owner or approved for all let erc721_comp = get_dep_component!(self, ERC721Comp); let owner = erc721_comp.owner_of(token_id); - let is_owner_or_approved = erc721_comp._is_approved_or_owner(operator, token_id); + let is_owner_or_approved = erc721_comp.get_approved(token_id) == operator; // [Compute] Operator is approved for slot let erc3525_comp = get_dep_component!(self, ERC3525Comp); diff --git a/src/lib.cairo b/src/lib.cairo index ecb24cb..3bc67d1 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -22,4 +22,3 @@ mod presets { mod erc3525_mintable_burnable_metadata_slot_approvable_slot_enumerable; } -mod tests; diff --git a/src/module.cairo b/src/module.cairo index 6dec880..2f15659 100644 --- a/src/module.cairo +++ b/src/module.cairo @@ -1,6 +1,7 @@ #[starknet::component] mod ERC3525Component { // Core deps + use openzeppelin::token::erc721::interface::IERC721; use array::{ArrayTrait, SpanTrait}; use option::OptionTrait; use traits::{Into, TryInto}; @@ -12,12 +13,12 @@ mod ERC3525Component { // External deps use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; - use openzeppelin::introspection::src5::SRC5Component::{SRC5, SRC5Camel}; + // use openzeppelin::introspection::src5::SRC5Component::{SRC5, SRC5Camel}; use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait}; - use openzeppelin::token::erc721::erc721::ERC721Component::InternalTrait as ERC721InternalTrait; - use openzeppelin::token::erc721::erc721::ERC721Component::ERC721; - use openzeppelin::token::erc721::erc721::ERC721Component; + use openzeppelin::token::erc721::{ + ERC721Component, ERC721HooksEmptyImpl, ERC721Component::InternalTrait as ERC721InternalTrait + }; use openzeppelin::account::interface::ISRC6_ID; // Local deps @@ -131,7 +132,7 @@ mod ERC3525Component { let erc721_comp = get_dep_component!(@self, ERC721); let owner = erc721_comp.owner_of(token_id); assert(owner != operator, Errors::APPROVAL_TO_OWNER); - assert(erc721_comp._is_approved_or_owner(caller, token_id), Errors::CALLER_NOT_ALLOWED); + assert(erc721_comp.get_approved(token_id) == caller, Errors::CALLER_NOT_ALLOWED); // [Effect] Store approved value self._approve_value(token_id, operator, value); @@ -296,7 +297,7 @@ mod ERC3525Component { let owner = erc721_comp.owner_of(token_id); let current_allowance = self._erc3525_approved_values.read((owner, token_id, spender)); let infinity: u256 = BoundedInt::max(); - let is_approved = erc721_comp._is_approved_or_owner(spender, token_id); + let is_approved = erc721_comp.get_approved(token_id) == spender; // [Effect] Update allowance if the rights are limited if current_allowance == infinity || is_approved { @@ -344,7 +345,7 @@ mod ERC3525Component { ) { // [Effect] Mint a new enumerable token if supported, standard token otherwise let mut erc721_comp = get_dep_component_mut!(ref self, ERC721); - erc721_comp._mint(to, token_id); + erc721_comp.mint(to, token_id); // [Effect] Store slot self._erc3525_slots.write(token_id, slot); @@ -417,7 +418,7 @@ mod ERC3525Component { // [Effect] Burn token let mut erc721_comp = get_dep_component_mut!(ref self, ERC721); - erc721_comp._burn(token_id); + erc721_comp.burn(token_id); // [Effect] Update token and total value let value = self._erc3525_values.read(token_id); @@ -566,13 +567,13 @@ mod ERC3525Component { > of AssertTrait { fn _assert_minted(self: @ComponentState, token_id: u256) { let erc721_comp = get_dep_component!(self, ERC721); - let exists = erc721_comp._exists(token_id); + let exists = erc721_comp.exists(token_id); assert(exists, Errors::TOKEN_NOT_MINTED); } fn _assert_not_minted(self: @ComponentState, token_id: u256) { let erc721_comp = get_dep_component!(self, ERC721); - let exists = erc721_comp._exists(token_id); + let exists = erc721_comp.exists(token_id); assert(!exists, Errors::TOKEN_ALREADY_MINTED); } } diff --git a/src/presets/erc3525_mintable_burnable.cairo b/src/presets/erc3525_mintable_burnable.cairo index 68eba03..d2be5cd 100644 --- a/src/presets/erc3525_mintable_burnable.cairo +++ b/src/presets/erc3525_mintable_burnable.cairo @@ -15,11 +15,10 @@ mod ERC3525MintableBurnable { use starknet::{get_caller_address, ContractAddress}; // SRC5 - use openzeppelin::introspection::interface::{ISRC5, ISRC5Camel}; use openzeppelin::introspection::src5::SRC5Component; // ERC721 - use openzeppelin::token::erc721::erc721::ERC721Component; + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; // ERC3525 use cairo_erc_3525::module::ERC3525Component; diff --git a/src/presets/erc3525_mintable_burnable_metadata.cairo b/src/presets/erc3525_mintable_burnable_metadata.cairo index 0fc1b4c..ba481e4 100644 --- a/src/presets/erc3525_mintable_burnable_metadata.cairo +++ b/src/presets/erc3525_mintable_burnable_metadata.cairo @@ -18,13 +18,11 @@ mod ERC3525MintableBurnableMetadata { use starknet::{get_caller_address, ContractAddress}; // SRC5 - use openzeppelin::introspection::interface::{ISRC5, ISRC5Camel}; use openzeppelin::introspection::src5::SRC5Component; // ERC721 - use openzeppelin::token::erc721::erc721::ERC721Component; - use openzeppelin::token::erc721::interface::{ - IERC721, IERC721CamelOnly, IERC721Metadata, IERC721MetadataCamelOnly + use openzeppelin::token::erc721::{ + ERC721Component, ERC721HooksEmptyImpl, interface::{IERC721, IERC721CamelOnly} }; // ERC3525 diff --git a/src/presets/erc3525_mintable_burnable_metadata_slot_approvable.cairo b/src/presets/erc3525_mintable_burnable_metadata_slot_approvable.cairo index fb5d16c..769ca0f 100644 --- a/src/presets/erc3525_mintable_burnable_metadata_slot_approvable.cairo +++ b/src/presets/erc3525_mintable_burnable_metadata_slot_approvable.cairo @@ -18,14 +18,10 @@ mod ERC3525MintableBurnableMSA { use starknet::{get_caller_address, ContractAddress}; // SRC5 - use openzeppelin::introspection::interface::{ISRC5, ISRC5Camel}; use openzeppelin::introspection::src5::SRC5Component; // ERC721 - use openzeppelin::token::erc721::erc721::ERC721Component; - use openzeppelin::token::erc721::interface::{ - IERC721, IERC721CamelOnly, IERC721Metadata, IERC721MetadataCamelOnly - }; + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl, interface::{IERC721, IERC721CamelOnly}}; // ERC3525 use cairo_erc_3525::module::ERC3525Component; @@ -59,8 +55,8 @@ mod ERC3525MintableBurnableMSA { // Component implementations #[abi(embed_v0)] impl SRC5Impl = SRC5Component::SRC5Impl; - #[abi(embed_v0)] - impl SRC5CamelImpl = SRC5Component::SRC5CamelImpl; + // #[abi(embed_v0)] + // impl SRC5CamelImpl = SRC5Component::SRC5CamelImpl; #[abi(embed_v0)] impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; #[abi(embed_v0)] diff --git a/src/presets/erc3525_mintable_burnable_metadata_slot_approvable_slot_enumerable.cairo b/src/presets/erc3525_mintable_burnable_metadata_slot_approvable_slot_enumerable.cairo index 6abf52d..554ea86 100644 --- a/src/presets/erc3525_mintable_burnable_metadata_slot_approvable_slot_enumerable.cairo +++ b/src/presets/erc3525_mintable_burnable_metadata_slot_approvable_slot_enumerable.cairo @@ -18,14 +18,11 @@ mod ERC3525MintableBurnableMSASE { use starknet::{get_caller_address, ContractAddress}; // SRC5 - use openzeppelin::introspection::interface::{ISRC5, ISRC5Camel}; + // use openzeppelin::introspection::interface::{ISRC5, ISRC5Camel}; use openzeppelin::introspection::src5::SRC5Component; // ERC721 - use openzeppelin::token::erc721::erc721::ERC721Component; - use openzeppelin::token::erc721::interface::{ - IERC721, IERC721CamelOnly, IERC721Metadata, IERC721MetadataCamelOnly - }; + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; // ERC3525 use cairo_erc_3525::module::ERC3525Component; diff --git a/src/tests/integration/constants.cairo b/src/tests/integration/constants.cairo deleted file mode 100644 index 09499e5..0000000 --- a/src/tests/integration/constants.cairo +++ /dev/null @@ -1,57 +0,0 @@ -use starknet::ContractAddress; -use starknet::contract_address_const; -use zeroable::Zeroable; - -// Constants - -const VALUE_DECIMALS: u8 = 6; -const TOKEN_ID_1: u256 = 1; -const TOKEN_ID_2: u256 = 2; -const INVALID_TOKEN: u256 = 666; -const SLOT_1: u256 = 'SLOT1'; -const SLOT_2: u256 = 'SLOT2'; -const VALUE: u256 = 1000; -const CONTRACT_URI: felt252 = 'CONTRACT_URI'; -const SLOT_URI: felt252 = 'SLOT_URI'; - -// Addresses - -fn ZERO() -> ContractAddress { - Zeroable::zero() -} - -fn OWNER() -> ContractAddress { - contract_address_const::<'OWNER'>() -} - -fn RECIPIENT() -> ContractAddress { - contract_address_const::<'RECIPIENT'>() -} - -fn SPENDER() -> ContractAddress { - contract_address_const::<'SPENDER'>() -} - -fn OPERATOR() -> ContractAddress { - contract_address_const::<'OPERATOR'>() -} - -fn SOMEONE() -> ContractAddress { - contract_address_const::<'SOMEONE'>() -} - -fn ANYONE() -> ContractAddress { - contract_address_const::<'ANYONE'>() -} - -fn NAME() -> ByteArray { - "NAME" -} - -fn SYMBOL() -> ByteArray { - "SYMBOL" -} - -fn BASE_URI() -> ByteArray { - "BASE_URI" -} diff --git a/src/tests/integration/test_base.cairo b/src/tests/integration/test_base.cairo deleted file mode 100644 index 0dc1b95..0000000 --- a/src/tests/integration/test_base.cairo +++ /dev/null @@ -1,139 +0,0 @@ -// Core deps -use result::ResultTrait; -use option::OptionTrait; -use array::ArrayTrait; -use traits::{Into, TryInto}; - -// Starknet deps -use starknet::{ContractAddress}; -use starknet::testing; - -// External deps -use openzeppelin::introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}; -use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait, IERC721_ID}; -use openzeppelin::presets::Account; - -// Local deps -use cairo_erc_3525::interface::{IERC3525Dispatcher, IERC3525DispatcherTrait, IERC3525_ID}; -use cairo_erc_3525::presets::erc3525_mintable_burnable::{ - ERC3525MintableBurnable, IExternalDispatcher, IExternalDispatcherTrait -}; -use cairo_erc_3525::tests::integration::constants; - -#[derive(Drop)] -struct Signers { - owner: ContractAddress, - someone: ContractAddress, - anyone: ContractAddress, - operator: ContractAddress, -} - -fn deploy_contract(class_hash: starknet::class_hash::ClassHash) -> ContractAddress { - let calldata: Array = array![constants::VALUE_DECIMALS.into()]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); - address -} - -fn deploy_account( - class_hash: starknet::class_hash::ClassHash, public_key: felt252 -) -> ContractAddress { - let calldata: Array = array![public_key]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); - address -} - -fn __setup__() -> (ContractAddress, Signers) { - let contract_address = deploy_contract( - ERC3525MintableBurnable::TEST_CLASS_HASH.try_into().unwrap() - ); - let class_hash = Account::TEST_CLASS_HASH.try_into().unwrap(); - let signer = Signers { - owner: deploy_account(class_hash, 'OWNER'), - someone: deploy_account(class_hash, 'SOMEONE'), - anyone: deploy_account(class_hash, 'ANYONE'), - operator: deploy_account(class_hash, 'OPERATOR'), - }; - (contract_address, signer) -} - -#[test] -#[available_gas(100_000_000)] -fn test_integration_supports_interface() { - // Setup - let (contract_address, _) = __setup__(); - let src5 = ISRC5Dispatcher { contract_address }; - assert(src5.supports_interface(ISRC5_ID), 'ISRC5 not supported'); - assert(src5.supports_interface(IERC721_ID), 'IERC721 not supported'); - assert(src5.supports_interface(IERC3525_ID), 'IERC3525 not supported'); -} - -#[test] -#[available_gas(100_000_000)] -fn test_integration_base_scenario() { - // Setup - let (contract_address, signers) = __setup__(); - let external = IExternalDispatcher { contract_address }; - let erc3525 = IERC3525Dispatcher { contract_address }; - let erc721 = IERC721Dispatcher { contract_address }; - - // Mint tokens - let one = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); - let two = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); - let three = external.mint(signers.someone, constants::SLOT_1, constants::VALUE); - let _four = external.mint(signers.anyone, constants::SLOT_1, constants::VALUE); - let _five = external.mint(signers.operator, constants::SLOT_1, constants::VALUE); - let six = external.mint(signers.owner, constants::SLOT_2, constants::VALUE); - let seven = external.mint(signers.someone, constants::SLOT_2, constants::VALUE); - let height = external.mint(signers.someone, constants::SLOT_2, constants::VALUE); - let nine = external.mint(signers.anyone, constants::SLOT_2, constants::VALUE); - - // Asserts - assert(erc3525.value_of(one) == constants::VALUE, 'Wrong value'); - assert(erc721.owner_of(one) == signers.owner, 'Wrong owner'); - - // Approve - testing::set_contract_address(signers.owner); - erc3525.approve_value(one, signers.anyone, constants::VALUE / 2); - erc3525.approve_value(two, signers.anyone, constants::VALUE / 2); - erc3525.approve_value(six, signers.anyone, constants::VALUE / 2); - - // Transfers to token id - testing::set_contract_address(signers.anyone); - erc3525.transfer_value_from(one, three, constants::ZERO(), 1); - erc3525.transfer_value_from(two, three, constants::ZERO(), 1); - erc3525.transfer_value_from(six, seven, constants::ZERO(), 1); - - // Transfer to - let ten = erc3525.transfer_value_from(one, 0, signers.operator, 1); - assert(ten == 10, 'Wrong id'); - assert(erc3525.allowance(one, signers.anyone) == constants::VALUE / 2 - 2, 'Wrong allowance'); - assert(erc3525.value_of(one) == constants::VALUE - 2, 'Wrong value'); - assert(external.total_value(constants::SLOT_1) == 5 * constants::VALUE, 'Wrong total value'); - assert(external.total_value(constants::SLOT_2) == 4 * constants::VALUE, 'Wrong total value'); - - // Burn value - testing::set_contract_address(signers.owner); - external.burn_value(one, 3); - testing::set_contract_address(signers.someone); - external.burn_value(height, 2); - testing::set_contract_address(signers.anyone); - external.burn_value(nine, 1); - assert(erc3525.allowance(one, signers.anyone) == constants::VALUE / 2 - 2, 'Wrong allowance'); - assert(erc3525.value_of(one) == constants::VALUE - 2 - 3, 'Wrong value'); - assert( - external.total_value(constants::SLOT_1) == 5 * constants::VALUE - 3, 'Wrong total value' - ); - assert( - external.total_value(constants::SLOT_2) == 4 * constants::VALUE - 3, 'Wrong total value' - ); - - // Burn token - testing::set_contract_address(signers.owner); - external.burn(one); - assert( - external.total_value(constants::SLOT_1) == 4 * constants::VALUE + 2, 'Wrong total value' - ); - assert( - external.total_value(constants::SLOT_2) == 4 * constants::VALUE - 3, 'Wrong total value' - ); -} diff --git a/src/tests/integration/test_metadata.cairo b/src/tests/integration/test_metadata.cairo deleted file mode 100644 index 12570e3..0000000 --- a/src/tests/integration/test_metadata.cairo +++ /dev/null @@ -1,105 +0,0 @@ -// Core deps -use result::ResultTrait; -use option::OptionTrait; -use array::ArrayTrait; -use traits::{Into, TryInto}; - -// Starknet deps -use starknet::{ContractAddress}; -use starknet::testing; - -// External deps -use openzeppelin::introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}; -use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait, IERC721_ID}; -use openzeppelin::presets::Account; - -// Local deps -use cairo_erc_3525::interface::{IERC3525Dispatcher, IERC3525DispatcherTrait, IERC3525_ID}; -use cairo_erc_3525::presets::erc3525_mintable_burnable_metadata::{ - ERC3525MintableBurnableMetadata, IExternalDispatcher, IExternalDispatcherTrait -}; -use cairo_erc_3525::extensions::metadata::interface::{ - IERC3525MetadataDispatcher, IERC3525MetadataDispatcherTrait, IERC3525_METADATA_ID -}; -use cairo_erc_3525::tests::integration::constants; - -#[derive(Drop)] -struct Signers { - owner: ContractAddress, - someone: ContractAddress, - anyone: ContractAddress, - operator: ContractAddress, -} - -fn deploy_contract(class_hash: starknet::class_hash::ClassHash) -> ContractAddress { - let mut calldata: Array = ArrayTrait::new(); - constants::NAME().serialize(ref calldata); - constants::SYMBOL().serialize(ref calldata); - constants::BASE_URI().serialize(ref calldata); - constants::VALUE_DECIMALS.serialize(ref calldata); - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); - address -} - -fn deploy_account( - class_hash: starknet::class_hash::ClassHash, public_key: felt252 -) -> ContractAddress { - let calldata: Array = array![public_key]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); - address -} - -fn __setup__() -> (ContractAddress, Signers) { - let contract_address = deploy_contract( - ERC3525MintableBurnableMetadata::TEST_CLASS_HASH.try_into().unwrap() - ); - let class_hash = Account::TEST_CLASS_HASH.try_into().unwrap(); - let signer = Signers { - owner: deploy_account(class_hash, 'OWNER'), - someone: deploy_account(class_hash, 'SOMEONE'), - anyone: deploy_account(class_hash, 'ANYONE'), - operator: deploy_account(class_hash, 'OPERATOR'), - }; - (contract_address, signer) -} - -#[test] -#[available_gas(100_000_000)] -fn test_integration_metadata_supports_interface() { - // Setup - let (contract_address, _) = __setup__(); - let src5 = ISRC5Dispatcher { contract_address }; - assert(src5.supports_interface(ISRC5_ID), 'ISRC5 not supported'); - assert(src5.supports_interface(IERC721_ID), 'IERC721 not supported'); - assert(src5.supports_interface(IERC3525_ID), 'IERC3525 not supported'); - assert(src5.supports_interface(IERC3525_METADATA_ID), 'IMetadata not supported'); -} - -#[test] -#[available_gas(100_000_000)] -fn test_integration_metadata_scenario() { - // Setup - let (contract_address, signers) = __setup__(); - let external = IExternalDispatcher { contract_address }; - let _erc3525 = IERC3525Dispatcher { contract_address }; - let erc3525_metadata = IERC3525MetadataDispatcher { contract_address }; - let _erc721 = IERC721Dispatcher { contract_address }; - - // Mint tokens - let _one = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); - let _two = external.mint(signers.operator, constants::SLOT_1, constants::VALUE); - - // Assert metadata - assert(erc3525_metadata.contract_uri() == 0, 'Wrong contract uri'); - assert(erc3525_metadata.slot_uri(constants::SLOT_1) == 0, 'Wrong slot uri'); - assert(erc3525_metadata.slot_uri(constants::SLOT_2) == 0, 'Wrong slot uri'); - - // Set metadta - external.set_contract_uri(constants::CONTRACT_URI); - external.set_slot_uri(constants::SLOT_1, constants::SLOT_URI); - - // Assert metadata - assert(erc3525_metadata.contract_uri() == constants::CONTRACT_URI, 'Wrong contract uri'); - assert(erc3525_metadata.slot_uri(constants::SLOT_1) == constants::SLOT_URI, 'Wrong slot uri'); - assert(erc3525_metadata.slot_uri(constants::SLOT_2) == 0, 'Wrong slot uri'); -} diff --git a/src/tests/integration/test_receiver.cairo b/src/tests/integration/test_receiver.cairo deleted file mode 100644 index 5045d40..0000000 --- a/src/tests/integration/test_receiver.cairo +++ /dev/null @@ -1,83 +0,0 @@ -// Core deps -use result::ResultTrait; -use option::OptionTrait; -use array::ArrayTrait; -use traits::{Into, TryInto}; - -// Starknet deps -use starknet::{ContractAddress}; -use starknet::testing; - -// External deps -use openzeppelin::introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}; -use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait, IERC721_ID}; -use openzeppelin::presets::Account; - -// Local deps -use cairo_erc_3525::interface::{IERC3525Dispatcher, IERC3525DispatcherTrait, IERC3525_ID}; -use cairo_erc_3525::presets::erc3525_mintable_burnable::{ - ERC3525MintableBurnable, IExternalDispatcher, IExternalDispatcherTrait -}; -use cairo_erc_3525::tests::integration::constants; -use cairo_erc_3525::tests::mocks::receiver::{IReceiverDispatcher, IReceiverDispatcherTrait}; -use cairo_erc_3525::tests::mocks::receiver::Receiver; - -#[derive(Drop)] -struct Signers { - owner: ContractAddress, -} - -fn deploy_contract(class_hash: starknet::class_hash::ClassHash) -> ContractAddress { - let calldata: Array = array![constants::VALUE_DECIMALS.into()]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); - address -} - -fn deploy_account( - class_hash: starknet::class_hash::ClassHash, public_key: felt252 -) -> ContractAddress { - let calldata: Array = array![public_key]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); - address -} - -fn deploy_receiver(class_hash: starknet::class_hash::ClassHash) -> ContractAddress { - let calldata: Array = array![]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); - address -} - -fn __setup__() -> (ContractAddress, Signers, ContractAddress) { - let contract_address = deploy_contract( - ERC3525MintableBurnable::TEST_CLASS_HASH.try_into().unwrap() - ); - let class_hash = Account::TEST_CLASS_HASH.try_into().unwrap(); - let signer = Signers { owner: deploy_account(class_hash, 'OWNER') }; - let class_hash = Receiver::TEST_CLASS_HASH.try_into().unwrap(); - let receiver_address = deploy_receiver(class_hash); - - (contract_address, signer, receiver_address) -} - -#[test] -#[available_gas(100_000_000)] -fn test_integration_receiver_scenario() { - // Setup - let (contract_address, signers, receiver_address) = __setup__(); - let _receiver = IReceiverDispatcher { contract_address: receiver_address }; - let external = IExternalDispatcher { contract_address }; - let _erc3525 = IERC3525Dispatcher { contract_address }; - let _erc721 = IERC721Dispatcher { contract_address }; - - // Mint tokens - let _one = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); -// // Assert receiver -// assert(receiver.called() == false, 'Wrong receiver called'); - -// // Transfer to -// testing::set_contract_address(signers.owner); -// let _ = erc3525.transfer_value_from(one, 0, receiver_address, 1); - -// // Assert receiver -// assert(receiver.called() == true, 'Wrong receiver called'); -} diff --git a/src/tests/integration/test_slot_approvable.cairo b/src/tests/integration/test_slot_approvable.cairo deleted file mode 100644 index 64bf236..0000000 --- a/src/tests/integration/test_slot_approvable.cairo +++ /dev/null @@ -1,99 +0,0 @@ -// Core deps -use result::ResultTrait; -use option::OptionTrait; -use array::ArrayTrait; -use traits::{Into, TryInto}; - -// Starknet deps -use starknet::{ContractAddress}; -use starknet::testing; - -// External deps -use openzeppelin::introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}; -use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait, IERC721_ID}; -use openzeppelin::presets::Account; - -// Local deps -use cairo_erc_3525::interface::{IERC3525Dispatcher, IERC3525DispatcherTrait, IERC3525_ID}; -use cairo_erc_3525::presets::erc3525_mintable_burnable_metadata_slot_approvable::{ - ERC3525MintableBurnableMSA, IExternalDispatcher, IExternalDispatcherTrait -}; -use cairo_erc_3525::extensions::slotapprovable::interface::{ - IERC3525SlotApprovableDispatcher, IERC3525SlotApprovableDispatcherTrait, - IERC3525_SLOT_APPROVABLE_ID -}; -use cairo_erc_3525::tests::integration::constants; - -#[derive(Drop)] -struct Signers { - owner: ContractAddress, - operator: ContractAddress, -} - -fn deploy_contract(class_hash: starknet::class_hash::ClassHash) -> ContractAddress { - let mut calldata: Array = ArrayTrait::new(); - constants::NAME().serialize(ref calldata); - constants::SYMBOL().serialize(ref calldata); - constants::BASE_URI().serialize(ref calldata); - constants::VALUE_DECIMALS.serialize(ref calldata); - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false) - .expect('Deploy contract failed'); - address -} - -fn deploy_account( - class_hash: starknet::class_hash::ClassHash, public_key: felt252 -) -> ContractAddress { - let calldata: Array = array![public_key]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false) - .expect('Deploy account failed'); - address -} - -fn __setup__() -> (ContractAddress, Signers) { - let contract_address = deploy_contract( - ERC3525MintableBurnableMSA::TEST_CLASS_HASH.try_into().unwrap() - ); - let class_hash = Account::TEST_CLASS_HASH.try_into().unwrap(); - let signer = Signers { - owner: deploy_account(class_hash, 'OWNER'), - operator: deploy_account(class_hash, 'OPERATOR'), - }; - (contract_address, signer) -} - -#[test] -#[available_gas(100_000_000)] -fn test_integration_slot_approvable_supports_interface() { - // Setup - let (contract_address, _) = __setup__(); - let src5 = ISRC5Dispatcher { contract_address }; - assert(src5.supports_interface(ISRC5_ID), 'ISRC5 not supported'); - assert(src5.supports_interface(IERC721_ID), 'IERC721 not supported'); - assert(src5.supports_interface(IERC3525_ID), 'IERC3525 not supported'); - assert(src5.supports_interface(IERC3525_SLOT_APPROVABLE_ID), 'ISlotApprovable not supported'); -} - -#[test] -#[available_gas(100_000_000)] -fn test_integration_slot_aprovable_scenario() { - // Setup - let (contract_address, signers) = __setup__(); - let external = IExternalDispatcher { contract_address }; - let erc3525 = IERC3525Dispatcher { contract_address }; - let erc3525_sa = IERC3525SlotApprovableDispatcher { contract_address }; - let _erc721 = IERC721Dispatcher { contract_address }; - - // Mint tokens - let one = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); - let two = external.mint(signers.operator, constants::SLOT_1, constants::VALUE); - - // Slot approvals - testing::set_contract_address(signers.owner); - erc3525_sa.set_approval_for_slot(signers.owner, constants::SLOT_1, signers.operator, true); - - // Transfer value - testing::set_contract_address(signers.operator); - erc3525.transfer_value_from(one, two, constants::ZERO(), 1); -} - diff --git a/src/tests/integration/test_slot_enumerable.cairo b/src/tests/integration/test_slot_enumerable.cairo deleted file mode 100644 index d24a167..0000000 --- a/src/tests/integration/test_slot_enumerable.cairo +++ /dev/null @@ -1,170 +0,0 @@ -// Core deps -use result::ResultTrait; -use option::OptionTrait; -use array::ArrayTrait; -use traits::{Into, TryInto}; - -// Starknet deps -use starknet::{ContractAddress}; -use starknet::testing; - -// External deps -use openzeppelin::introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}; -use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait, IERC721_ID}; -use openzeppelin::presets::Account; - -// Local deps -use cairo_erc_3525::interface::{IERC3525Dispatcher, IERC3525DispatcherTrait, IERC3525_ID}; -use cairo_erc_3525::presets::erc3525_mintable_burnable_metadata_slot_approvable_slot_enumerable::{ - ERC3525MintableBurnableMSASE, IExternalDispatcher, IExternalDispatcherTrait -}; -use cairo_erc_3525::extensions::slotenumerable::interface::{ - IERC3525SlotEnumerableDispatcher, IERC3525SlotEnumerableDispatcherTrait, - IERC3525_SLOT_ENUMERABLE_ID -}; -use cairo_erc_3525::tests::integration::constants; - -use debug::PrintTrait; - -#[derive(Drop)] -struct Signers { - owner: ContractAddress, - someone: ContractAddress, - anyone: ContractAddress, - operator: ContractAddress, -} - -fn deploy_contract(class_hash: starknet::class_hash::ClassHash) -> ContractAddress { - let mut calldata: Array = ArrayTrait::new(); - constants::NAME().serialize(ref calldata); - constants::SYMBOL().serialize(ref calldata); - constants::BASE_URI().serialize(ref calldata); - constants::VALUE_DECIMALS.serialize(ref calldata); - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false) - .expect('Deploy contract failed'); - address -} - -fn deploy_account( - class_hash: starknet::class_hash::ClassHash, public_key: felt252 -) -> ContractAddress { - let calldata: Array = array![public_key]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false) - .expect('Deploy account failed'); - address -} - -fn __setup__() -> (ContractAddress, Signers) { - let contract_address = deploy_contract( - ERC3525MintableBurnableMSASE::TEST_CLASS_HASH.try_into().unwrap() - ); - let class_hash = Account::TEST_CLASS_HASH.try_into().unwrap(); - let signer = Signers { - owner: deploy_account(class_hash, 'OWNER'), - someone: deploy_account(class_hash, 'SOMEONE'), - anyone: deploy_account(class_hash, 'ANYONE'), - operator: deploy_account(class_hash, 'OPERATOR'), - }; - (contract_address, signer) -} - -#[test] -#[available_gas(100_000_000)] -fn test_integration_slot_enumerable_supports_interface() { - // Setup - let (contract_address, _) = __setup__(); - let src5 = ISRC5Dispatcher { contract_address }; - assert(src5.supports_interface(ISRC5_ID), 'ISRC5 not supported'); - assert(src5.supports_interface(IERC721_ID), 'IERC721 not supported'); - assert(src5.supports_interface(IERC3525_ID), 'IERC3525 not supported'); - assert(src5.supports_interface(IERC3525_SLOT_ENUMERABLE_ID), 'ISlotEnumerable not supported'); -} -#[test] -#[available_gas(100_000_000)] -fn test_integration_slot_enumerable_scenario() { - // Setup - let (contract_address, signers) = __setup__(); - let external = IExternalDispatcher { contract_address }; - let erc3525 = IERC3525Dispatcher { contract_address }; - let erc3525_se = IERC3525SlotEnumerableDispatcher { contract_address }; - let _erc721 = IERC721Dispatcher { contract_address }; - - // Mint tokens - let one = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); - let two = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); - let three = external.mint(signers.someone, constants::SLOT_1, constants::VALUE); - let four = external.mint(signers.anyone, constants::SLOT_1, constants::VALUE); - let five = external.mint(signers.operator, constants::SLOT_1, constants::VALUE); - - // Assert enumerable - assert(erc3525_se.slot_count() == 1, 'Wrong slot count'); - assert(erc3525_se.slot_by_index(0) == constants::SLOT_1, 'Wrong slot at index'); - assert(erc3525_se.token_supply_in_slot(constants::SLOT_1) == 5, 'Wrong toke supply in slot'); - assert( - erc3525_se.token_in_slot_by_index(constants::SLOT_1, 0) == one, - 'Wrong token in slot at index' - ); - assert( - erc3525_se.token_in_slot_by_index(constants::SLOT_1, 1) == two, - 'Wrong token in slot at index' - ); - assert( - erc3525_se.token_in_slot_by_index(constants::SLOT_1, 2) == three, - 'Wrong token in slot at index' - ); - assert( - erc3525_se.token_in_slot_by_index(constants::SLOT_1, 3) == four, - 'Wrong token in slot at index' - ); - assert( - erc3525_se.token_in_slot_by_index(constants::SLOT_1, 4) == five, - 'Wrong token in slot at index' - ); - - // Mint tokens - let six = external.mint(signers.owner, constants::SLOT_2, constants::VALUE); - let seven = external.mint(signers.someone, constants::SLOT_2, constants::VALUE); - let height = external.mint(signers.someone, constants::SLOT_2, constants::VALUE); - let nine = external.mint(signers.anyone, constants::SLOT_2, constants::VALUE); - - // Assert enumerable - assert(erc3525_se.slot_count() == 2, 'Wrong slot count'); - assert(erc3525_se.slot_by_index(1) == constants::SLOT_2, 'Wrong slot at index'); - assert(erc3525_se.token_supply_in_slot(constants::SLOT_2) == 4, 'Wrong toke supply in slot'); - assert( - erc3525_se.token_in_slot_by_index(constants::SLOT_2, 0) == six, - 'Wrong token in slot at index' - ); - assert( - erc3525_se.token_in_slot_by_index(constants::SLOT_2, 1) == seven, - 'Wrong token in slot at index' - ); - assert( - erc3525_se.token_in_slot_by_index(constants::SLOT_2, 2) == height, - 'Wrong token in slot at index' - ); - assert( - erc3525_se.token_in_slot_by_index(constants::SLOT_2, 3) == nine, - 'Wrong token in slot at index' - ); - - // Approve - testing::set_contract_address(signers.owner); - erc3525.approve_value(one, signers.anyone, constants::VALUE / 2); - erc3525.approve_value(two, signers.anyone, constants::VALUE / 2); - erc3525.approve_value(six, signers.anyone, constants::VALUE / 2); - - // Transfers to token id - testing::set_contract_address(signers.anyone); - erc3525.transfer_value_from(one, three, constants::ZERO(), 1); - erc3525.transfer_value_from(two, three, constants::ZERO(), 1); - erc3525.transfer_value_from(six, seven, constants::ZERO(), 1); - - // Transfer to - let ten = erc3525.transfer_value_from(one, 0, signers.operator, 1); - assert(ten == 10, 'Wrong id'); - assert(erc3525.allowance(one, signers.anyone) == constants::VALUE / 2 - 2, 'Wrong allowance'); - assert(erc3525.value_of(one) == constants::VALUE - 2, 'Wrong value'); - assert(external.total_value(constants::SLOT_1) == 5 * constants::VALUE, 'Wrong total value'); - assert(external.total_value(constants::SLOT_2) == 4 * constants::VALUE, 'Wrong total value'); -} diff --git a/src/tests/mocks/contracts.cairo b/src/tests/mocks/contracts.cairo deleted file mode 100644 index cccee78..0000000 --- a/src/tests/mocks/contracts.cairo +++ /dev/null @@ -1,331 +0,0 @@ -#[starknet::contract] -mod DualCaseERC3525Mock { - use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc721::ERC721Component; - use cairo_erc_3525::module::ERC3525Component; - use starknet::ContractAddress; - - component!(path: ERC721Component, storage: erc721, event: ERC721Event); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - component!(path: ERC3525Component, storage: erc3525, event: ERC3525Event); - - // ERC721 - #[abi(embed_v0)] - impl ERC721Impl = ERC721Component::ERC721Impl; - #[abi(embed_v0)] - impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; - #[abi(embed_v0)] - impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl; - #[abi(embed_v0)] - impl ERC721MetadataCamelOnly = - ERC721Component::ERC721MetadataCamelOnlyImpl; - impl ERC721InternalImpl = ERC721Component::InternalImpl; - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - - // ERC3525 - #[abi(embed_v0)] - impl ERC3525Impl = ERC3525Component::ERC3525Impl; - impl ERC3525InternalImpl = ERC3525Component::InternalImpl; - impl ERC3525AssertImpl = ERC3525Component::AssertImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc721: ERC721Component::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage, - #[substorage(v0)] - erc3525: ERC3525Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC721Event: ERC721Component::Event, - #[flat] - SRC5Event: SRC5Component::Event, - #[flat] - ERC3525Event: ERC3525Component::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - name: ByteArray, - symbol: ByteArray, - value_decimals: u8, - base_uri: ByteArray, - recipient: ContractAddress, - ) { - self.erc3525.initializer(value_decimals); - self.erc721.initializer(name, symbol, base_uri); - } -} - -#[starknet::contract] -mod DualCaseERC3525MetadataMock { - use cairo_erc_3525::extensions::metadata::module::ERC3525MetadataComponent; - use cairo_erc_3525::module::ERC3525Component; - use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc721::ERC721Component; - use starknet::ContractAddress; - - component!(path: ERC721Component, storage: erc721, event: ERC721Event); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - component!(path: ERC3525Component, storage: erc3525, event: ERC3525Event); - component!( - path: ERC3525MetadataComponent, storage: erc3525_metadata, event: ERC3525MetadataEvent - ); - - // ERC721 - #[abi(embed_v0)] - impl ERC721Impl = ERC721Component::ERC721Impl; - #[abi(embed_v0)] - impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; - #[abi(embed_v0)] - impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl; - #[abi(embed_v0)] - impl ERC721MetadataCamelOnly = - ERC721Component::ERC721MetadataCamelOnlyImpl; - impl ERC721InternalImpl = ERC721Component::InternalImpl; - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - - // ERC3525 - #[abi(embed_v0)] - impl ERC3525Impl = ERC3525Component::ERC3525Impl; - impl ERC3525InternalImpl = ERC3525Component::InternalImpl; - impl ERC3525AssertImpl = ERC3525Component::AssertImpl; - - // ERC3525Metadata - #[abi(embed_v0)] - impl ERC3525MetadataImpl = - ERC3525MetadataComponent::ERC3525MetadataImpl; - #[abi(embed_v0)] - impl ERC3525MetadataCamelOnlyImpl = - ERC3525MetadataComponent::ERC3525MetadataCamelOnlyImpl; - impl ERC3525MetadataInternalImpl = ERC3525MetadataComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc721: ERC721Component::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage, - #[substorage(v0)] - erc3525: ERC3525Component::Storage, - #[substorage(v0)] - erc3525_metadata: ERC3525MetadataComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC721Event: ERC721Component::Event, - #[flat] - SRC5Event: SRC5Component::Event, - #[flat] - ERC3525Event: ERC3525Component::Event, - #[flat] - ERC3525MetadataEvent: ERC3525MetadataComponent::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - name: ByteArray, - symbol: ByteArray, - value_decimals: u8, - base_uri: ByteArray, - recipient: ContractAddress, - ) { - self.erc3525_metadata.initializer(); - self.erc3525.initializer(value_decimals); - self.erc721.initializer(name, symbol, base_uri); - } -} - -#[starknet::contract] -mod DualCaseERC3525SlotApprovableMock { - use cairo_erc_3525::extensions::slotapprovable::module::ERC3525SlotApprovableComponent; - use cairo_erc_3525::module::ERC3525Component; - use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc721::ERC721Component; - use starknet::ContractAddress; - - component!(path: ERC721Component, storage: erc721, event: ERC721Event); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - component!(path: ERC3525Component, storage: erc3525, event: ERC3525Event); - component!( - path: ERC3525SlotApprovableComponent, - storage: erc3525_slot_approvable, - event: ERC3525SlotApprovableEvent - ); - - // ERC721 - #[abi(embed_v0)] - impl ERC721Impl = ERC721Component::ERC721Impl; - #[abi(embed_v0)] - impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; - #[abi(embed_v0)] - impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl; - #[abi(embed_v0)] - impl ERC721MetadataCamelOnly = - ERC721Component::ERC721MetadataCamelOnlyImpl; - impl ERC721InternalImpl = ERC721Component::InternalImpl; - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - - // ERC3525 - #[abi(embed_v0)] - impl ERC3525Impl = ERC3525Component::ERC3525Impl; - impl ERC3525InternalImpl = ERC3525Component::InternalImpl; - impl ERC3525AssertImpl = ERC3525Component::AssertImpl; - - // ERC3525SlotApprovable - #[abi(embed_v0)] - impl ERC3525SlotApprovableImpl = - ERC3525SlotApprovableComponent::ERC3525SlotApprovableImpl; - #[abi(embed_v0)] - impl ERC3525SlotApprovableCamelOnlyImpl = - ERC3525SlotApprovableComponent::ERC3525SlotApprovableCamelOnlyImpl; - impl ERC3525SlotApprovableInternalImpl = - ERC3525SlotApprovableComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc721: ERC721Component::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage, - #[substorage(v0)] - erc3525: ERC3525Component::Storage, - #[substorage(v0)] - erc3525_slot_approvable: ERC3525SlotApprovableComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC721Event: ERC721Component::Event, - #[flat] - SRC5Event: SRC5Component::Event, - #[flat] - ERC3525Event: ERC3525Component::Event, - #[flat] - ERC3525SlotApprovableEvent: ERC3525SlotApprovableComponent::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - name: ByteArray, - symbol: ByteArray, - value_decimals: u8, - base_uri: ByteArray, - recipient: ContractAddress, - ) { - self.erc3525_slot_approvable.initializer(); - self.erc3525.initializer(value_decimals); - self.erc721.initializer(name, symbol, base_uri); - } -} - -#[starknet::contract] -mod DualCaseERC3525SlotEnumerableMock { - use cairo_erc_3525::extensions::slotenumerable::module::ERC3525SlotEnumerableComponent; - use cairo_erc_3525::module::ERC3525Component; - use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc721::ERC721Component; - use starknet::ContractAddress; - - component!(path: ERC721Component, storage: erc721, event: ERC721Event); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - component!(path: ERC3525Component, storage: erc3525, event: ERC3525Event); - component!( - path: ERC3525SlotEnumerableComponent, - storage: erc3525_slot_enumerable, - event: ERC3525SlotEnumerableEvent - ); - - // ERC721 - #[abi(embed_v0)] - impl ERC721Impl = ERC721Component::ERC721Impl; - #[abi(embed_v0)] - impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; - #[abi(embed_v0)] - impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl; - #[abi(embed_v0)] - impl ERC721MetadataCamelOnly = - ERC721Component::ERC721MetadataCamelOnlyImpl; - impl ERC721InternalImpl = ERC721Component::InternalImpl; - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - - // ERC3525 - #[abi(embed_v0)] - impl ERC3525Impl = ERC3525Component::ERC3525Impl; - impl ERC3525InternalImpl = ERC3525Component::InternalImpl; - impl ERC3525AssertImpl = ERC3525Component::AssertImpl; - - // ERC3525SlotEnumerable - #[abi(embed_v0)] - impl ERC3525SlotEnumerableImpl = - ERC3525SlotEnumerableComponent::ERC3525SlotEnumerableImpl; - #[abi(embed_v0)] - impl ERC3525SlotEnumerableCamelOnlyImpl = - ERC3525SlotEnumerableComponent::ERC3525SlotEnumerableCamelOnlyImpl; - impl ERC3525SlotEnumerableInternalImpl = - ERC3525SlotEnumerableComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc721: ERC721Component::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage, - #[substorage(v0)] - erc3525: ERC3525Component::Storage, - #[substorage(v0)] - erc3525_slot_enumerable: ERC3525SlotEnumerableComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC721Event: ERC721Component::Event, - #[flat] - SRC5Event: SRC5Component::Event, - #[flat] - ERC3525Event: ERC3525Component::Event, - #[flat] - ERC3525SlotEnumerableEvent: ERC3525SlotEnumerableComponent::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - name: ByteArray, - symbol: ByteArray, - value_decimals: u8, - base_uri: ByteArray, - recipient: ContractAddress, - ) { - self.erc3525_slot_enumerable.initializer(); - self.erc3525.initializer(value_decimals); - self.erc721.initializer(name, symbol, base_uri); - } -} diff --git a/src/tests/mocks/receiver.cairo b/src/tests/mocks/receiver.cairo deleted file mode 100644 index 9fb92f8..0000000 --- a/src/tests/mocks/receiver.cairo +++ /dev/null @@ -1,81 +0,0 @@ -#[starknet::interface] -trait IReceiver { - fn called(self: @TContractState) -> bool; -} - -#[starknet::contract] -mod Receiver { - // Starknet deps - use starknet::ContractAddress; - - // External deps - use openzeppelin::introspection::interface::{ISRC5, ISRC5Dispatcher, ISRC5DispatcherTrait}; - use openzeppelin::introspection::src5::SRC5Component; - use cairo_erc_3525::interface::{IERC3525Receiver, IERC3525_RECEIVER_ID}; - - // Local deps - use super::IReceiver; - - // Components - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // Component implementations - impl SRC5InternalImpl = SRC5Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - src5: SRC5Component::Storage, - _called: bool - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - SRC5Event: SRC5Component::Event, - } - - - #[constructor] - fn constructor(ref self: ContractState) { - self.initializer(); - } - - #[abi(embed_v0)] - impl SRC5Impl of ISRC5 { - fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { - self.src5.supports_interface(interface_id) - } - } - - #[abi(embed_v0)] - impl ReceiverImpl of IReceiver { - fn called(self: @ContractState) -> bool { - self._called.read() - } - } - - #[abi(embed_v0)] - impl ERC3525ReceiverImpl of IERC3525Receiver { - fn on_erc3525_received( - ref self: ContractState, - operator: ContractAddress, - from_token_id: u256, - to_token_id: u256, - value: u256, - data: Span, - ) -> felt252 { - self._called.write(true); - IERC3525_RECEIVER_ID - } - } - - #[generate_trait] - impl InternalImpl of InternalTrait { - fn initializer(ref self: ContractState) { - // [Effect] Register interfaces - self.src5.register_interface(IERC3525_RECEIVER_ID); - } - } -} diff --git a/src/tests/unit/constants.cairo b/src/tests/unit/constants.cairo deleted file mode 100644 index 9afb714..0000000 --- a/src/tests/unit/constants.cairo +++ /dev/null @@ -1,110 +0,0 @@ -// Core imports - -use zeroable::Zeroable; - -// Starknet imports - -use starknet::ContractAddress; -use starknet::contract_address_const; - -// Local imports - -use cairo_erc_3525::module::ERC3525Component; -use cairo_erc_3525::extensions::metadata::module::ERC3525MetadataComponent; -use cairo_erc_3525::extensions::slotapprovable::module::ERC3525SlotApprovableComponent; -use cairo_erc_3525::extensions::slotenumerable::module::ERC3525SlotEnumerableComponent; -use cairo_erc_3525::tests::mocks::contracts::{ - DualCaseERC3525Mock, DualCaseERC3525MetadataMock, DualCaseERC3525SlotApprovableMock, - DualCaseERC3525SlotEnumerableMock -}; - -// Setup - -pub type ERC3525ComponentState = - ERC3525Component::ComponentState; -pub type ERC3525MetadataComponentState = - ERC3525MetadataComponent::ComponentState; -pub type ERC3525SlotApprovableComponentState = - ERC3525SlotApprovableComponent::ComponentState< - DualCaseERC3525SlotApprovableMock::ContractState - >; -pub type ERC3525SlotEnumerableComponentState = - ERC3525SlotEnumerableComponent::ComponentState< - DualCaseERC3525SlotEnumerableMock::ContractState - >; - -// State - -fn COMPONENT_STATE() -> ERC3525ComponentState { - ERC3525Component::component_state_for_testing() -} - -fn CONTRACT_STATE() -> DualCaseERC3525Mock::ContractState { - DualCaseERC3525Mock::contract_state_for_testing() -} - -fn COMPONENT_STATE_METADATA() -> ERC3525MetadataComponentState { - ERC3525MetadataComponent::component_state_for_testing() -} - -fn CONTRACT_STATE_METADATA() -> DualCaseERC3525MetadataMock::ContractState { - DualCaseERC3525MetadataMock::contract_state_for_testing() -} - -fn COMPONENT_STATE_SLOT_APPROVABLE() -> ERC3525SlotApprovableComponentState { - ERC3525SlotApprovableComponent::component_state_for_testing() -} - -fn CONTRACT_STATE_SLOT_APPROVABLE() -> DualCaseERC3525SlotApprovableMock::ContractState { - DualCaseERC3525SlotApprovableMock::contract_state_for_testing() -} - -fn COMPONENT_STATE_SLOT_ENUMERABLE() -> ERC3525SlotEnumerableComponentState { - ERC3525SlotEnumerableComponent::component_state_for_testing() -} - -fn CONTRACT_STATE_SLOT_ENUMERABLE() -> DualCaseERC3525SlotEnumerableMock::ContractState { - DualCaseERC3525SlotEnumerableMock::contract_state_for_testing() -} - -// Constants - -const NAME: felt252 = 'NAME'; -const SYMBOL: felt252 = 'SYMBOL'; -const VALUE_DECIMALS: u8 = 6; -const TOKEN_ID_1: u256 = 1; -const TOKEN_ID_2: u256 = 2; -const INVALID_TOKEN: u256 = 666; -const SLOT_1: u256 = 'SLOT1'; -const SLOT_2: u256 = 'SLOT2'; -const VALUE: u256 = 1000; - -// Addresses - -fn ZERO() -> ContractAddress { - Zeroable::zero() -} - -fn OWNER() -> ContractAddress { - contract_address_const::<'OWNER'>() -} - -fn RECIPIENT() -> ContractAddress { - contract_address_const::<'RECIPIENT'>() -} - -fn SPENDER() -> ContractAddress { - contract_address_const::<'SPENDER'>() -} - -fn OPERATOR() -> ContractAddress { - contract_address_const::<'OPERATOR'>() -} - -fn SOMEONE() -> ContractAddress { - contract_address_const::<'SOMEONE'>() -} - -fn ANYONE() -> ContractAddress { - contract_address_const::<'ANYONE'>() -} diff --git a/src/tests/unit/test_approvals.cairo b/src/tests/unit/test_approvals.cairo deleted file mode 100644 index bc59421..0000000 --- a/src/tests/unit/test_approvals.cairo +++ /dev/null @@ -1,155 +0,0 @@ -// Starknet imports - -use starknet::testing::set_caller_address; - -// External imports - -use openzeppelin::token::erc721::erc721::ERC721Component::ERC721Impl; -use openzeppelin::token::erc721::erc721::ERC721Component; - -// Local imports - -use cairo_erc_3525::module::ERC3525Component::{ERC3525Impl, InternalImpl}; -use cairo_erc_3525::module::ERC3525Component; -use cairo_erc_3525::tests::unit::constants::{ - ERC3525ComponentState, COMPONENT_STATE, CONTRACT_STATE, VALUE_DECIMALS, TOKEN_ID_1, - INVALID_TOKEN, SLOT_1, VALUE, ZERO, OWNER, OPERATOR, SOMEONE, ANYONE -}; - -// Settings - -fn setup() -> ERC3525ComponentState { - let mut state = COMPONENT_STATE(); - state.initializer(VALUE_DECIMALS); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, 1 * VALUE); - state -} - -// Tests approvals - -#[test] -#[available_gas(20000000)] -fn test_owner_can_approve_operator_value() { - let mut state = setup(); - set_caller_address(OWNER()); - let allowance = state.allowance(TOKEN_ID_1, OPERATOR()); - assert(allowance == 0, 'Wrong allowance'); - state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); - let allowance = state.allowance(TOKEN_ID_1, OPERATOR()); - assert(allowance == VALUE, 'Wrong allowance'); -} - -#[test] -#[available_gas(20000000)] -fn test_owner_can_approve_more_to_same() { - let mut state = setup(); - set_caller_address(OWNER()); - state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); - state.approve_value(TOKEN_ID_1, OPERATOR(), 2 * VALUE); - let allowance = state.allowance(TOKEN_ID_1, OPERATOR()); - assert(allowance == 2 * VALUE, 'Wrong allowance'); -} - -#[test] -#[available_gas(20000000)] -fn test_owner_can_approve_less_to_same() { - let mut state = setup(); - set_caller_address(OWNER()); - state.approve_value(TOKEN_ID_1, OPERATOR(), 2 * VALUE); - state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); - let allowance = state.allowance(TOKEN_ID_1, OPERATOR()); - assert(allowance == VALUE, 'Wrong allowance'); -} - -#[test] -#[available_gas(20000000)] -fn test_owner_can_approve_someone_else() { - let mut state = setup(); - set_caller_address(OWNER()); - state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); - let allowance = state.allowance(TOKEN_ID_1, OPERATOR()); - assert(allowance == VALUE, 'Wrong allowance'); - state.approve_value(TOKEN_ID_1, SOMEONE(), 2 * VALUE); - let allowance = state.allowance(TOKEN_ID_1, SOMEONE()); - assert(allowance == 2 * VALUE, 'Wrong allowance'); -} - -#[test] -#[available_gas(20000000)] -fn test_721approved_for_all_can_approve_value() { - let mut state = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - // ERC721 setup - mock_state.erc721.set_approval_for_all(SOMEONE(), true); - // ERC3525 test - set_caller_address(SOMEONE()); // Prank - state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); - let allowance = state.allowance(TOKEN_ID_1, OPERATOR()); - assert(allowance == VALUE, 'Wrong allowance'); -} - -#[test] -#[available_gas(20000000)] -fn test_721approved_can_approve_value() { - let mut state = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - // ERC721 setup - mock_state.erc721.approve(SOMEONE(), TOKEN_ID_1); - // ERC3525 test - set_caller_address(SOMEONE()); // Prank - state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); - let allowance = state.allowance(TOKEN_ID_1, OPERATOR()); - assert(allowance == VALUE, 'Wrong allowance'); -} - -#[test] -#[should_panic] -#[available_gas(20000000)] -fn test_cannot_approve_value_to_zero_address() { - let mut state = setup(); - set_caller_address(OWNER()); - // ERC3525 test - state.approve_value(TOKEN_ID_1, ZERO(), VALUE); -} - -#[test] -#[should_panic] -#[available_gas(20000000)] -fn test_cannot_approve_value_to_self() { - let mut state = setup(); - set_caller_address(OWNER()); - // ERC3525 test - state.approve_value(TOKEN_ID_1, OWNER(), VALUE); -} - -#[test] -#[should_panic] -#[available_gas(20000000)] -fn test_anyone_cannot_approve_value() { - let mut state = setup(); - set_caller_address(ANYONE()); - // ERC3525 test - state.approve_value(TOKEN_ID_1, ANYONE(), VALUE); -} - -#[test] -#[should_panic] -#[available_gas(20000000)] -fn test_cannot_approve_value_nonexistent_token_id() { - let mut state = setup(); - set_caller_address(OWNER()); - // ERC3525 test - state.approve_value(INVALID_TOKEN, OPERATOR(), VALUE); -} - -#[test] -#[should_panic] -#[available_gas(20000000)] -fn test_zero_address_cannot_approve_value() { - let mut state = setup(); - set_caller_address(ZERO()); - // ERC3525 test - state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); -} diff --git a/src/tests/unit/test_initialization.cairo b/src/tests/unit/test_initialization.cairo deleted file mode 100644 index 9ec4562..0000000 --- a/src/tests/unit/test_initialization.cairo +++ /dev/null @@ -1,16 +0,0 @@ -// Local imports - -use cairo_erc_3525::module::ERC3525Component::{ERC3525Impl, InternalImpl}; -use cairo_erc_3525::module::ERC3525Component; -use cairo_erc_3525::tests::unit::constants::{COMPONENT_STATE, VALUE_DECIMALS}; - - -// Tests initialization - -#[test] -#[available_gas(20000000)] -fn test_can_set_valid_decimals() { - let mut state = COMPONENT_STATE(); - state.initializer(VALUE_DECIMALS); - assert(state.value_decimals() == VALUE_DECIMALS, 'Wrong value decimals'); -} diff --git a/src/tests/unit/test_metadata.cairo b/src/tests/unit/test_metadata.cairo deleted file mode 100644 index 44a0ab3..0000000 --- a/src/tests/unit/test_metadata.cairo +++ /dev/null @@ -1,60 +0,0 @@ -// Core imports - -use integer::BoundedInt; -use debug::PrintTrait; - -// Starknet imports - -use starknet::ContractAddress; -use starknet::testing::set_caller_address; - -// External imports - -use openzeppelin::token::erc721::erc721::ERC721Component; -use openzeppelin::presets::Account; - -// Local imports - -use cairo_erc_3525::module::ERC3525Component; -use cairo_erc_3525::extensions::metadata::module::ERC3525MetadataComponent::{ - ERC3525MetadataImpl, InternalImpl -}; -use cairo_erc_3525::extensions::metadata::module::ERC3525MetadataComponent; -use cairo_erc_3525::tests::unit::constants::{ - ERC3525MetadataComponentState, COMPONENT_STATE, COMPONENT_STATE_METADATA, VALUE_DECIMALS, - SLOT_1, SLOT_2 -}; - -// Settings - -fn setup() -> ERC3525MetadataComponentState { - let mut state_metadata = COMPONENT_STATE_METADATA(); - state_metadata.initializer(); - state_metadata -} - -#[test] -#[available_gas(20000000)] -fn test_metadata_contract_uri() { - let mut state_metadata = setup(); - let uri = state_metadata.contract_uri(); - assert(uri == 0, 'Wrong contract URI'); - let new_uri = 'https://example.com'; - state_metadata._set_contract_uri(new_uri); - let uri = state_metadata.contract_uri(); - assert(uri == new_uri, 'Wrong contract URI'); -} - -#[test] -#[available_gas(20000000)] -fn test_metadata_slot_uri() { - let mut state_metadata = setup(); - let uri = state_metadata.slot_uri(SLOT_1); - assert(uri == 0, 'Wrong contract URI'); - let new_uri = 'https://example.com'; - state_metadata._set_slot_uri(SLOT_1, new_uri); - let uri = state_metadata.slot_uri(SLOT_1); - assert(uri == new_uri, 'Wrong contract URI'); - let uri = state_metadata.slot_uri(SLOT_2); - assert(uri == 0, 'Wrong contract URI'); -} diff --git a/src/tests/unit/test_mint_burn.cairo b/src/tests/unit/test_mint_burn.cairo deleted file mode 100644 index 0f7b1af..0000000 --- a/src/tests/unit/test_mint_burn.cairo +++ /dev/null @@ -1,230 +0,0 @@ -// Core imports - -use debug::PrintTrait; - -// Starknet imports - -use starknet::get_contract_address; -use starknet::testing::set_caller_address; - -// External imports - -use openzeppelin::token::erc721::erc721::ERC721Component::{ - ERC721Impl, InternalImpl as ERC721InternalImpl -}; -use openzeppelin::token::erc721::erc721::ERC721Component; - -// Local imports - -use cairo_erc_3525::module::ERC3525Component::{ERC3525Impl, InternalImpl}; -use cairo_erc_3525::module::ERC3525Component; -use cairo_erc_3525::tests::utils; -use cairo_erc_3525::tests::unit::constants::{ - ERC3525ComponentState, COMPONENT_STATE, CONTRACT_STATE, VALUE_DECIMALS, TOKEN_ID_1, SLOT_1, - VALUE, ZERO, OWNER -}; - -// Settings - -fn setup() -> ERC3525ComponentState { - let mut state = COMPONENT_STATE(); - state.initializer(VALUE_DECIMALS); - state -} - -// Tests approvals - -#[test] -#[available_gas(20000000)] -fn test_mint() { - let mut state = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - - let balance = mock_state.balance_of(OWNER()); - assert(balance == 1, 'Wrong balance'); - let slot = state.slot_of(TOKEN_ID_1); - assert(slot == SLOT_1, 'Wrong slot'); - let owner = mock_state.owner_of(TOKEN_ID_1); - assert(owner == OWNER(), 'Wrong owner'); - - // [Assert] Events - let event = utils::pop_log::(get_contract_address()).unwrap(); - match event { - ERC721Component::Event::Transfer(event) => { - assert(event.from == ZERO(), 'Wrong event from'); - assert(event.to == OWNER(), 'Wrong event to'); - assert(event.token_id == TOKEN_ID_1, 'Wrong event token_id'); - }, - _ => panic!("Wrong event type"), - } - let event = starknet::testing::pop_log::(get_contract_address()) - .unwrap(); - assert(event.token_id == TOKEN_ID_1, 'Wrong event from_token_id'); - assert(event.old_slot == 0, 'Wrong event old_slot'); - assert(event.new_slot == SLOT_1, 'Wrong new_slot value'); - let event = starknet::testing::pop_log::< - ERC3525Component::TransferValue - >(get_contract_address()) - .unwrap(); - assert(event.from_token_id == 0, 'Wrong event from_token_id'); - assert(event.to_token_id == TOKEN_ID_1, 'Wrong event to_token_id'); - assert(event.value == VALUE, 'Wrong event value'); -} - -#[test] -#[available_gas(20000000)] -fn test_mint_value_with_previous_balance() { - let mut state = setup(); - set_caller_address(OWNER()); - - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint_value(TOKEN_ID_1, VALUE); - let value = state.value_of(TOKEN_ID_1); - assert(value == 2 * VALUE, 'Wrong value'); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: token not minted',))] -fn test_mint_value_revert_not_minted() { - let mut state = setup(); - set_caller_address(OWNER()); - - state._mint_value(TOKEN_ID_1, VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: invalid token_id',))] -fn test_mint_revert_zero_token_id() { - let mut state = setup(); - set_caller_address(OWNER()); - let token_id: u256 = 0; - - state._mint(OWNER(), token_id, SLOT_1, VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: invalid address',))] -fn test_mint_revert_zero_address() { - let mut state = setup(); - set_caller_address(OWNER()); - - state._mint(ZERO(), TOKEN_ID_1, SLOT_1, VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: token already minted',))] -fn test_mint_revert_existing_id() { - let mut state = setup(); - set_caller_address(OWNER()); - - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: token not minted',))] -fn test_burn() { - let mut state = setup(); - set_caller_address(OWNER()); - - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._burn(TOKEN_ID_1); - - // [Setup] mint Transfer, SlotChanged and TransferValue events - let _event = starknet::testing::pop_log::(get_contract_address()) - .unwrap(); - let _event = starknet::testing::pop_log::(get_contract_address()) - .unwrap(); - let _event = starknet::testing::pop_log::< - ERC3525Component::TransferValue - >(get_contract_address()) - .unwrap(); - - // [Assert] Events - let event = utils::pop_log::(get_contract_address()).unwrap(); - match event { - ERC721Component::Event::Transfer(event) => { - assert(event.from == OWNER(), 'Wrong event from'); - assert(event.to == ZERO(), 'Wrong event to'); - assert(event.token_id == TOKEN_ID_1, 'Wrong event token_id'); - }, - _ => panic!("Wrong event type"), - } - let event = starknet::testing::pop_log::< - ERC3525Component::TransferValue - >(get_contract_address()) - .unwrap(); - assert(event.from_token_id == TOKEN_ID_1, 'Wrong event from_token_id'); - assert(event.to_token_id == 0, 'Wrong event to_token_id'); - assert(event.value == VALUE, 'Wrong event value'); - let event = starknet::testing::pop_log::(get_contract_address()) - .unwrap(); - assert(event.token_id == TOKEN_ID_1, 'Wrong event from_token_id'); - assert(event.old_slot == SLOT_1, 'Wrong event old_slot'); - assert(event.new_slot == 0, 'Wrong new_slot value'); - - // [Assert] Token does not exist anymore - let _value = state.value_of(TOKEN_ID_1); -} - -#[test] -#[available_gas(20000000)] -fn test_burn_value() { - let mut state = setup(); - set_caller_address(OWNER()); - - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._burn_value(TOKEN_ID_1, VALUE); - - // [Setup] mint Transfer, SlotChanged and TransferValue events - let _event = starknet::testing::pop_log::(get_contract_address()) - .unwrap(); - let _event = starknet::testing::pop_log::(get_contract_address()) - .unwrap(); - let _event = starknet::testing::pop_log::< - ERC3525Component::TransferValue - >(get_contract_address()) - .unwrap(); - - // [Assert] Events - let event = starknet::testing::pop_log::< - ERC3525Component::TransferValue - >(get_contract_address()) - .unwrap(); - assert(event.from_token_id == TOKEN_ID_1, 'Wrong event from_token_id'); - assert(event.to_token_id == 0, 'Wrong event to_token_id'); - assert(event.value == VALUE, 'Wrong event value'); - - // [Assert] Token value - let value = state.value_of(TOKEN_ID_1); - assert(value == 0, 'Wrong value'); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: token not minted',))] -fn test_burn_revert_not_minted() { - let mut state = setup(); - set_caller_address(OWNER()); - - state._burn(TOKEN_ID_1); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: value exceeds balance',))] -fn test_burn_value_revert_exceeds_balance() { - let mut state = setup(); - set_caller_address(OWNER()); - - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._burn_value(TOKEN_ID_1, 2 * VALUE); -} diff --git a/src/tests/unit/test_slot_approvable.cairo b/src/tests/unit/test_slot_approvable.cairo deleted file mode 100644 index 14aca31..0000000 --- a/src/tests/unit/test_slot_approvable.cairo +++ /dev/null @@ -1,119 +0,0 @@ -// Core imports - -use debug::PrintTrait; - -// Starknet imports - -use starknet::testing::set_caller_address; - -// External imports - -use openzeppelin::token::erc721::erc721::ERC721Component; - -// Local imports - -use cairo_erc_3525::module::ERC3525Component::{InternalTrait, ERC3525Impl}; -use cairo_erc_3525::module::ERC3525Component; -use cairo_erc_3525::extensions::slotapprovable::module::ERC3525SlotApprovableComponent::{ - ERC3525SlotApprovableImpl, InternalTrait as ERC3525SlotApprovableInternalTrait, - ExternalTrait as ERC3525SlotApprovableExternalTrait -}; -use cairo_erc_3525::extensions::slotapprovable::module::ERC3525SlotApprovableComponent; -use cairo_erc_3525::tests::unit::constants::{ - ERC3525SlotApprovableComponentState, CONTRACT_STATE, COMPONENT_STATE_SLOT_APPROVABLE, - VALUE_DECIMALS, TOKEN_ID_1, TOKEN_ID_2, SLOT_1, SLOT_2, VALUE, ZERO, OWNER, OPERATOR, SOMEONE -}; - -// Settings - -fn setup() -> ERC3525SlotApprovableComponentState { - let mut state = COMPONENT_STATE_SLOT_APPROVABLE(); - let mut mock_state = CONTRACT_STATE(); - mock_state.erc3525.initializer(VALUE_DECIMALS); - state.initializer(); - state -} -// fn setup() -> (ERC3525::ContractState, ERC3525SlotApprovable::ContractState) { -// let mut state = STATE(); -// mock_state.initializer(VALUE_DECIMALS); -// let mut state_slot_approvable = STATE_SLOT_APPROVABLE(); -// state.initializer(ref state_slot_approvable); -// (state, state_slot_approvable) -// } - -// Tests approvals - -#[test] -#[available_gas(20000000)] -fn test_slot_approvable_owner_can_approve_slot() { - let mut state = setup(); - set_caller_address(OWNER()); - state.set_approval_for_slot(OWNER(), SLOT_1, OPERATOR(), true); -} - -#[test] -#[available_gas(20000000)] -fn test_slot_approvable_operator_can_approve_value() { - let mut state = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - mock_state.erc3525._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state.set_approval_for_slot(OWNER(), SLOT_1, OPERATOR(), true); - set_caller_address(OPERATOR()); - state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: caller not allowed',))] -fn test_slot_approvable_operator_cannot_approve_any_token() { - let mut state = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - mock_state.erc3525._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state.set_approval_for_slot(OWNER(), SLOT_2, OPERATOR(), true); - set_caller_address(OPERATOR()); - state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: insufficient allowance',))] -fn test_slot_approvable_operator_cannot_transfer_anyones_value() { - let mut state = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - mock_state.erc3525._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - mock_state.erc3525._mint(SOMEONE(), TOKEN_ID_2, SLOT_1, VALUE); - state.set_approval_for_slot(OWNER(), SLOT_1, OPERATOR(), true); - set_caller_address(OPERATOR()); - state.transfer_value_from(TOKEN_ID_2, 0, OPERATOR(), VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: slot mismatch',))] -fn test_slot_approvable_operator_transfer_value_revert_slot_mismatch() { - let mut state = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - mock_state.erc3525._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - mock_state.erc3525._mint(SOMEONE(), TOKEN_ID_2, SLOT_2, VALUE); - state.set_approval_for_slot(OWNER(), SLOT_1, OPERATOR(), true); - set_caller_address(OPERATOR()); - state.transfer_value_from(TOKEN_ID_1, TOKEN_ID_2, ZERO(), VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: caller not allowed',))] -fn test_slot_approvable_revoked_slot_operator_cannot_approve() { - let mut state = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - mock_state.erc3525._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state.set_approval_for_slot(OWNER(), SLOT_1, OPERATOR(), true); - state.set_approval_for_slot(OWNER(), SLOT_1, OPERATOR(), false); - set_caller_address(OPERATOR()); - state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); -} diff --git a/src/tests/unit/test_slot_enumerable.cairo b/src/tests/unit/test_slot_enumerable.cairo deleted file mode 100644 index 2e9d463..0000000 --- a/src/tests/unit/test_slot_enumerable.cairo +++ /dev/null @@ -1,234 +0,0 @@ -// Core imports - -use integer::BoundedInt; -use debug::PrintTrait; - -// Starknet imports - -use starknet::ContractAddress; -use starknet::testing::set_caller_address; - -// External imports - -use openzeppelin::token::erc721::erc721::ERC721Component::{ - ERC721Impl, InternalImpl as ERC721InternalImpl -}; -use openzeppelin::token::erc721::erc721::ERC721Component; -use openzeppelin::presets::Account; - -// Local imports - -use cairo_erc_3525::module::ERC3525Component::{ERC3525Impl, InternalImpl}; -use cairo_erc_3525::module::ERC3525Component; -use cairo_erc_3525::extensions::slotenumerable::module::ERC3525SlotEnumerableComponent::{ - ERC3525SlotEnumerableImpl, InternalImpl as ERC3525SlotEnumerableInternalImpl -}; -use cairo_erc_3525::extensions::slotenumerable::module::ERC3525SlotEnumerableComponent; -use cairo_erc_3525::tests::unit::constants::{ - ERC3525SlotEnumerableComponentState, CONTRACT_STATE, COMPONENT_STATE_SLOT_ENUMERABLE, - VALUE_DECIMALS, TOKEN_ID_1, TOKEN_ID_2, SLOT_1, SLOT_2, VALUE, ZERO, OWNER, SOMEONE -}; - -// Settings - -fn deploy_account( - class_hash: starknet::class_hash::ClassHash, public_key: felt252 -) -> ContractAddress { - let calldata: Array = array![public_key]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); - address -} - - -fn setup() -> (ERC3525SlotEnumerableComponentState, ContractAddress) { - let class_hash = Account::TEST_CLASS_HASH.try_into().unwrap(); - let receiver = deploy_account(class_hash, 'RECEIVER'); - let mut state = COMPONENT_STATE_SLOT_ENUMERABLE(); - let mut mock_state = CONTRACT_STATE(); - mock_state.erc3525.initializer(VALUE_DECIMALS); - state.initializer(); - (state, receiver) -} - -#[test] -#[available_gas(20000000)] -fn test_slot_enumerable_slot_count() { - let (mut state, _) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(OWNER(), TOKEN_ID_2, SLOT_2, 0); - let count = state.slot_count(); - assert(count == 2, 'Wrong slot count'); -} - -#[test] -#[available_gas(20000000)] -fn test_slot_enumerable_slot_by_index() { - let (mut state, _) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(OWNER(), TOKEN_ID_2, SLOT_2, 0); - let slot = state.slot_by_index(0); - assert(slot == SLOT_1, 'Wrong slot'); - let slot = state.slot_by_index(1); - assert(slot == SLOT_2, 'Wrong slot'); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: index out of bounds',))] -fn test_slot_enumerable_slot_by_index_revert_out_of_bounds() { - let (mut state, _) = setup(); - state.slot_by_index(0); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: index out of bounds',))] -fn test_slot_enumerable_slot_by_index_revert_overflow() { - let (mut state, _) = setup(); - state.slot_by_index(BoundedInt::max()); -} - -#[test] -#[available_gas(20000000)] -fn test_slot_enumerable_token_supply_in_slot() { - let (mut state, _) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(OWNER(), TOKEN_ID_2, SLOT_2, 0); - let supply = state.token_supply_in_slot(SLOT_1); - assert(supply == 1, 'Wrong token supply'); - let supply = state.token_supply_in_slot(SLOT_2); - assert(supply == 1, 'Wrong token supply'); -} - -#[test] -#[available_gas(20000000)] -fn test_slot_enumerable_token_supply_in_slot_is_empty() { - let (mut state, _) = setup(); - let supply = state.token_supply_in_slot(SLOT_1); - assert(supply == 0, 'Wrong token supply'); -} - -#[test] -#[available_gas(20000000)] -fn test_slot_enumerable_token_supply_in_slot_after_transfer() { - let (mut state, _) = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - // ERC721 setup - mock_state.erc721.transfer_from(OWNER(), SOMEONE(), TOKEN_ID_1); - // [Assert] Token supply in slot - let supply = state.token_supply_in_slot(SLOT_1); - assert(supply == 1, 'Wrong token supply'); -} - -#[test] -#[available_gas(20000000)] -fn test_slot_enumerable_token_supply_in_slot_after_transfer_to_address() { - let (mut state, receiver) = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - // [Effect] Transfer value to address - let new_token_id = mock_state.erc3525.transfer_value_from(TOKEN_ID_1, 0, receiver, VALUE); - state._after_transfer_value_from(new_token_id); - // [Assert] Token supply in slot - let supply = state.token_supply_in_slot(SLOT_1); - assert(supply == 2, 'Wrong token supply'); -} - -#[test] -#[available_gas(20000000)] -fn test_slot_enumerable_token_supply_in_slot_after_transfer_to_token() { - let (mut state, receiver) = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(receiver, TOKEN_ID_2, SLOT_1, VALUE); - // [Effect] Transfer value to address - mock_state.erc3525.transfer_value_from(TOKEN_ID_1, TOKEN_ID_2, ZERO(), VALUE); - state._after_transfer_value_from(TOKEN_ID_2); - // [Assert] Token supply in slot - let supply = state.token_supply_in_slot(SLOT_1); - assert(supply == 2, 'Wrong token supply'); -} - -#[test] -#[available_gas(20000000)] -fn test_slot_enumerable_token_in_slot_by_index() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(receiver, TOKEN_ID_2, SLOT_2, VALUE); - // [Assert] Token in slot by index - let token_id = state.token_in_slot_by_index(SLOT_1, 0); - assert(token_id == TOKEN_ID_1, 'Wrong token id'); - let token_id = state.token_in_slot_by_index(SLOT_2, 0); - assert(token_id == TOKEN_ID_2, 'Wrong token id'); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: index out of bounds',))] -fn test_slot_enumerable_token_in_slot_by_index_revert_out_of_bounds() { - let (mut state, _) = setup(); - // [Assert] Token in slot by index - state.token_in_slot_by_index(SLOT_1, 0); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: index out of bounds',))] -fn test_slot_enumerable_token_in_slot_by_index_revert_overflow() { - let (mut state, _) = setup(); - // [Assert] Token in slot by index - state.token_in_slot_by_index(SLOT_1, BoundedInt::max()); -} - -#[test] -#[available_gas(20000000)] -fn test_slot_enumerable_token_in_slot_by_index_after_transfer() { - let (mut state, _) = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - // ERC721 setup - mock_state.erc721.transfer_from(OWNER(), SOMEONE(), TOKEN_ID_1); - // [Assert] Token in slot by index - let token_id = state.token_in_slot_by_index(SLOT_1, 0); - assert(token_id == TOKEN_ID_1, 'Wrong token id'); -} - -#[test] -#[available_gas(20000000)] -fn test_slot_enumerable_token_in_slot_by_index_after_transfer_to_address() { - let (mut state, receiver) = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - // [Effect] Transfer value to address - let new_token_id = mock_state.erc3525.transfer_value_from(TOKEN_ID_1, 0, receiver, VALUE); - state._after_transfer_value_from(new_token_id); - // [Assert] Token in slot by index - let token_id = state.token_in_slot_by_index(SLOT_1, 0); - assert(token_id == TOKEN_ID_1, 'Wrong token id'); -} - -#[test] -#[available_gas(20000000)] -fn test_slot_enumerable_token_in_slot_by_index_after_transfer_to_token() { - let (mut state, receiver) = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(receiver, TOKEN_ID_2, SLOT_1, VALUE); - // [Effect] Transfer value to address - mock_state.erc3525.transfer_value_from(TOKEN_ID_1, TOKEN_ID_2, ZERO(), VALUE); - state._after_transfer_value_from(TOKEN_ID_2); - // [Assert] Token in slot by index - let token_id = state.token_in_slot_by_index(SLOT_1, 0); - assert(token_id == TOKEN_ID_1, 'Wrong token id'); -} diff --git a/src/tests/unit/test_transfer_to_address.cairo b/src/tests/unit/test_transfer_to_address.cairo deleted file mode 100644 index 4a0942f..0000000 --- a/src/tests/unit/test_transfer_to_address.cairo +++ /dev/null @@ -1,187 +0,0 @@ -// Core imports - -use integer::BoundedInt; -use debug::PrintTrait; - -// Starknet imports - -use starknet::ContractAddress; -use starknet::testing::set_caller_address; - -// External imports - -use openzeppelin::token::erc721::erc721::ERC721Component::{ - ERC721Impl, InternalImpl as ERC721InternalImpl -}; -use openzeppelin::token::erc721::erc721::ERC721Component; -use openzeppelin::presets::Account; - -// Local imports - -use cairo_erc_3525::module::ERC3525Component::{ERC3525Impl, InternalImpl}; -use cairo_erc_3525::module::ERC3525Component; -use cairo_erc_3525::tests::unit::constants::{ - ERC3525ComponentState, COMPONENT_STATE, CONTRACT_STATE, VALUE_DECIMALS, TOKEN_ID_1, TOKEN_ID_2, - SLOT_1, VALUE, ZERO, OWNER, OPERATOR -}; - -// Settings - -fn deploy_account( - class_hash: starknet::class_hash::ClassHash, public_key: felt252 -) -> ContractAddress { - let calldata: Array = array![public_key]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); - address -} - -fn setup() -> (ERC3525ComponentState, ContractAddress) { - let mut state = COMPONENT_STATE(); - let class_hash = Account::TEST_CLASS_HASH.try_into().unwrap(); - let receiver = deploy_account(class_hash, 'RECEIVER'); - state.initializer(VALUE_DECIMALS); - (state, receiver) -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_to_address() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state.transfer_value_from(TOKEN_ID_1, 0, receiver, VALUE); -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_to_address_approved_can_transfer() { - let (mut state, receiver) = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - // ERC721 setup - mock_state.erc721.approve(OPERATOR(), TOKEN_ID_1); - // ERC3525 - set_caller_address(OPERATOR()); - state.transfer_value_from(TOKEN_ID_1, 0, receiver, VALUE); -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_to_address_value_approved_can_transfer() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, 2 * VALUE); - state.approve_value(TOKEN_ID_1, OPERATOR(), 2 * VALUE); - // // ERC3525 - let initial_allowance = state.allowance(TOKEN_ID_1, OPERATOR()); - set_caller_address(OPERATOR()); - state.transfer_value_from(TOKEN_ID_1, 0, receiver, VALUE); - let final_allowance = state.allowance(TOKEN_ID_1, OPERATOR()); - assert(initial_allowance == VALUE + final_allowance, 'Wrong allowance'); -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_to_address_unlimited_value_approved_can_transfer() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state.approve_value(TOKEN_ID_1, OPERATOR(), BoundedInt::max()); - // // ERC3525 - set_caller_address(OPERATOR()); - state.transfer_value_from(TOKEN_ID_1, 0, receiver, VALUE); - let final_allowance = state.allowance(TOKEN_ID_1, OPERATOR()); - assert(final_allowance == BoundedInt::max(), 'Wrong allowance'); -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_to_address_approved_for_all_can_transfer() { - let (mut state, receiver) = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - // ERC721 setup - mock_state.erc721.set_approval_for_all(OPERATOR(), true); - // // ERC3525 - set_caller_address(OPERATOR()); - state.transfer_value_from(TOKEN_ID_1, 0, receiver, VALUE); -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_to_address_owner_can_transfer_value_to_himself() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(receiver, TOKEN_ID_1, SLOT_1, VALUE); - // // ERC3525 - set_caller_address(receiver); - state.transfer_value_from(TOKEN_ID_1, 0, receiver, VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: insufficient allowance',))] -fn test_transfer_to_address_not_approved_cannot_transfer_value() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - // // ERC3525 - set_caller_address(OPERATOR()); - state.transfer_value_from(TOKEN_ID_1, 0, receiver, VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: value exceeds balance',))] -fn test_transfer_to_address_revert_value_exceeds_balance() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - // // ERC3525 - state.transfer_value_from(TOKEN_ID_1, 0, receiver, VALUE + 1); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: insufficient allowance',))] -fn test_transfer_to_address_revert_insufficient_allowance() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE - 1); - // // ERC3525 - set_caller_address(OPERATOR()); - state.transfer_value_from(TOKEN_ID_1, 0, receiver, VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: invalid from token id',))] -fn test_transfer_to_address_revert_invalid_from_token_id() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state.transfer_value_from(0, 0, receiver, VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: mutually excl args set',))] -fn test_transfer_to_address_revert_both_unset() { - let (mut state, _) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state.transfer_value_from(TOKEN_ID_1, 0, ZERO(), VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: mutually excl args set',))] -fn test_transfer_to_address_revert_both_set() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state.transfer_value_from(TOKEN_ID_1, TOKEN_ID_2, receiver, VALUE); -} diff --git a/src/tests/unit/test_transfer_to_token.cairo b/src/tests/unit/test_transfer_to_token.cairo deleted file mode 100644 index f950dfe..0000000 --- a/src/tests/unit/test_transfer_to_token.cairo +++ /dev/null @@ -1,181 +0,0 @@ -// Core imports - -use integer::BoundedInt; -use debug::PrintTrait; - -// Starknet imports - -use starknet::ContractAddress; -use starknet::testing::set_caller_address; - -// External imports - -use openzeppelin::token::erc721::erc721::ERC721Component::{ - ERC721Impl, InternalImpl as ERC721InternalImpl -}; -use openzeppelin::token::erc721::erc721::ERC721Component; -use openzeppelin::presets::Account; - -// Local imports - -use cairo_erc_3525::module::ERC3525Component::{ERC3525Impl, InternalImpl}; -use cairo_erc_3525::module::ERC3525Component; -use cairo_erc_3525::tests::unit::constants::{ - ERC3525ComponentState, COMPONENT_STATE, CONTRACT_STATE, VALUE_DECIMALS, TOKEN_ID_1, TOKEN_ID_2, - SLOT_1, VALUE, ZERO, OWNER, OPERATOR -}; - -// Settings - -fn deploy_account( - class_hash: starknet::class_hash::ClassHash, public_key: felt252 -) -> ContractAddress { - let calldata: Array = array![public_key]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); - address -} - -fn setup() -> (ERC3525ComponentState, ContractAddress) { - let mut state = COMPONENT_STATE(); - let class_hash = Account::TEST_CLASS_HASH.try_into().unwrap(); - let receiver = deploy_account(class_hash, 'RECEIVER'); - state.initializer(VALUE_DECIMALS); - (state, receiver) -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_to_token() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(receiver, TOKEN_ID_2, SLOT_1, 0); - state.transfer_value_from(TOKEN_ID_1, TOKEN_ID_2, ZERO(), VALUE); - let from_value = state.value_of(TOKEN_ID_1); - assert(from_value == 0, 'Wrong value'); - let to_value = state.value_of(TOKEN_ID_2); - assert(to_value == VALUE, 'Wrong value'); -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_to_token_approved_can_transfer() { - let (mut state, receiver) = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(receiver, TOKEN_ID_2, SLOT_1, 0); - // ERC721 setup - mock_state.erc721.approve(OPERATOR(), TOKEN_ID_1); - // ERC3525 - set_caller_address(OPERATOR()); - state.transfer_value_from(TOKEN_ID_1, TOKEN_ID_2, ZERO(), VALUE); -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_to_token_value_approved_can_transfer() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(receiver, TOKEN_ID_2, SLOT_1, 0); - state.approve_value(TOKEN_ID_1, OPERATOR(), 2 * VALUE); - // // ERC3525 - let initial_allowance = state.allowance(TOKEN_ID_1, OPERATOR()); - set_caller_address(OPERATOR()); - state.transfer_value_from(TOKEN_ID_1, TOKEN_ID_2, ZERO(), VALUE); - let final_allowance = state.allowance(TOKEN_ID_1, OPERATOR()); - assert(initial_allowance == VALUE + final_allowance, 'Wrong allowance'); -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_to_token_unlimited_value_approved_can_transfer() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(receiver, TOKEN_ID_2, SLOT_1, 0); - state.approve_value(TOKEN_ID_1, OPERATOR(), BoundedInt::max()); - // // ERC3525 - set_caller_address(OPERATOR()); - state.transfer_value_from(TOKEN_ID_1, TOKEN_ID_2, ZERO(), VALUE); - let final_allowance = state.allowance(TOKEN_ID_1, OPERATOR()); - assert(final_allowance == BoundedInt::max(), 'Wrong allowance'); -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_to_token_approved_for_all_can_transfer() { - let (mut state, receiver) = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(receiver, TOKEN_ID_2, SLOT_1, 0); - // ERC721 setup - mock_state.erc721.set_approval_for_all(OPERATOR(), true); - // // ERC3525 - set_caller_address(OPERATOR()); - state.transfer_value_from(TOKEN_ID_1, TOKEN_ID_2, ZERO(), VALUE); -} - -#[test] -#[available_gas(20000000)] -fn test_transfer_to_token_owner_can_transfer_value_to_himself() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(receiver, TOKEN_ID_1, SLOT_1, VALUE); - state._mint(receiver, TOKEN_ID_2, SLOT_1, 0); - // // ERC3525 - set_caller_address(receiver); - state.transfer_value_from(TOKEN_ID_1, TOKEN_ID_2, ZERO(), VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: insufficient allowance',))] -fn test_transfer_to_token_not_approved_cannot_transfer_value() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(receiver, TOKEN_ID_2, SLOT_1, 0); - // // ERC3525 - set_caller_address(OPERATOR()); - state.transfer_value_from(TOKEN_ID_1, TOKEN_ID_2, ZERO(), VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: value exceeds balance',))] -fn test_transfer_to_token_revert_value_exceeds_balance() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(receiver, TOKEN_ID_2, SLOT_1, 0); - // // ERC3525 - state.transfer_value_from(TOKEN_ID_1, TOKEN_ID_2, ZERO(), VALUE + 1); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: insufficient allowance',))] -fn test_transfer_to_token_revert_insufficient_allowance() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(receiver, TOKEN_ID_2, SLOT_1, 0); - state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE - 1); - // // ERC3525 - set_caller_address(OPERATOR()); - state.transfer_value_from(TOKEN_ID_1, TOKEN_ID_2, ZERO(), VALUE); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: invalid from token id',))] -fn test_transfer_to_token_revert_invalid_from_token_id() { - let (mut state, receiver) = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - state._mint(receiver, TOKEN_ID_2, SLOT_1, 0); - state.transfer_value_from(0, TOKEN_ID_2, ZERO(), VALUE); -} diff --git a/src/tests/unit/test_views.cairo b/src/tests/unit/test_views.cairo deleted file mode 100644 index bdb8d46..0000000 --- a/src/tests/unit/test_views.cairo +++ /dev/null @@ -1,87 +0,0 @@ -// Core imports - -use integer::BoundedInt; -use debug::PrintTrait; - -// Starknet imports - -use starknet::ContractAddress; -use starknet::testing::set_caller_address; - -// External imports - -use openzeppelin::token::erc721::erc721::ERC721Component::{ - ERC721Impl, InternalImpl as ERC721InternalImpl -}; -use openzeppelin::token::erc721::erc721::ERC721Component; - -// Local imports - -use cairo_erc_3525::module::ERC3525Component::{ERC3525Impl, InternalImpl}; -use cairo_erc_3525::module::ERC3525Component; -use cairo_erc_3525::tests::unit::constants::{ - ERC3525ComponentState, COMPONENT_STATE, CONTRACT_STATE, VALUE_DECIMALS, TOKEN_ID_1, TOKEN_ID_2, - SLOT_1, SLOT_2, VALUE, OWNER -}; - -// Settings - -fn setup() -> ERC3525ComponentState { - let mut state = COMPONENT_STATE(); - state.initializer(VALUE_DECIMALS); - state -} - -#[test] -#[available_gas(20000000)] -fn test_value_of() { - let mut state = setup(); - let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - let value = state.value_of(TOKEN_ID_1); - assert(value == VALUE, 'Wrong value'); - // ERC721 setup - let balance = mock_state.balance_of(OWNER()); - assert(balance == 1, 'Wrong balance'); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: token not minted',))] -fn test_value_of_revert_token_not_minted() { - let mut state = setup(); - state.value_of(TOKEN_ID_1); -} - -#[test] -#[available_gas(20000000)] -fn test_slot_of() { - let mut state = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); - let slot = state.slot_of(TOKEN_ID_1); - assert(slot == SLOT_1, 'Wrong slot'); -} - -#[test] -#[available_gas(20000000)] -#[should_panic(expected: ('ERC3525: token not minted',))] -fn test_slot_of_revert_token_not_minted() { - let mut state = setup(); - set_caller_address(OWNER()); - state.slot_of(TOKEN_ID_1); -} - -#[test] -#[available_gas(20000000)] -fn test_total_value() { - let mut state = setup(); - set_caller_address(OWNER()); - state._mint(OWNER(), 1, SLOT_1, 1 * VALUE); - state._mint(OWNER(), 2, SLOT_1, 2 * VALUE); - state._mint(OWNER(), 3, SLOT_2, 3 * VALUE); - state._mint(OWNER(), 4, SLOT_2, 4 * VALUE); - assert(state._total_value(SLOT_1) == 3 * VALUE, 'Wrong total value'); - assert(state._total_value(SLOT_2) == 7 * VALUE, 'Wrong total value'); -} diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo deleted file mode 100644 index 38f29b2..0000000 --- a/src/tests/utils.cairo +++ /dev/null @@ -1,69 +0,0 @@ -use starknet::ContractAddress; -use starknet::SyscallResultTrait; -use starknet::testing; - -fn deploy(contract_class_hash: felt252, calldata: Array) -> ContractAddress { - let (address, _) = starknet::deploy_syscall( - contract_class_hash.try_into().unwrap(), 0, calldata.span(), false - ) - .unwrap_syscall(); - address -} - -fn deploy_with_salt( - contract_class_hash: felt252, calldata: Array, salt: felt252 -) -> ContractAddress { - let (address, _) = starknet::deploy_syscall( - contract_class_hash.try_into().unwrap(), salt, calldata.span(), false - ) - .unwrap_syscall(); - address -} - -/// Pop the earliest unpopped logged event enum for the contract and checks -/// there's no more keys or data left on the event, preventing unaccounted params. -/// -/// CAUTION: If the event enum contains two `flat` events with the same structure (member types), -/// this function will always match the first event, even when the second one is emitted. -fn pop_log, +starknet::Event>(address: ContractAddress) -> Option { - let (mut keys, mut data) = testing::pop_log_raw(address)?; - - let ret = starknet::Event::deserialize(ref keys, ref data); - assert!(data.is_empty(), "Event has extra data"); - assert!(keys.is_empty(), "Event has extra keys"); - ret -} - -/// Asserts that `expected_keys` exactly matches the indexed keys from `event`. -/// -/// `expected_keys` must include all indexed event keys for `event` in the order -/// that they're defined. -/// -/// If the event is not flattened, the first key will be the event member name -/// e.g. selector!("EnumMemberName"). -fn assert_indexed_keys, +starknet::Event>(event: T, expected_keys: Span) { - let mut keys = array![]; - let mut data = array![]; - - event.append_keys_and_data(ref keys, ref data); - assert!(expected_keys == keys.span()); -} - -fn assert_no_events_left(address: ContractAddress) { - assert!(testing::pop_log_raw(address).is_none(), "Events remaining on queue"); -} - -fn drop_event(address: ContractAddress) { - let _ = testing::pop_log_raw(address); -} - -fn drop_events(address: ContractAddress, count: felt252) { - let mut _count = count; - loop { - if _count == 0 { - break; - } - drop_event(address); - _count -= 1; - } -} From 188ca513510a89e5db89fbe77ada605535977c30 Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Sat, 21 Sep 2024 20:42:27 +0100 Subject: [PATCH 02/14] feat: migrate tests/integration/test_base.cairo to snforge 0.27.0 --- .tool-versions | 1 + Scarb.toml | 4 +- src/lib.cairo | 3 + src/test_helpers/account.cairo | 61 ++++++++++++ tests/integration/constants.cairo | 57 +++++++++++ tests/integration/test_base.cairo | 153 ++++++++++++++++++++++++++++++ tests/lib.cairo | 8 ++ 7 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 src/test_helpers/account.cairo create mode 100644 tests/integration/constants.cairo create mode 100644 tests/integration/test_base.cairo create mode 100644 tests/lib.cairo diff --git a/.tool-versions b/.tool-versions index 179f2a8..b6e9b54 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1,2 @@ scarb 2.6.5 +starknet-foundry 0.27.0 \ No newline at end of file diff --git a/Scarb.toml b/Scarb.toml index d516ea8..abb051d 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -14,7 +14,9 @@ snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag [[target.starknet-contract]] sierra = true casm = true -build-external-contracts = ["openzeppelin::presets::AccountUpgradeable"] + +[scripts] +test = "snforge test" [tool.snforge] exit_first = false \ No newline at end of file diff --git a/src/lib.cairo b/src/lib.cairo index 3bc67d1..a755fc5 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -22,3 +22,6 @@ mod presets { mod erc3525_mintable_burnable_metadata_slot_approvable_slot_enumerable; } +mod test_helpers { + mod account; +} diff --git a/src/test_helpers/account.cairo b/src/test_helpers/account.cairo new file mode 100644 index 0000000..09bf60f --- /dev/null +++ b/src/test_helpers/account.cairo @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.14.0 (presets/account.cairo) + +/// # Account Preset +/// +/// OpenZeppelin's upgradeable account which can change its public key and declare, deploy, or call contracts. +#[starknet::contract(account)] +pub(crate) mod AccountUpgradeable { + use openzeppelin::account::AccountComponent; + use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::upgrades::UpgradeableComponent; + use openzeppelin::upgrades::interface::IUpgradeable; + use starknet::ClassHash; + + component!(path: AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + + // Account Mixin + #[abi(embed_v0)] + pub(crate) impl AccountMixinImpl = + AccountComponent::AccountMixinImpl; + impl AccountInternalImpl = AccountComponent::InternalImpl; + + // Upgradeable + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + account: AccountComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event + } + + #[constructor] + pub(crate) fn constructor(ref self: ContractState, public_key: felt252) { + self.account.initializer(public_key); + } + + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { + self.account.assert_only_self(); + self.upgradeable.upgrade(new_class_hash); + } + } +} \ No newline at end of file diff --git a/tests/integration/constants.cairo b/tests/integration/constants.cairo new file mode 100644 index 0000000..09499e5 --- /dev/null +++ b/tests/integration/constants.cairo @@ -0,0 +1,57 @@ +use starknet::ContractAddress; +use starknet::contract_address_const; +use zeroable::Zeroable; + +// Constants + +const VALUE_DECIMALS: u8 = 6; +const TOKEN_ID_1: u256 = 1; +const TOKEN_ID_2: u256 = 2; +const INVALID_TOKEN: u256 = 666; +const SLOT_1: u256 = 'SLOT1'; +const SLOT_2: u256 = 'SLOT2'; +const VALUE: u256 = 1000; +const CONTRACT_URI: felt252 = 'CONTRACT_URI'; +const SLOT_URI: felt252 = 'SLOT_URI'; + +// Addresses + +fn ZERO() -> ContractAddress { + Zeroable::zero() +} + +fn OWNER() -> ContractAddress { + contract_address_const::<'OWNER'>() +} + +fn RECIPIENT() -> ContractAddress { + contract_address_const::<'RECIPIENT'>() +} + +fn SPENDER() -> ContractAddress { + contract_address_const::<'SPENDER'>() +} + +fn OPERATOR() -> ContractAddress { + contract_address_const::<'OPERATOR'>() +} + +fn SOMEONE() -> ContractAddress { + contract_address_const::<'SOMEONE'>() +} + +fn ANYONE() -> ContractAddress { + contract_address_const::<'ANYONE'>() +} + +fn NAME() -> ByteArray { + "NAME" +} + +fn SYMBOL() -> ByteArray { + "SYMBOL" +} + +fn BASE_URI() -> ByteArray { + "BASE_URI" +} diff --git a/tests/integration/test_base.cairo b/tests/integration/test_base.cairo new file mode 100644 index 0000000..8b35f4b --- /dev/null +++ b/tests/integration/test_base.cairo @@ -0,0 +1,153 @@ +// Core deps +use result::ResultTrait; +use option::OptionTrait; +use array::ArrayTrait; +use traits::{Into, TryInto}; + +// snforge deps +use snforge_std::{ + declare, ContractClassTrait, start_cheat_caller_address, stop_cheat_caller_address +}; + +// Starknet deps +use starknet::{ContractAddress}; + +// External deps +use openzeppelin::introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}; +use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait, IERC721_ID}; + +// Local deps +use cairo_erc_3525::interface::{IERC3525Dispatcher, IERC3525DispatcherTrait, IERC3525_ID}; +use cairo_erc_3525::presets::erc3525_mintable_burnable::{ + ERC3525MintableBurnable, IExternalDispatcher, IExternalDispatcherTrait +}; +use super::constants; + + +#[derive(Drop)] +struct Signers { + owner: ContractAddress, + someone: ContractAddress, + anyone: ContractAddress, + operator: ContractAddress, +} + +fn deploy_account( + class_hash: snforge_std::cheatcodes::contract_class::ContractClass, public_key: felt252 +) -> ContractAddress { + let calldata: Array = array![public_key]; + let (address, _) = class_hash.deploy(@calldata).unwrap(); + address +} + +fn __setup__() -> (ContractAddress, Signers) { + let ERC3525MintableBurnable_class_hash = declare("ERC3525MintableBurnable").unwrap(); + let mut ERC3525MintableBurnable_constructor_calldata = ArrayTrait::new(); + let value_decimals: u8 = 6; + value_decimals.serialize(ref ERC3525MintableBurnable_constructor_calldata); + let (contract_address, _) = ERC3525MintableBurnable_class_hash + .deploy(@ERC3525MintableBurnable_constructor_calldata) + .unwrap(); + + let AccountUpgradeable_class_hash = declare("AccountUpgradeable").unwrap(); + let signer = Signers { + owner: deploy_account(AccountUpgradeable_class_hash, 'OWNER'), + someone: deploy_account(AccountUpgradeable_class_hash, 'SOMEONE'), + anyone: deploy_account(AccountUpgradeable_class_hash, 'ANYONE'), + operator: deploy_account(AccountUpgradeable_class_hash, 'OPERATOR'), + }; + (contract_address, signer) +} + +#[test] +#[available_gas(100_000_000)] +fn test_integration_supports_interface() { + // Setup + let (contract_address, _) = __setup__(); + let src5 = ISRC5Dispatcher { contract_address }; + assert(src5.supports_interface(ISRC5_ID), 'ISRC5 not supported'); + assert(src5.supports_interface(IERC721_ID), 'IERC721 not supported'); + assert(src5.supports_interface(IERC3525_ID), 'IERC3525 not supported'); +} + +#[test] +#[available_gas(100_000_000)] +fn test_integration_base_scenario() { + // Setup + let (contract_address, signers) = __setup__(); + let external = IExternalDispatcher { contract_address }; + let erc3525 = IERC3525Dispatcher { contract_address }; + let erc721 = IERC721Dispatcher { contract_address }; + + // Mint tokens + let one = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); + let two = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); + let three = external.mint(signers.someone, constants::SLOT_1, constants::VALUE); + let _four = external.mint(signers.anyone, constants::SLOT_1, constants::VALUE); + let _five = external.mint(signers.operator, constants::SLOT_1, constants::VALUE); + let six = external.mint(signers.owner, constants::SLOT_2, constants::VALUE); + let seven = external.mint(signers.someone, constants::SLOT_2, constants::VALUE); + let height = external.mint(signers.someone, constants::SLOT_2, constants::VALUE); + let nine = external.mint(signers.anyone, constants::SLOT_2, constants::VALUE); + + // Asserts + assert(erc3525.value_of(one) == constants::VALUE, 'Wrong value'); + assert(erc721.owner_of(one) == signers.owner, 'Wrong owner'); + + // Approve + start_cheat_caller_address(contract_address, signers.owner); + erc721.approve(signers.owner, one); + erc721.approve(signers.owner, two); + erc721.approve(signers.owner, six); + erc3525.approve_value(one, signers.anyone, constants::VALUE / 2); + erc3525.approve_value(two, signers.anyone, constants::VALUE / 2); + erc3525.approve_value(six, signers.anyone, constants::VALUE / 2); + stop_cheat_caller_address(signers.owner); + + // Transfers to token id + start_cheat_caller_address(contract_address, signers.anyone); + erc3525.transfer_value_from(one, three, constants::ZERO(), 1); + erc3525.transfer_value_from(two, three, constants::ZERO(), 1); + erc3525.transfer_value_from(six, seven, constants::ZERO(), 1); + stop_cheat_caller_address(signers.anyone); + + // Transfer to + let ten = erc3525.transfer_value_from(one, 0, signers.operator, 1); + assert(ten == 10, 'Wrong id'); + assert(erc3525.allowance(one, signers.anyone) == constants::VALUE / 2 - 2, 'Wrong allowance'); + assert(erc3525.value_of(one) == constants::VALUE - 2, 'Wrong value'); + assert(external.total_value(constants::SLOT_1) == 5 * constants::VALUE, 'Wrong total value'); + assert(external.total_value(constants::SLOT_2) == 4 * constants::VALUE, 'Wrong total value'); + + // Burn value + start_cheat_caller_address(contract_address, signers.owner); + external.burn_value(one, 3); + stop_cheat_caller_address(signers.owner); + + start_cheat_caller_address(contract_address, signers.someone); + external.burn_value(height, 2); + stop_cheat_caller_address(signers.someone); + + start_cheat_caller_address(contract_address, signers.anyone); + external.burn_value(nine, 1); + assert(erc3525.allowance(one, signers.anyone) == constants::VALUE / 2 - 2, 'Wrong allowance'); + assert(erc3525.value_of(one) == constants::VALUE - 2 - 3, 'Wrong value'); + assert( + external.total_value(constants::SLOT_1) == 5 * constants::VALUE - 3, 'Wrong total value' + ); + assert( + external.total_value(constants::SLOT_2) == 4 * constants::VALUE - 3, 'Wrong total value' + ); + stop_cheat_caller_address(signers.anyone); + + // Burn token + start_cheat_caller_address(contract_address, signers.owner); + external.burn(one); + assert( + external.total_value(constants::SLOT_1) == 4 * constants::VALUE + 2, 'Wrong total value' + ); + assert( + external.total_value(constants::SLOT_2) == 4 * constants::VALUE - 3, 'Wrong total value' + ); + stop_cheat_caller_address(signers.owner); +} diff --git a/tests/lib.cairo b/tests/lib.cairo new file mode 100644 index 0000000..bfe1591 --- /dev/null +++ b/tests/lib.cairo @@ -0,0 +1,8 @@ +mod integration { + mod constants; + mod test_base; +// mod test_metadata; +// mod test_receiver; +// mod test_slot_approvable; +// mod test_slot_enumerable; +} From 22d09fe7afd556643f15e2e0ecce5bf56343ee4e Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Sat, 21 Sep 2024 20:57:36 +0100 Subject: [PATCH 03/14] feat: migrate tests/integration/test_metadata.cairo to snforge 0.27.0 --- tests/integration/test_metadata.cairo | 106 ++++++++++++++++++++++++++ tests/lib.cairo | 2 +- 2 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 tests/integration/test_metadata.cairo diff --git a/tests/integration/test_metadata.cairo b/tests/integration/test_metadata.cairo new file mode 100644 index 0000000..f265069 --- /dev/null +++ b/tests/integration/test_metadata.cairo @@ -0,0 +1,106 @@ +// Core deps +use result::ResultTrait; +use option::OptionTrait; +use array::ArrayTrait; +use traits::{Into, TryInto}; + +// snforge deps +use snforge_std::{ + declare, ContractClassTrait, start_cheat_caller_address, stop_cheat_caller_address +}; + +// Starknet deps +use starknet::{ContractAddress}; + +// External deps +use openzeppelin::introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}; +use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait, IERC721_ID}; + +// Local deps +use cairo_erc_3525::interface::{IERC3525Dispatcher, IERC3525DispatcherTrait, IERC3525_ID}; +use cairo_erc_3525::presets::erc3525_mintable_burnable_metadata::{ + ERC3525MintableBurnableMetadata, IExternalDispatcher, IExternalDispatcherTrait +}; +use cairo_erc_3525::extensions::metadata::interface::{ + IERC3525MetadataDispatcher, IERC3525MetadataDispatcherTrait, IERC3525_METADATA_ID +}; +use super::constants; + +#[derive(Drop)] +struct Signers { + owner: ContractAddress, + someone: ContractAddress, + anyone: ContractAddress, + operator: ContractAddress, +} + +fn deploy_account( + class_hash: snforge_std::cheatcodes::contract_class::ContractClass, public_key: felt252 +) -> ContractAddress { + let calldata: Array = array![public_key]; + let (address, _) = class_hash.deploy(@calldata).unwrap(); + address +} + +fn __setup__() -> (ContractAddress, Signers) { + let ERC3525MintableBurnableMetadata_class_hash = declare("ERC3525MintableBurnableMetadata") + .unwrap(); + let mut ERC3525MintableBurnableMetadata_constructor_calldata = ArrayTrait::new(); + constants::NAME().serialize(ref ERC3525MintableBurnableMetadata_constructor_calldata); + constants::SYMBOL().serialize(ref ERC3525MintableBurnableMetadata_constructor_calldata); + constants::BASE_URI().serialize(ref ERC3525MintableBurnableMetadata_constructor_calldata); + constants::VALUE_DECIMALS.serialize(ref ERC3525MintableBurnableMetadata_constructor_calldata); + let (contract_address, _) = ERC3525MintableBurnableMetadata_class_hash + .deploy(@ERC3525MintableBurnableMetadata_constructor_calldata) + .unwrap(); + + let AccountUpgradeable_class_hash = declare("AccountUpgradeable").unwrap(); + let signer = Signers { + owner: deploy_account(AccountUpgradeable_class_hash, 'OWNER'), + someone: deploy_account(AccountUpgradeable_class_hash, 'SOMEONE'), + anyone: deploy_account(AccountUpgradeable_class_hash, 'ANYONE'), + operator: deploy_account(AccountUpgradeable_class_hash, 'OPERATOR'), + }; + (contract_address, signer) +} + +#[test] +#[available_gas(100_000_000)] +fn test_integration_metadata_supports_interface() { + // Setup + let (contract_address, _) = __setup__(); + let src5 = ISRC5Dispatcher { contract_address }; + assert(src5.supports_interface(ISRC5_ID), 'ISRC5 not supported'); + assert(src5.supports_interface(IERC721_ID), 'IERC721 not supported'); + assert(src5.supports_interface(IERC3525_ID), 'IERC3525 not supported'); + assert(src5.supports_interface(IERC3525_METADATA_ID), 'IMetadata not supported'); +} + +#[test] +#[available_gas(100_000_000)] +fn test_integration_metadata_scenario() { + // Setup + let (contract_address, signers) = __setup__(); + let external = IExternalDispatcher { contract_address }; + let _erc3525 = IERC3525Dispatcher { contract_address }; + let erc3525_metadata = IERC3525MetadataDispatcher { contract_address }; + let _erc721 = IERC721Dispatcher { contract_address }; + + // Mint tokens + let _one = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); + let _two = external.mint(signers.operator, constants::SLOT_1, constants::VALUE); + + // Assert metadata + assert(erc3525_metadata.contract_uri() == 0, 'Wrong contract uri'); + assert(erc3525_metadata.slot_uri(constants::SLOT_1) == 0, 'Wrong slot uri'); + assert(erc3525_metadata.slot_uri(constants::SLOT_2) == 0, 'Wrong slot uri'); + + // Set metadta + external.set_contract_uri(constants::CONTRACT_URI); + external.set_slot_uri(constants::SLOT_1, constants::SLOT_URI); + + // Assert metadata + assert(erc3525_metadata.contract_uri() == constants::CONTRACT_URI, 'Wrong contract uri'); + assert(erc3525_metadata.slot_uri(constants::SLOT_1) == constants::SLOT_URI, 'Wrong slot uri'); + assert(erc3525_metadata.slot_uri(constants::SLOT_2) == 0, 'Wrong slot uri'); +} diff --git a/tests/lib.cairo b/tests/lib.cairo index bfe1591..d4a7b7d 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -1,7 +1,7 @@ mod integration { mod constants; mod test_base; -// mod test_metadata; + mod test_metadata; // mod test_receiver; // mod test_slot_approvable; // mod test_slot_enumerable; From bf53e87cbf4b37abb40047849797daae2685134e Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Mon, 23 Sep 2024 09:45:28 +0100 Subject: [PATCH 04/14] feat: migrate tests/integration/test_slot_approvable.cairo to snforge 0.27.0 --- tests/integration/test_slot_approvable.cairo | 99 ++++++++++++++++++++ tests/lib.cairo | 4 +- 2 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 tests/integration/test_slot_approvable.cairo diff --git a/tests/integration/test_slot_approvable.cairo b/tests/integration/test_slot_approvable.cairo new file mode 100644 index 0000000..bdf29ff --- /dev/null +++ b/tests/integration/test_slot_approvable.cairo @@ -0,0 +1,99 @@ +// Core deps +use result::ResultTrait; +use option::OptionTrait; +use array::ArrayTrait; +use traits::{Into, TryInto}; + +// snforge deps +use snforge_std::{ + declare, ContractClassTrait, start_cheat_caller_address, stop_cheat_caller_address +}; + +// Starknet deps +use starknet::{ContractAddress}; + +// External deps +use openzeppelin::introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}; +use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait, IERC721_ID}; + +// Local deps +use cairo_erc_3525::interface::{IERC3525Dispatcher, IERC3525DispatcherTrait, IERC3525_ID}; +use cairo_erc_3525::presets::erc3525_mintable_burnable_metadata_slot_approvable::{ + ERC3525MintableBurnableMSA, IExternalDispatcher, IExternalDispatcherTrait +}; +use cairo_erc_3525::extensions::slotapprovable::interface::{ + IERC3525SlotApprovableDispatcher, IERC3525SlotApprovableDispatcherTrait, + IERC3525_SLOT_APPROVABLE_ID +}; +use super::constants; + +#[derive(Drop)] +struct Signers { + owner: ContractAddress, + operator: ContractAddress, +} + +fn deploy_account( + class_hash: snforge_std::cheatcodes::contract_class::ContractClass, public_key: felt252 +) -> ContractAddress { + let calldata: Array = array![public_key]; + let (address, _) = class_hash.deploy(@calldata).unwrap(); + address +} + +fn __setup__() -> (ContractAddress, Signers) { + let ERC3525MintableBurnableMSA_class_hash = declare("ERC3525MintableBurnableMSA").unwrap(); + let mut ERC3525MintableBurnableMSA_constructor_calldata = ArrayTrait::new(); + constants::NAME().serialize(ref ERC3525MintableBurnableMSA_constructor_calldata); + constants::SYMBOL().serialize(ref ERC3525MintableBurnableMSA_constructor_calldata); + constants::BASE_URI().serialize(ref ERC3525MintableBurnableMSA_constructor_calldata); + constants::VALUE_DECIMALS.serialize(ref ERC3525MintableBurnableMSA_constructor_calldata); + let (contract_address, _) = ERC3525MintableBurnableMSA_class_hash + .deploy(@ERC3525MintableBurnableMSA_constructor_calldata) + .unwrap(); + + let AccountUpgradeable_class_hash = declare("AccountUpgradeable").unwrap(); + let signer = Signers { + owner: deploy_account(AccountUpgradeable_class_hash, 'OWNER'), + operator: deploy_account(AccountUpgradeable_class_hash, 'OPERATOR'), + }; + (contract_address, signer) +} + +#[test] +#[available_gas(100_000_000)] +fn test_integration_slot_approvable_supports_interface() { + // Setup + let (contract_address, _) = __setup__(); + let src5 = ISRC5Dispatcher { contract_address }; + assert(src5.supports_interface(ISRC5_ID), 'ISRC5 not supported'); + assert(src5.supports_interface(IERC721_ID), 'IERC721 not supported'); + assert(src5.supports_interface(IERC3525_ID), 'IERC3525 not supported'); + assert(src5.supports_interface(IERC3525_SLOT_APPROVABLE_ID), 'ISlotApprovable not supported'); +} + +#[test] +#[available_gas(100_000_000)] +fn test_integration_slot_aprovable_scenario() { + // Setup + let (contract_address, signers) = __setup__(); + let external = IExternalDispatcher { contract_address }; + let erc3525 = IERC3525Dispatcher { contract_address }; + let erc3525_sa = IERC3525SlotApprovableDispatcher { contract_address }; + let _erc721 = IERC721Dispatcher { contract_address }; + + // Mint tokens + let one = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); + let two = external.mint(signers.operator, constants::SLOT_1, constants::VALUE); + + // Slot approvals + start_cheat_caller_address(contract_address, signers.owner); + erc3525_sa.set_approval_for_slot(signers.owner, constants::SLOT_1, signers.operator, true); + stop_cheat_caller_address(signers.owner); + + // Transfer value + start_cheat_caller_address(contract_address, signers.operator); + erc3525.transfer_value_from(one, two, constants::ZERO(), 1); + stop_cheat_caller_address(signers.operator) +} + diff --git a/tests/lib.cairo b/tests/lib.cairo index d4a7b7d..cbacf4f 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -2,7 +2,7 @@ mod integration { mod constants; mod test_base; mod test_metadata; -// mod test_receiver; -// mod test_slot_approvable; + // mod test_receiver; + mod test_slot_approvable; // mod test_slot_enumerable; } From ddc4c5f33d59c1b4ff5a2548c02db75740eb0940 Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Mon, 23 Sep 2024 10:20:55 +0100 Subject: [PATCH 05/14] feat: migrate tests/integration/test_slot_enumerable.cairo to snforge 0.27.0 --- tests/integration/test_slot_enumerable.cairo | 173 +++++++++++++++++++ tests/lib.cairo | 2 +- 2 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 tests/integration/test_slot_enumerable.cairo diff --git a/tests/integration/test_slot_enumerable.cairo b/tests/integration/test_slot_enumerable.cairo new file mode 100644 index 0000000..b241e70 --- /dev/null +++ b/tests/integration/test_slot_enumerable.cairo @@ -0,0 +1,173 @@ +// Core deps +use result::ResultTrait; +use option::OptionTrait; +use array::ArrayTrait; +use traits::{Into, TryInto}; + +// snforge deps +use snforge_std::{ + declare, ContractClassTrait, start_cheat_caller_address, stop_cheat_caller_address +}; + +// Starknet deps +use starknet::{ContractAddress}; + +// External deps +use openzeppelin::introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}; +use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait, IERC721_ID}; + +// Local deps +use cairo_erc_3525::interface::{IERC3525Dispatcher, IERC3525DispatcherTrait, IERC3525_ID}; +use cairo_erc_3525::presets::erc3525_mintable_burnable_metadata_slot_approvable_slot_enumerable::{ + ERC3525MintableBurnableMSASE, IExternalDispatcher, IExternalDispatcherTrait +}; +use cairo_erc_3525::extensions::slotenumerable::interface::{ + IERC3525SlotEnumerableDispatcher, IERC3525SlotEnumerableDispatcherTrait, + IERC3525_SLOT_ENUMERABLE_ID +}; +use super::constants; + +use debug::PrintTrait; + +#[derive(Drop)] +struct Signers { + owner: ContractAddress, + someone: ContractAddress, + anyone: ContractAddress, + operator: ContractAddress, +} + +fn deploy_account( + class_hash: snforge_std::cheatcodes::contract_class::ContractClass, public_key: felt252 +) -> ContractAddress { + let calldata: Array = array![public_key]; + let (address, _) = class_hash.deploy(@calldata).unwrap(); + address +} + +fn __setup__() -> (ContractAddress, Signers) { + let ERC3525MintableBurnableMSASE_class_hash = declare("ERC3525MintableBurnableMSASE").unwrap(); + let mut ERC3525MintableBurnableMSASE_constructor_calldata = ArrayTrait::new(); + constants::NAME().serialize(ref ERC3525MintableBurnableMSASE_constructor_calldata); + constants::SYMBOL().serialize(ref ERC3525MintableBurnableMSASE_constructor_calldata); + constants::BASE_URI().serialize(ref ERC3525MintableBurnableMSASE_constructor_calldata); + constants::VALUE_DECIMALS.serialize(ref ERC3525MintableBurnableMSASE_constructor_calldata); + let (contract_address, _) = ERC3525MintableBurnableMSASE_class_hash + .deploy(@ERC3525MintableBurnableMSASE_constructor_calldata) + .unwrap(); + + let AccountUpgradeable_class_hash = declare("AccountUpgradeable").unwrap(); + let signer = Signers { + owner: deploy_account(AccountUpgradeable_class_hash, 'OWNER'), + someone: deploy_account(AccountUpgradeable_class_hash, 'SOMEONE'), + anyone: deploy_account(AccountUpgradeable_class_hash, 'ANYONE'), + operator: deploy_account(AccountUpgradeable_class_hash, 'OPERATOR'), + }; + (contract_address, signer) +} + +#[test] +#[available_gas(100_000_000)] +fn test_integration_slot_enumerable_supports_interface() { + // Setup + let (contract_address, _) = __setup__(); + let src5 = ISRC5Dispatcher { contract_address }; + assert(src5.supports_interface(ISRC5_ID), 'ISRC5 not supported'); + assert(src5.supports_interface(IERC721_ID), 'IERC721 not supported'); + assert(src5.supports_interface(IERC3525_ID), 'IERC3525 not supported'); + assert(src5.supports_interface(IERC3525_SLOT_ENUMERABLE_ID), 'ISlotEnumerable not supported'); +} +#[test] +#[available_gas(100_000_000)] +fn test_integration_slot_enumerable_scenario() { + // Setup + let (contract_address, signers) = __setup__(); + let external = IExternalDispatcher { contract_address }; + let erc3525 = IERC3525Dispatcher { contract_address }; + let erc3525_se = IERC3525SlotEnumerableDispatcher { contract_address }; + let erc721 = IERC721Dispatcher { contract_address }; + + // Mint tokens + let one = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); + let two = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); + let three = external.mint(signers.someone, constants::SLOT_1, constants::VALUE); + let four = external.mint(signers.anyone, constants::SLOT_1, constants::VALUE); + let five = external.mint(signers.operator, constants::SLOT_1, constants::VALUE); + + // Assert enumerable + assert(erc3525_se.slot_count() == 1, 'Wrong slot count'); + assert(erc3525_se.slot_by_index(0) == constants::SLOT_1, 'Wrong slot at index'); + assert(erc3525_se.token_supply_in_slot(constants::SLOT_1) == 5, 'Wrong toke supply in slot'); + assert( + erc3525_se.token_in_slot_by_index(constants::SLOT_1, 0) == one, + 'Wrong token in slot at index' + ); + assert( + erc3525_se.token_in_slot_by_index(constants::SLOT_1, 1) == two, + 'Wrong token in slot at index' + ); + assert( + erc3525_se.token_in_slot_by_index(constants::SLOT_1, 2) == three, + 'Wrong token in slot at index' + ); + assert( + erc3525_se.token_in_slot_by_index(constants::SLOT_1, 3) == four, + 'Wrong token in slot at index' + ); + assert( + erc3525_se.token_in_slot_by_index(constants::SLOT_1, 4) == five, + 'Wrong token in slot at index' + ); + + // Mint tokens + let six = external.mint(signers.owner, constants::SLOT_2, constants::VALUE); + let seven = external.mint(signers.someone, constants::SLOT_2, constants::VALUE); + let height = external.mint(signers.someone, constants::SLOT_2, constants::VALUE); + let nine = external.mint(signers.anyone, constants::SLOT_2, constants::VALUE); + + // Assert enumerable + assert(erc3525_se.slot_count() == 2, 'Wrong slot count'); + assert(erc3525_se.slot_by_index(1) == constants::SLOT_2, 'Wrong slot at index'); + assert(erc3525_se.token_supply_in_slot(constants::SLOT_2) == 4, 'Wrong toke supply in slot'); + assert( + erc3525_se.token_in_slot_by_index(constants::SLOT_2, 0) == six, + 'Wrong token in slot at index' + ); + assert( + erc3525_se.token_in_slot_by_index(constants::SLOT_2, 1) == seven, + 'Wrong token in slot at index' + ); + assert( + erc3525_se.token_in_slot_by_index(constants::SLOT_2, 2) == height, + 'Wrong token in slot at index' + ); + assert( + erc3525_se.token_in_slot_by_index(constants::SLOT_2, 3) == nine, + 'Wrong token in slot at index' + ); + + // Approve + start_cheat_caller_address(contract_address, signers.owner); + erc721.approve(signers.owner, one); + erc721.approve(signers.owner, two); + erc721.approve(signers.owner, six); + erc3525.approve_value(one, signers.anyone, constants::VALUE / 2); + erc3525.approve_value(two, signers.anyone, constants::VALUE / 2); + erc3525.approve_value(six, signers.anyone, constants::VALUE / 2); + stop_cheat_caller_address(signers.owner); + + // Transfers to token id + start_cheat_caller_address(contract_address, signers.anyone); + erc3525.transfer_value_from(one, three, constants::ZERO(), 1); + erc3525.transfer_value_from(two, three, constants::ZERO(), 1); + erc3525.transfer_value_from(six, seven, constants::ZERO(), 1); + stop_cheat_caller_address(signers.anyone); + + // Transfer to + let ten = erc3525.transfer_value_from(one, 0, signers.operator, 1); + assert(ten == 10, 'Wrong id'); + assert(erc3525.allowance(one, signers.anyone) == constants::VALUE / 2 - 2, 'Wrong allowance'); + assert(erc3525.value_of(one) == constants::VALUE - 2, 'Wrong value'); + assert(external.total_value(constants::SLOT_1) == 5 * constants::VALUE, 'Wrong total value'); + assert(external.total_value(constants::SLOT_2) == 4 * constants::VALUE, 'Wrong total value'); +} diff --git a/tests/lib.cairo b/tests/lib.cairo index cbacf4f..d8a762f 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -4,5 +4,5 @@ mod integration { mod test_metadata; // mod test_receiver; mod test_slot_approvable; -// mod test_slot_enumerable; + mod test_slot_enumerable; } From e6b47b311b8282f2d81bf1847f68fcbf1485988d Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Mon, 23 Sep 2024 11:18:08 +0100 Subject: [PATCH 06/14] feat: migrate tests/integration/test_receiver.cairo to snforge 0.27.0 --- src/lib.cairo | 2 + src/test_helpers/contracts.cairo | 331 ++++++++++++++++++++++++++ src/test_helpers/receiver.cairo | 81 +++++++ tests/integration/test_receiver.cairo | 94 ++++++++ tests/lib.cairo | 2 +- 5 files changed, 509 insertions(+), 1 deletion(-) create mode 100644 src/test_helpers/contracts.cairo create mode 100644 src/test_helpers/receiver.cairo create mode 100644 tests/integration/test_receiver.cairo diff --git a/src/lib.cairo b/src/lib.cairo index a755fc5..da440ca 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -24,4 +24,6 @@ mod presets { mod test_helpers { mod account; + mod contracts; + mod receiver; } diff --git a/src/test_helpers/contracts.cairo b/src/test_helpers/contracts.cairo new file mode 100644 index 0000000..0317dac --- /dev/null +++ b/src/test_helpers/contracts.cairo @@ -0,0 +1,331 @@ +#[starknet::contract] +mod DualCaseERC3525Mock { + use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; + use cairo_erc_3525::module::ERC3525Component; + use starknet::ContractAddress; + + component!(path: ERC721Component, storage: erc721, event: ERC721Event); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: ERC3525Component, storage: erc3525, event: ERC3525Event); + + // ERC721 + #[abi(embed_v0)] + impl ERC721Impl = ERC721Component::ERC721Impl; + #[abi(embed_v0)] + impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; + #[abi(embed_v0)] + impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl; + #[abi(embed_v0)] + impl ERC721MetadataCamelOnly = + ERC721Component::ERC721MetadataCamelOnlyImpl; + impl ERC721InternalImpl = ERC721Component::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + // ERC3525 + #[abi(embed_v0)] + impl ERC3525Impl = ERC3525Component::ERC3525Impl; + impl ERC3525InternalImpl = ERC3525Component::InternalImpl; + impl ERC3525AssertImpl = ERC3525Component::AssertImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc721: ERC721Component::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage, + #[substorage(v0)] + erc3525: ERC3525Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC721Event: ERC721Component::Event, + #[flat] + SRC5Event: SRC5Component::Event, + #[flat] + ERC3525Event: ERC3525Component::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + name: ByteArray, + symbol: ByteArray, + value_decimals: u8, + base_uri: ByteArray, + recipient: ContractAddress, + ) { + self.erc3525.initializer(value_decimals); + self.erc721.initializer(name, symbol, base_uri); + } +} + +#[starknet::contract] +mod DualCaseERC3525MetadataMock { + use cairo_erc_3525::extensions::metadata::module::ERC3525MetadataComponent; + use cairo_erc_3525::module::ERC3525Component; + use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; + use starknet::ContractAddress; + + component!(path: ERC721Component, storage: erc721, event: ERC721Event); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: ERC3525Component, storage: erc3525, event: ERC3525Event); + component!( + path: ERC3525MetadataComponent, storage: erc3525_metadata, event: ERC3525MetadataEvent + ); + + // ERC721 + #[abi(embed_v0)] + impl ERC721Impl = ERC721Component::ERC721Impl; + #[abi(embed_v0)] + impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; + #[abi(embed_v0)] + impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl; + #[abi(embed_v0)] + impl ERC721MetadataCamelOnly = + ERC721Component::ERC721MetadataCamelOnlyImpl; + impl ERC721InternalImpl = ERC721Component::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + // ERC3525 + #[abi(embed_v0)] + impl ERC3525Impl = ERC3525Component::ERC3525Impl; + impl ERC3525InternalImpl = ERC3525Component::InternalImpl; + impl ERC3525AssertImpl = ERC3525Component::AssertImpl; + + // ERC3525Metadata + #[abi(embed_v0)] + impl ERC3525MetadataImpl = + ERC3525MetadataComponent::ERC3525MetadataImpl; + #[abi(embed_v0)] + impl ERC3525MetadataCamelOnlyImpl = + ERC3525MetadataComponent::ERC3525MetadataCamelOnlyImpl; + impl ERC3525MetadataInternalImpl = ERC3525MetadataComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc721: ERC721Component::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage, + #[substorage(v0)] + erc3525: ERC3525Component::Storage, + #[substorage(v0)] + erc3525_metadata: ERC3525MetadataComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC721Event: ERC721Component::Event, + #[flat] + SRC5Event: SRC5Component::Event, + #[flat] + ERC3525Event: ERC3525Component::Event, + #[flat] + ERC3525MetadataEvent: ERC3525MetadataComponent::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + name: ByteArray, + symbol: ByteArray, + value_decimals: u8, + base_uri: ByteArray, + recipient: ContractAddress, + ) { + self.erc3525_metadata.initializer(); + self.erc3525.initializer(value_decimals); + self.erc721.initializer(name, symbol, base_uri); + } +} + +#[starknet::contract] +mod DualCaseERC3525SlotApprovableMock { + use cairo_erc_3525::extensions::slotapprovable::module::ERC3525SlotApprovableComponent; + use cairo_erc_3525::module::ERC3525Component; + use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; + use starknet::ContractAddress; + + component!(path: ERC721Component, storage: erc721, event: ERC721Event); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: ERC3525Component, storage: erc3525, event: ERC3525Event); + component!( + path: ERC3525SlotApprovableComponent, + storage: erc3525_slot_approvable, + event: ERC3525SlotApprovableEvent + ); + + // ERC721 + #[abi(embed_v0)] + impl ERC721Impl = ERC721Component::ERC721Impl; + #[abi(embed_v0)] + impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; + #[abi(embed_v0)] + impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl; + #[abi(embed_v0)] + impl ERC721MetadataCamelOnly = + ERC721Component::ERC721MetadataCamelOnlyImpl; + impl ERC721InternalImpl = ERC721Component::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + // ERC3525 + #[abi(embed_v0)] + impl ERC3525Impl = ERC3525Component::ERC3525Impl; + impl ERC3525InternalImpl = ERC3525Component::InternalImpl; + impl ERC3525AssertImpl = ERC3525Component::AssertImpl; + + // ERC3525SlotApprovable + #[abi(embed_v0)] + impl ERC3525SlotApprovableImpl = + ERC3525SlotApprovableComponent::ERC3525SlotApprovableImpl; + #[abi(embed_v0)] + impl ERC3525SlotApprovableCamelOnlyImpl = + ERC3525SlotApprovableComponent::ERC3525SlotApprovableCamelOnlyImpl; + impl ERC3525SlotApprovableInternalImpl = + ERC3525SlotApprovableComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc721: ERC721Component::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage, + #[substorage(v0)] + erc3525: ERC3525Component::Storage, + #[substorage(v0)] + erc3525_slot_approvable: ERC3525SlotApprovableComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC721Event: ERC721Component::Event, + #[flat] + SRC5Event: SRC5Component::Event, + #[flat] + ERC3525Event: ERC3525Component::Event, + #[flat] + ERC3525SlotApprovableEvent: ERC3525SlotApprovableComponent::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + name: ByteArray, + symbol: ByteArray, + value_decimals: u8, + base_uri: ByteArray, + recipient: ContractAddress, + ) { + self.erc3525_slot_approvable.initializer(); + self.erc3525.initializer(value_decimals); + self.erc721.initializer(name, symbol, base_uri); + } +} + +#[starknet::contract] +mod DualCaseERC3525SlotEnumerableMock { + use cairo_erc_3525::extensions::slotenumerable::module::ERC3525SlotEnumerableComponent; + use cairo_erc_3525::module::ERC3525Component; + use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; + use starknet::ContractAddress; + + component!(path: ERC721Component, storage: erc721, event: ERC721Event); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: ERC3525Component, storage: erc3525, event: ERC3525Event); + component!( + path: ERC3525SlotEnumerableComponent, + storage: erc3525_slot_enumerable, + event: ERC3525SlotEnumerableEvent + ); + + // ERC721 + #[abi(embed_v0)] + impl ERC721Impl = ERC721Component::ERC721Impl; + #[abi(embed_v0)] + impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; + #[abi(embed_v0)] + impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl; + #[abi(embed_v0)] + impl ERC721MetadataCamelOnly = + ERC721Component::ERC721MetadataCamelOnlyImpl; + impl ERC721InternalImpl = ERC721Component::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + // ERC3525 + #[abi(embed_v0)] + impl ERC3525Impl = ERC3525Component::ERC3525Impl; + impl ERC3525InternalImpl = ERC3525Component::InternalImpl; + impl ERC3525AssertImpl = ERC3525Component::AssertImpl; + + // ERC3525SlotEnumerable + #[abi(embed_v0)] + impl ERC3525SlotEnumerableImpl = + ERC3525SlotEnumerableComponent::ERC3525SlotEnumerableImpl; + #[abi(embed_v0)] + impl ERC3525SlotEnumerableCamelOnlyImpl = + ERC3525SlotEnumerableComponent::ERC3525SlotEnumerableCamelOnlyImpl; + impl ERC3525SlotEnumerableInternalImpl = + ERC3525SlotEnumerableComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc721: ERC721Component::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage, + #[substorage(v0)] + erc3525: ERC3525Component::Storage, + #[substorage(v0)] + erc3525_slot_enumerable: ERC3525SlotEnumerableComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC721Event: ERC721Component::Event, + #[flat] + SRC5Event: SRC5Component::Event, + #[flat] + ERC3525Event: ERC3525Component::Event, + #[flat] + ERC3525SlotEnumerableEvent: ERC3525SlotEnumerableComponent::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + name: ByteArray, + symbol: ByteArray, + value_decimals: u8, + base_uri: ByteArray, + recipient: ContractAddress, + ) { + self.erc3525_slot_enumerable.initializer(); + self.erc3525.initializer(value_decimals); + self.erc721.initializer(name, symbol, base_uri); + } +} diff --git a/src/test_helpers/receiver.cairo b/src/test_helpers/receiver.cairo new file mode 100644 index 0000000..9fb92f8 --- /dev/null +++ b/src/test_helpers/receiver.cairo @@ -0,0 +1,81 @@ +#[starknet::interface] +trait IReceiver { + fn called(self: @TContractState) -> bool; +} + +#[starknet::contract] +mod Receiver { + // Starknet deps + use starknet::ContractAddress; + + // External deps + use openzeppelin::introspection::interface::{ISRC5, ISRC5Dispatcher, ISRC5DispatcherTrait}; + use openzeppelin::introspection::src5::SRC5Component; + use cairo_erc_3525::interface::{IERC3525Receiver, IERC3525_RECEIVER_ID}; + + // Local deps + use super::IReceiver; + + // Components + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // Component implementations + impl SRC5InternalImpl = SRC5Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + src5: SRC5Component::Storage, + _called: bool + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + SRC5Event: SRC5Component::Event, + } + + + #[constructor] + fn constructor(ref self: ContractState) { + self.initializer(); + } + + #[abi(embed_v0)] + impl SRC5Impl of ISRC5 { + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + self.src5.supports_interface(interface_id) + } + } + + #[abi(embed_v0)] + impl ReceiverImpl of IReceiver { + fn called(self: @ContractState) -> bool { + self._called.read() + } + } + + #[abi(embed_v0)] + impl ERC3525ReceiverImpl of IERC3525Receiver { + fn on_erc3525_received( + ref self: ContractState, + operator: ContractAddress, + from_token_id: u256, + to_token_id: u256, + value: u256, + data: Span, + ) -> felt252 { + self._called.write(true); + IERC3525_RECEIVER_ID + } + } + + #[generate_trait] + impl InternalImpl of InternalTrait { + fn initializer(ref self: ContractState) { + // [Effect] Register interfaces + self.src5.register_interface(IERC3525_RECEIVER_ID); + } + } +} diff --git a/tests/integration/test_receiver.cairo b/tests/integration/test_receiver.cairo new file mode 100644 index 0000000..6bc0add --- /dev/null +++ b/tests/integration/test_receiver.cairo @@ -0,0 +1,94 @@ +// Core deps +use result::ResultTrait; +use option::OptionTrait; +use array::ArrayTrait; +use traits::{Into, TryInto}; + +// snforge deps +use snforge_std::{ + declare, ContractClassTrait, start_cheat_caller_address, stop_cheat_caller_address +}; + +// Starknet deps +use starknet::{ContractAddress}; + +// External deps +use openzeppelin::introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}; +use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait, IERC721_ID}; + +// Local deps +use cairo_erc_3525::interface::{IERC3525Dispatcher, IERC3525DispatcherTrait, IERC3525_ID}; +use cairo_erc_3525::presets::erc3525_mintable_burnable::{ + ERC3525MintableBurnable, IExternalDispatcher, IExternalDispatcherTrait +}; +use super::constants; +use cairo_erc_3525::test_helpers::receiver::{ + Receiver, IReceiverDispatcher, IReceiverDispatcherTrait +}; + +#[derive(Drop)] +struct Signers { + owner: ContractAddress, +} + +fn deploy_contract(class_hash: starknet::class_hash::ClassHash) -> ContractAddress { + let calldata: Array = array![constants::VALUE_DECIMALS.into()]; + let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); + address +} + +fn deploy_account( + class_hash: snforge_std::cheatcodes::contract_class::ContractClass, public_key: felt252 +) -> ContractAddress { + let calldata: Array = array![public_key]; + let (address, _) = class_hash.deploy(@calldata).unwrap(); + address +} + +fn deploy_receiver( + class_hash: snforge_std::cheatcodes::contract_class::ContractClass +) -> ContractAddress { + let calldata: Array = array![]; + let (address, _) = class_hash.deploy(@calldata).unwrap(); + address +} + +fn __setup__() -> (ContractAddress, Signers, ContractAddress) { + let ERC3525MintableBurnable_class_hash = declare("ERC3525MintableBurnable").unwrap(); + let mut ERC3525MintableBurnable_constructor_calldata = ArrayTrait::new(); + constants::VALUE_DECIMALS.serialize(ref ERC3525MintableBurnable_constructor_calldata); + let (contract_address, _) = ERC3525MintableBurnable_class_hash + .deploy(@ERC3525MintableBurnable_constructor_calldata) + .unwrap(); + + let AccountUpgradeable_class_hash = declare("AccountUpgradeable").unwrap(); + let signer = Signers { owner: deploy_account(AccountUpgradeable_class_hash, 'OWNER') }; + + let receiver_class_hash = declare("Receiver").unwrap(); + let receiver_address = deploy_receiver(receiver_class_hash); + + (contract_address, signer, receiver_address) +} + +#[test] +#[available_gas(100_000_000)] +fn test_integration_receiver_scenario() { + // Setup + let (contract_address, signers, receiver_address) = __setup__(); + let _receiver = IReceiverDispatcher { contract_address: receiver_address }; + let external = IExternalDispatcher { contract_address }; + let _erc3525 = IERC3525Dispatcher { contract_address }; + let _erc721 = IERC721Dispatcher { contract_address }; + + // Mint tokens + let _one = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); +// // Assert receiver +// assert(receiver.called() == false, 'Wrong receiver called'); + +// // Transfer to +// testing::set_contract_address(signers.owner); +// let _ = erc3525.transfer_value_from(one, 0, receiver_address, 1); + +// // Assert receiver +// assert(receiver.called() == true, 'Wrong receiver called'); +} diff --git a/tests/lib.cairo b/tests/lib.cairo index d8a762f..5281371 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -2,7 +2,7 @@ mod integration { mod constants; mod test_base; mod test_metadata; - // mod test_receiver; + mod test_receiver; mod test_slot_approvable; mod test_slot_enumerable; } From bbe5193307d67a52e10dbb35a991afb6fc6b9504 Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Mon, 23 Sep 2024 11:21:19 +0100 Subject: [PATCH 07/14] chore: update github workflows .yml files --- .github/workflows/build.yml | 4 +++- .github/workflows/test.yml | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9408aff..ebe9d9a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,9 @@ jobs: - uses: actions/checkout@v3 - name: Set up Scarb uses: software-mansion/setup-scarb@v1 + with: + scarb-version: "2.6.5" - name: Check cairo format run: scarb fmt --check - name: Build cairo programs - run: scarb build \ No newline at end of file + run: scarb build diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c386622..7feaaac 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,6 +12,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up Scarb uses: software-mansion/setup-scarb@v1 + with: + scarb-version: "2.6.5" - name: Run cairo tests run: scarb test - \ No newline at end of file From ad46c66ad9ceef3e7701e82c1dc754d072740b04 Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Mon, 23 Sep 2024 14:51:25 +0100 Subject: [PATCH 08/14] feat: migrate tests/unit/test_approvals.cairo to snforge 0.27.0 --- tests/lib.cairo | 5 + tests/unit/constants.cairo | 103 ++++++++++++++++++ tests/unit/test_approvals.cairo | 180 ++++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 tests/unit/constants.cairo create mode 100644 tests/unit/test_approvals.cairo diff --git a/tests/lib.cairo b/tests/lib.cairo index 5281371..9028874 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -6,3 +6,8 @@ mod integration { mod test_slot_approvable; mod test_slot_enumerable; } + +mod unit { + mod constants; + mod test_approvals; +} diff --git a/tests/unit/constants.cairo b/tests/unit/constants.cairo new file mode 100644 index 0000000..8332cec --- /dev/null +++ b/tests/unit/constants.cairo @@ -0,0 +1,103 @@ +// Core imports +use zeroable::Zeroable; + +// Starknet imports +use starknet::ContractAddress; +use starknet::contract_address_const; + +// Local imports +use cairo_erc_3525::module::ERC3525Component; +use cairo_erc_3525::extensions::metadata::module::ERC3525MetadataComponent; +use cairo_erc_3525::extensions::slotapprovable::module::ERC3525SlotApprovableComponent; +use cairo_erc_3525::extensions::slotenumerable::module::ERC3525SlotEnumerableComponent; +use cairo_erc_3525::test_helpers::contracts::{ + DualCaseERC3525Mock, DualCaseERC3525MetadataMock, DualCaseERC3525SlotApprovableMock, + DualCaseERC3525SlotEnumerableMock +}; + +// Setup +pub type ERC3525ComponentState = + ERC3525Component::ComponentState; +pub type ERC3525MetadataComponentState = + ERC3525MetadataComponent::ComponentState; +pub type ERC3525SlotApprovableComponentState = + ERC3525SlotApprovableComponent::ComponentState< + DualCaseERC3525SlotApprovableMock::ContractState + >; +pub type ERC3525SlotEnumerableComponentState = + ERC3525SlotEnumerableComponent::ComponentState< + DualCaseERC3525SlotEnumerableMock::ContractState + >; + +// State +fn COMPONENT_STATE() -> ERC3525ComponentState { + ERC3525Component::component_state_for_testing() +} + +fn CONTRACT_STATE() -> DualCaseERC3525Mock::ContractState { + DualCaseERC3525Mock::contract_state_for_testing() +} + +fn COMPONENT_STATE_METADATA() -> ERC3525MetadataComponentState { + ERC3525MetadataComponent::component_state_for_testing() +} + +fn CONTRACT_STATE_METADATA() -> DualCaseERC3525MetadataMock::ContractState { + DualCaseERC3525MetadataMock::contract_state_for_testing() +} + +fn COMPONENT_STATE_SLOT_APPROVABLE() -> ERC3525SlotApprovableComponentState { + ERC3525SlotApprovableComponent::component_state_for_testing() +} + +fn CONTRACT_STATE_SLOT_APPROVABLE() -> DualCaseERC3525SlotApprovableMock::ContractState { + DualCaseERC3525SlotApprovableMock::contract_state_for_testing() +} + +fn COMPONENT_STATE_SLOT_ENUMERABLE() -> ERC3525SlotEnumerableComponentState { + ERC3525SlotEnumerableComponent::component_state_for_testing() +} + +fn CONTRACT_STATE_SLOT_ENUMERABLE() -> DualCaseERC3525SlotEnumerableMock::ContractState { + DualCaseERC3525SlotEnumerableMock::contract_state_for_testing() +} + +// Constants +const NAME: felt252 = 'NAME'; +const SYMBOL: felt252 = 'SYMBOL'; +const VALUE_DECIMALS: u8 = 6; +const TOKEN_ID_1: u256 = 1; +const TOKEN_ID_2: u256 = 2; +const INVALID_TOKEN: u256 = 666; +const SLOT_1: u256 = 'SLOT1'; +const SLOT_2: u256 = 'SLOT2'; +const VALUE: u256 = 1000; + +// Addresses +fn ZERO() -> ContractAddress { + Zeroable::zero() +} + +fn OWNER() -> ContractAddress { + contract_address_const::<'OWNER'>() +} + +fn RECIPIENT() -> ContractAddress { + contract_address_const::<'RECIPIENT'>() +} + +fn SPENDER() -> ContractAddress { + contract_address_const::<'SPENDER'>() +} + +fn OPERATOR() -> ContractAddress { + contract_address_const::<'OPERATOR'>() +} + +fn SOMEONE() -> ContractAddress { + contract_address_const::<'SOMEONE'>() +} + +fn ANYONE() -> ContractAddress { + contract_address_const::<'ANYONE'>() +} diff --git a/tests/unit/test_approvals.cairo b/tests/unit/test_approvals.cairo new file mode 100644 index 0000000..059c9a5 --- /dev/null +++ b/tests/unit/test_approvals.cairo @@ -0,0 +1,180 @@ +// Starknet imports +use starknet::testing::set_caller_address; + +// snforge deps +use snforge_std::{ + declare, ContractClassTrait, start_cheat_caller_address, stop_cheat_caller_address, + test_address, +}; + +// External imports +use openzeppelin::token::erc721::erc721::ERC721Component::ERC721Impl; +use openzeppelin::token::erc721::erc721::ERC721Component; + +// Local imports +use cairo_erc_3525::module::ERC3525Component::{ERC3525Impl, InternalImpl}; +use cairo_erc_3525::module::ERC3525Component; +use super::constants::{ + ERC3525ComponentState, COMPONENT_STATE, CONTRACT_STATE, VALUE_DECIMALS, TOKEN_ID_1, + INVALID_TOKEN, SLOT_1, VALUE, ZERO, OWNER, OPERATOR, SOMEONE, ANYONE +}; + +// Settings +fn setup() -> ERC3525ComponentState { + let mut state = COMPONENT_STATE(); + state.initializer(VALUE_DECIMALS); + state._mint(OWNER(), TOKEN_ID_1, SLOT_1, 1 * VALUE); + state +} + +// Tests approvals + +#[test] +#[available_gas(20000000)] +fn test_owner_can_approve_operator_value() { + let mut state = setup(); + let mut mock_state = CONTRACT_STATE(); + start_cheat_caller_address(test_address(), OWNER()); + let allowance = state.allowance(TOKEN_ID_1, OPERATOR()); + assert(allowance == 0, 'Wrong allowance'); + mock_state.erc721.approve(OWNER(), TOKEN_ID_1); + state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); + let allowance = state.allowance(TOKEN_ID_1, OPERATOR()); + assert(allowance == VALUE, 'Wrong allowance'); + stop_cheat_caller_address(OWNER()); +} + +#[test] +#[available_gas(20000000)] +fn test_owner_can_approve_more_to_same() { + let mut state = setup(); + let mut mock_state = CONTRACT_STATE(); + start_cheat_caller_address(test_address(), OWNER()); + mock_state.erc721.approve(OWNER(), TOKEN_ID_1); + state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); + state.approve_value(TOKEN_ID_1, OPERATOR(), 2 * VALUE); + let allowance = state.allowance(TOKEN_ID_1, OPERATOR()); + assert(allowance == 2 * VALUE, 'Wrong allowance'); + stop_cheat_caller_address(OWNER()); +} + +#[test] +#[available_gas(20000000)] +fn test_owner_can_approve_less_to_same() { + let mut state = setup(); + let mut mock_state = CONTRACT_STATE(); + start_cheat_caller_address(test_address(), OWNER()); + mock_state.erc721.approve(OWNER(), TOKEN_ID_1); + state.approve_value(TOKEN_ID_1, OPERATOR(), 2 * VALUE); + state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); + let allowance = state.allowance(TOKEN_ID_1, OPERATOR()); + assert(allowance == VALUE, 'Wrong allowance'); + stop_cheat_caller_address(OWNER()); +} + +#[test] +#[available_gas(20000000)] +fn test_owner_can_approve_someone_else() { + let mut state = setup(); + let mut mock_state = CONTRACT_STATE(); + start_cheat_caller_address(test_address(), OWNER()); + mock_state.erc721.approve(OWNER(), TOKEN_ID_1); + state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); + let allowance = state.allowance(TOKEN_ID_1, OPERATOR()); + assert(allowance == VALUE, 'Wrong allowance'); + state.approve_value(TOKEN_ID_1, SOMEONE(), 2 * VALUE); + let allowance = state.allowance(TOKEN_ID_1, SOMEONE()); + assert(allowance == 2 * VALUE, 'Wrong allowance'); + stop_cheat_caller_address(OWNER()); +} + +#[test] +#[available_gas(20000000)] +fn test_721approved_for_all_can_approve_value() { + let mut state = setup(); + let mut mock_state = CONTRACT_STATE(); + start_cheat_caller_address(test_address(), OWNER()); + // ERC721 setup + mock_state.erc721.set_approval_for_all(SOMEONE(), true); + mock_state.erc721.approve(SOMEONE(), TOKEN_ID_1); + stop_cheat_caller_address(OWNER()); + // ERC3525 test + start_cheat_caller_address(test_address(), SOMEONE()); + state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); + let allowance = state.allowance(TOKEN_ID_1, OPERATOR()); + assert(allowance == VALUE, 'Wrong allowance'); + stop_cheat_caller_address(SOMEONE()); +} + +#[test] +#[available_gas(20000000)] +fn test_721approved_can_approve_value() { + let mut state = setup(); + let mut mock_state = CONTRACT_STATE(); + start_cheat_caller_address(test_address(), OWNER()); + // ERC721 setup + mock_state.erc721.approve(SOMEONE(), TOKEN_ID_1); + stop_cheat_caller_address(OWNER()); + // ERC3525 test + start_cheat_caller_address(test_address(), SOMEONE()); + state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); + let allowance = state.allowance(TOKEN_ID_1, OPERATOR()); + assert(allowance == VALUE, 'Wrong allowance'); + stop_cheat_caller_address(SOMEONE()); +} + +#[test] +#[should_panic] +#[available_gas(20000000)] +fn test_cannot_approve_value_to_zero_address() { + let mut state = setup(); + start_cheat_caller_address(test_address(), OWNER()); + // ERC3525 test + state.approve_value(TOKEN_ID_1, ZERO(), VALUE); + stop_cheat_caller_address(OWNER()); +} + +#[test] +#[should_panic] +#[available_gas(20000000)] +fn test_cannot_approve_value_to_self() { + let mut state = setup(); + start_cheat_caller_address(test_address(), OWNER()); + // ERC3525 test + state.approve_value(TOKEN_ID_1, OWNER(), VALUE); + stop_cheat_caller_address(OWNER()); +} + +#[test] +#[should_panic] +#[available_gas(20000000)] +fn test_anyone_cannot_approve_value() { + let mut state = setup(); + start_cheat_caller_address(test_address(), ANYONE()); + // ERC3525 test + state.approve_value(TOKEN_ID_1, ANYONE(), VALUE); + stop_cheat_caller_address(ANYONE()); +} + +#[test] +#[should_panic] +#[available_gas(20000000)] +fn test_cannot_approve_value_nonexistent_token_id() { + let mut state = setup(); + start_cheat_caller_address(test_address(), OWNER()); + // ERC3525 test + state.approve_value(INVALID_TOKEN, OPERATOR(), VALUE); + stop_cheat_caller_address(OWNER()); +} + +#[test] +#[should_panic] +#[available_gas(20000000)] +fn test_zero_address_cannot_approve_value() { + let mut state = setup(); + start_cheat_caller_address(test_address(), ZERO()); + // ERC3525 test + state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); + stop_cheat_caller_address(ZERO()); +} + From 07b2369911b93cdcbf372af4592b7bed4b5365f2 Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Mon, 23 Sep 2024 15:01:37 +0100 Subject: [PATCH 09/14] feat: migrate tests/unit/test_initialization.cairo to snforge 0.27.0 --- tests/lib.cairo | 1 + tests/unit/test_initialization.cairo | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/unit/test_initialization.cairo diff --git a/tests/lib.cairo b/tests/lib.cairo index 9028874..ad4df9b 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -10,4 +10,5 @@ mod integration { mod unit { mod constants; mod test_approvals; + mod test_initialization; } diff --git a/tests/unit/test_initialization.cairo b/tests/unit/test_initialization.cairo new file mode 100644 index 0000000..8815805 --- /dev/null +++ b/tests/unit/test_initialization.cairo @@ -0,0 +1,13 @@ +// Local imports +use cairo_erc_3525::module::{ERC3525Component, ERC3525Component::{ERC3525Impl, InternalImpl}}; +use super::constants::{COMPONENT_STATE, VALUE_DECIMALS}; + + +// Tests initialization +#[test] +#[available_gas(20000000)] +fn test_can_set_valid_decimals() { + let mut state = COMPONENT_STATE(); + state.initializer(VALUE_DECIMALS); + assert(state.value_decimals() == VALUE_DECIMALS, 'Wrong value decimals'); +} From 4c75ed4adeddf7ad3a94edfebdc0043c42a558f2 Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Mon, 23 Sep 2024 15:06:05 +0100 Subject: [PATCH 10/14] feat: migrate tests/unit/test_metadata.cairo to snforge 0.27.0 --- tests/lib.cairo | 1 + tests/unit/test_metadata.cairo | 59 ++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 tests/unit/test_metadata.cairo diff --git a/tests/lib.cairo b/tests/lib.cairo index ad4df9b..3576a56 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -11,4 +11,5 @@ mod unit { mod constants; mod test_approvals; mod test_initialization; + mod test_metadata; } diff --git a/tests/unit/test_metadata.cairo b/tests/unit/test_metadata.cairo new file mode 100644 index 0000000..a8447bb --- /dev/null +++ b/tests/unit/test_metadata.cairo @@ -0,0 +1,59 @@ +// Core imports +use integer::BoundedInt; +use debug::PrintTrait; + +// Starknet imports +use starknet::ContractAddress; + +// use snforge_std::{ +// declare, ContractClassTrait, start_cheat_caller_address, stop_cheat_caller_address, +// test_address, +// }; + +// External imports +use openzeppelin::token::erc721::ERC721Component; + +// Local imports +use cairo_erc_3525::module::ERC3525Component; +use cairo_erc_3525::extensions::metadata::module::ERC3525MetadataComponent::{ + ERC3525MetadataImpl, InternalImpl +}; +use cairo_erc_3525::extensions::metadata::module::ERC3525MetadataComponent; +use super::constants::{ + ERC3525MetadataComponentState, COMPONENT_STATE, COMPONENT_STATE_METADATA, VALUE_DECIMALS, + SLOT_1, SLOT_2 +}; + +// Settings + +fn setup() -> ERC3525MetadataComponentState { + let mut state_metadata = COMPONENT_STATE_METADATA(); + state_metadata.initializer(); + state_metadata +} + +#[test] +#[available_gas(20000000)] +fn test_metadata_contract_uri() { + let mut state_metadata = setup(); + let uri = state_metadata.contract_uri(); + assert(uri == 0, 'Wrong contract URI'); + let new_uri = 'https://example.com'; + state_metadata._set_contract_uri(new_uri); + let uri = state_metadata.contract_uri(); + assert(uri == new_uri, 'Wrong contract URI'); +} + +#[test] +#[available_gas(20000000)] +fn test_metadata_slot_uri() { + let mut state_metadata = setup(); + let uri = state_metadata.slot_uri(SLOT_1); + assert(uri == 0, 'Wrong contract URI'); + let new_uri = 'https://example.com'; + state_metadata._set_slot_uri(SLOT_1, new_uri); + let uri = state_metadata.slot_uri(SLOT_1); + assert(uri == new_uri, 'Wrong contract URI'); + let uri = state_metadata.slot_uri(SLOT_2); + assert(uri == 0, 'Wrong contract URI'); +} From 844c03ded6c5e1b19ababf34196e19ced3eb6621 Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Mon, 23 Sep 2024 16:28:28 +0100 Subject: [PATCH 11/14] feat: migrate tests/unit/test_mint_burn.cairo to snforge 0.27.0 --- src/lib.cairo | 1 + src/test_helpers/utils.cairo | 69 +++++++++ tests/lib.cairo | 1 + tests/unit/test_mint_burn.cairo | 243 ++++++++++++++++++++++++++++++++ 4 files changed, 314 insertions(+) create mode 100644 src/test_helpers/utils.cairo create mode 100644 tests/unit/test_mint_burn.cairo diff --git a/src/lib.cairo b/src/lib.cairo index da440ca..90cd74d 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -26,4 +26,5 @@ mod test_helpers { mod account; mod contracts; mod receiver; + mod utils; } diff --git a/src/test_helpers/utils.cairo b/src/test_helpers/utils.cairo new file mode 100644 index 0000000..38f29b2 --- /dev/null +++ b/src/test_helpers/utils.cairo @@ -0,0 +1,69 @@ +use starknet::ContractAddress; +use starknet::SyscallResultTrait; +use starknet::testing; + +fn deploy(contract_class_hash: felt252, calldata: Array) -> ContractAddress { + let (address, _) = starknet::deploy_syscall( + contract_class_hash.try_into().unwrap(), 0, calldata.span(), false + ) + .unwrap_syscall(); + address +} + +fn deploy_with_salt( + contract_class_hash: felt252, calldata: Array, salt: felt252 +) -> ContractAddress { + let (address, _) = starknet::deploy_syscall( + contract_class_hash.try_into().unwrap(), salt, calldata.span(), false + ) + .unwrap_syscall(); + address +} + +/// Pop the earliest unpopped logged event enum for the contract and checks +/// there's no more keys or data left on the event, preventing unaccounted params. +/// +/// CAUTION: If the event enum contains two `flat` events with the same structure (member types), +/// this function will always match the first event, even when the second one is emitted. +fn pop_log, +starknet::Event>(address: ContractAddress) -> Option { + let (mut keys, mut data) = testing::pop_log_raw(address)?; + + let ret = starknet::Event::deserialize(ref keys, ref data); + assert!(data.is_empty(), "Event has extra data"); + assert!(keys.is_empty(), "Event has extra keys"); + ret +} + +/// Asserts that `expected_keys` exactly matches the indexed keys from `event`. +/// +/// `expected_keys` must include all indexed event keys for `event` in the order +/// that they're defined. +/// +/// If the event is not flattened, the first key will be the event member name +/// e.g. selector!("EnumMemberName"). +fn assert_indexed_keys, +starknet::Event>(event: T, expected_keys: Span) { + let mut keys = array![]; + let mut data = array![]; + + event.append_keys_and_data(ref keys, ref data); + assert!(expected_keys == keys.span()); +} + +fn assert_no_events_left(address: ContractAddress) { + assert!(testing::pop_log_raw(address).is_none(), "Events remaining on queue"); +} + +fn drop_event(address: ContractAddress) { + let _ = testing::pop_log_raw(address); +} + +fn drop_events(address: ContractAddress, count: felt252) { + let mut _count = count; + loop { + if _count == 0 { + break; + } + drop_event(address); + _count -= 1; + } +} diff --git a/tests/lib.cairo b/tests/lib.cairo index 3576a56..e34f596 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -12,4 +12,5 @@ mod unit { mod test_approvals; mod test_initialization; mod test_metadata; + mod test_mint_burn; } diff --git a/tests/unit/test_mint_burn.cairo b/tests/unit/test_mint_burn.cairo new file mode 100644 index 0000000..17e39bf --- /dev/null +++ b/tests/unit/test_mint_burn.cairo @@ -0,0 +1,243 @@ +// Core imports +use debug::PrintTrait; + +// Starknet imports +use starknet::get_contract_address; + +// snforge deps +use snforge_std::{ + declare, ContractClassTrait, start_cheat_caller_address, stop_cheat_caller_address, + test_address, spy_events, EventSpyAssertionsTrait +}; + +// External imports +use openzeppelin::token::erc721::ERC721Component::{ERC721Impl, InternalImpl as ERC721InternalImpl}; +use openzeppelin::token::erc721::ERC721Component; + +// Local imports +use cairo_erc_3525::module::ERC3525Component::{ERC3525Impl, InternalImpl}; +use cairo_erc_3525::module::ERC3525Component; +use cairo_erc_3525::test_helpers::utils; +use super::constants::{ + ERC3525ComponentState, COMPONENT_STATE, CONTRACT_STATE, VALUE_DECIMALS, TOKEN_ID_1, SLOT_1, + VALUE, ZERO, OWNER +}; + +// Settings + +fn setup() -> ERC3525ComponentState { + let mut state = COMPONENT_STATE(); + state.initializer(VALUE_DECIMALS); + state +} + +// Tests approvals + +#[test] +#[available_gas(20000000)] +fn test_mint() { + let mut state = setup(); + let mut mock_state = CONTRACT_STATE(); + start_cheat_caller_address(test_address(), OWNER()); + + let mut spy = spy_events(); + + state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); + + let balance = mock_state.balance_of(OWNER()); + assert(balance == 1, 'Wrong balance'); + let slot = state.slot_of(TOKEN_ID_1); + assert(slot == SLOT_1, 'Wrong slot'); + let owner = mock_state.owner_of(TOKEN_ID_1); + assert(owner == OWNER(), 'Wrong owner'); + + spy + .assert_emitted( + @array![ + ( + test_address(), + ERC721Component::Event::Transfer( + ERC721Component::Transfer { + from: ZERO(), to: OWNER(), token_id: TOKEN_ID_1 + } + ) + ), + ] + ); + + spy + .assert_emitted( + @array![ + ( + test_address(), + ERC3525Component::Event::TransferValue( + ERC3525Component::TransferValue { + from_token_id: 0, to_token_id: TOKEN_ID_1, value: VALUE + } + ) + ) + ] + ); +} + +#[test] +#[available_gas(20000000)] +fn test_mint_value_with_previous_balance() { + let mut state = setup(); + start_cheat_caller_address(test_address(), OWNER()); + + state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); + state._mint_value(TOKEN_ID_1, VALUE); + let value = state.value_of(TOKEN_ID_1); + assert(value == 2 * VALUE, 'Wrong value'); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC3525: token not minted',))] +fn test_mint_value_revert_not_minted() { + let mut state = setup(); + start_cheat_caller_address(test_address(), OWNER()); + + state._mint_value(TOKEN_ID_1, VALUE); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC3525: invalid token_id',))] +fn test_mint_revert_zero_token_id() { + let mut state = setup(); + start_cheat_caller_address(test_address(), OWNER()); + let token_id: u256 = 0; + + state._mint(OWNER(), token_id, SLOT_1, VALUE); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC3525: invalid address',))] +fn test_mint_revert_zero_address() { + let mut state = setup(); + start_cheat_caller_address(test_address(), OWNER()); + + state._mint(ZERO(), TOKEN_ID_1, SLOT_1, VALUE); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC3525: token already minted',))] +fn test_mint_revert_existing_id() { + let mut state = setup(); + start_cheat_caller_address(test_address(), OWNER()); + + state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); + state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC3525: token not minted',))] +fn test_burn() { + let mut state = setup(); + start_cheat_caller_address(test_address(), OWNER()); + + let mut spy = spy_events(); + + state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); + state._burn(TOKEN_ID_1); + + spy + .assert_emitted( + @array![ + ( + test_address(), + ERC721Component::Event::Transfer( + ERC721Component::Transfer { + from: OWNER(), to: ZERO(), token_id: TOKEN_ID_1 + } + ) + ), + ] + ); + + spy + .assert_emitted( + @array![ + ( + test_address(), + ERC3525Component::Event::SlotChanged( + ERC3525Component::SlotChanged { + token_id: TOKEN_ID_1, old_slot: SLOT_1, new_slot: 0 + } + ) + ) + ] + ); + + spy + .assert_emitted( + @array![ + ( + test_address(), + ERC3525Component::Event::TransferValue( + ERC3525Component::TransferValue { + from_token_id: TOKEN_ID_1, to_token_id: 0, value: VALUE + } + ) + ) + ] + ); + + let _value = state.value_of(TOKEN_ID_1); +} + +#[test] +#[available_gas(20000000)] +fn test_burn_value() { + let mut state = setup(); + start_cheat_caller_address(test_address(), OWNER()); + + let mut spy = spy_events(); + + state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); + state._burn_value(TOKEN_ID_1, VALUE); + + spy + .assert_emitted( + @array![ + ( + test_address(), + ERC3525Component::Event::TransferValue( + ERC3525Component::TransferValue { + from_token_id: TOKEN_ID_1, to_token_id: 0, value: VALUE + } + ) + ) + ] + ); + + // [Assert] Token value + let value = state.value_of(TOKEN_ID_1); + assert(value == 0, 'Wrong value'); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC3525: token not minted',))] +fn test_burn_revert_not_minted() { + let mut state = setup(); + start_cheat_caller_address(test_address(), OWNER()); + + state._burn(TOKEN_ID_1); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC3525: value exceeds balance',))] +fn test_burn_value_revert_exceeds_balance() { + let mut state = setup(); + start_cheat_caller_address(test_address(), OWNER()); + + state._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); + state._burn_value(TOKEN_ID_1, 2 * VALUE); +} From 9ce5333b436aa70444c4ccd789f3bfcc5eb77c25 Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Mon, 23 Sep 2024 16:45:04 +0100 Subject: [PATCH 12/14] feat: migrate tests/unit/test_slot_approvable.cairo to snforge 0.27.0 --- tests/lib.cairo | 1 + tests/unit/test_metadata.cairo | 5 - tests/unit/test_slot_approvable.cairo | 128 ++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 tests/unit/test_slot_approvable.cairo diff --git a/tests/lib.cairo b/tests/lib.cairo index e34f596..12f432b 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -13,4 +13,5 @@ mod unit { mod test_initialization; mod test_metadata; mod test_mint_burn; + mod test_slot_approvable; } diff --git a/tests/unit/test_metadata.cairo b/tests/unit/test_metadata.cairo index a8447bb..ae7e443 100644 --- a/tests/unit/test_metadata.cairo +++ b/tests/unit/test_metadata.cairo @@ -5,11 +5,6 @@ use debug::PrintTrait; // Starknet imports use starknet::ContractAddress; -// use snforge_std::{ -// declare, ContractClassTrait, start_cheat_caller_address, stop_cheat_caller_address, -// test_address, -// }; - // External imports use openzeppelin::token::erc721::ERC721Component; diff --git a/tests/unit/test_slot_approvable.cairo b/tests/unit/test_slot_approvable.cairo new file mode 100644 index 0000000..867c3fe --- /dev/null +++ b/tests/unit/test_slot_approvable.cairo @@ -0,0 +1,128 @@ +// Core imports +use debug::PrintTrait; + +// snforge deps +use snforge_std::{ + declare, ContractClassTrait, start_cheat_caller_address, stop_cheat_caller_address, + test_address, spy_events, EventSpyAssertionsTrait +}; + +// External imports +use openzeppelin::token::erc721::ERC721Component; + +// Local imports +use cairo_erc_3525::module::ERC3525Component::{InternalTrait, ERC3525Impl}; +use cairo_erc_3525::module::ERC3525Component; +use cairo_erc_3525::extensions::slotapprovable::module::ERC3525SlotApprovableComponent::{ + ERC3525SlotApprovableImpl, InternalTrait as ERC3525SlotApprovableInternalTrait, + ExternalTrait as ERC3525SlotApprovableExternalTrait +}; +use cairo_erc_3525::extensions::slotapprovable::module::ERC3525SlotApprovableComponent; +use super::constants::{ + ERC3525SlotApprovableComponentState, CONTRACT_STATE, COMPONENT_STATE_SLOT_APPROVABLE, + VALUE_DECIMALS, TOKEN_ID_1, TOKEN_ID_2, SLOT_1, SLOT_2, VALUE, ZERO, OWNER, OPERATOR, SOMEONE +}; + +// Settings + +fn setup() -> ERC3525SlotApprovableComponentState { + let mut state = COMPONENT_STATE_SLOT_APPROVABLE(); + let mut mock_state = CONTRACT_STATE(); + mock_state.erc3525.initializer(VALUE_DECIMALS); + state.initializer(); + state +} +// fn setup() -> (ERC3525::ContractState, ERC3525SlotApprovable::ContractState) { +// let mut state = STATE(); +// mock_state.initializer(VALUE_DECIMALS); +// let mut state_slot_approvable = STATE_SLOT_APPROVABLE(); +// state.initializer(ref state_slot_approvable); +// (state, state_slot_approvable) +// } + +// Tests approvals +#[test] +#[available_gas(20000000)] +fn test_slot_approvable_owner_can_approve_slot() { + let mut state = setup(); + start_cheat_caller_address(test_address(), OWNER()); + state.set_approval_for_slot(OWNER(), SLOT_1, OPERATOR(), true); + stop_cheat_caller_address(OWNER()); +} + +#[test] +#[available_gas(20000000)] +fn test_slot_approvable_operator_can_approve_value() { + let mut state = setup(); + let mut mock_state = CONTRACT_STATE(); + start_cheat_caller_address(test_address(), OWNER()); + mock_state.erc3525._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); + state.set_approval_for_slot(OWNER(), SLOT_1, OPERATOR(), true); + stop_cheat_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OPERATOR()); + state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); + stop_cheat_caller_address(OPERATOR()); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC3525: caller not allowed',))] +fn test_slot_approvable_operator_cannot_approve_any_token() { + let mut state = setup(); + let mut mock_state = CONTRACT_STATE(); + start_cheat_caller_address(test_address(), OWNER()); + mock_state.erc3525._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); + state.set_approval_for_slot(OWNER(), SLOT_2, OPERATOR(), true); + stop_cheat_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OPERATOR()); + state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); + stop_cheat_caller_address(OPERATOR()); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC3525: insufficient allowance',))] +fn test_slot_approvable_operator_cannot_transfer_anyones_value() { + let mut state = setup(); + let mut mock_state = CONTRACT_STATE(); + start_cheat_caller_address(test_address(), OWNER()); + mock_state.erc3525._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); + mock_state.erc3525._mint(SOMEONE(), TOKEN_ID_2, SLOT_1, VALUE); + state.set_approval_for_slot(OWNER(), SLOT_1, OPERATOR(), true); + stop_cheat_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OPERATOR()); + state.transfer_value_from(TOKEN_ID_2, 0, OPERATOR(), VALUE); + stop_cheat_caller_address(OPERATOR()); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC3525: slot mismatch',))] +fn test_slot_approvable_operator_transfer_value_revert_slot_mismatch() { + let mut state = setup(); + let mut mock_state = CONTRACT_STATE(); + start_cheat_caller_address(test_address(), OWNER()); + mock_state.erc3525._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); + mock_state.erc3525._mint(SOMEONE(), TOKEN_ID_2, SLOT_2, VALUE); + state.set_approval_for_slot(OWNER(), SLOT_1, OPERATOR(), true); + stop_cheat_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OPERATOR()); + state.transfer_value_from(TOKEN_ID_1, TOKEN_ID_2, ZERO(), VALUE); + stop_cheat_caller_address(OPERATOR()); +} + +#[test] +#[available_gas(20000000)] +#[should_panic(expected: ('ERC3525: caller not allowed',))] +fn test_slot_approvable_revoked_slot_operator_cannot_approve() { + let mut state = setup(); + let mut mock_state = CONTRACT_STATE(); + start_cheat_caller_address(test_address(), OWNER()); + mock_state.erc3525._mint(OWNER(), TOKEN_ID_1, SLOT_1, VALUE); + state.set_approval_for_slot(OWNER(), SLOT_1, OPERATOR(), true); + state.set_approval_for_slot(OWNER(), SLOT_1, OPERATOR(), false); + stop_cheat_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OPERATOR()); + state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); + stop_cheat_caller_address(OPERATOR()); +} From d5a094d172d59074c7029fd14cab45ac545e31dd Mon Sep 17 00:00:00 2001 From: tekkac Date: Sat, 28 Sep 2024 19:47:16 +0200 Subject: [PATCH 13/14] =?UTF-8?q?=F0=9F=8E=A8=20format=20code=20and=20add?= =?UTF-8?q?=20config=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++- Scarb.lock | 20 +++++++++++++++++++ Scarb.toml | 4 ++-- ...le_burnable_metadata_slot_approvable.cairo | 4 +++- src/test_helpers/account.cairo | 2 +- 5 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 Scarb.lock diff --git a/.gitignore b/.gitignore index 9f97022..389b850 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -target/ \ No newline at end of file +target/ +.snfoundry* \ No newline at end of file diff --git a/Scarb.lock b/Scarb.lock new file mode 100644 index 0000000..db309cf --- /dev/null +++ b/Scarb.lock @@ -0,0 +1,20 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "cairo_erc_3525" +version = "2.1.0" +dependencies = [ + "openzeppelin", + "snforge_std", +] + +[[package]] +name = "openzeppelin" +version = "0.14.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.14.0#f091c4f51ddeb10297db984acae965328c5a4e5b" + +[[package]] +name = "snforge_std" +version = "0.27.0" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.27.0#2d99b7c00678ef0363881ee0273550c44a9263de" diff --git a/Scarb.toml b/Scarb.toml index abb051d..d4caddc 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -2,7 +2,7 @@ name = "cairo_erc_3525" version = "2.1.0" edition = "2023_01" -cairo-version = "2.6.3" +cairo-version = "2.6.4" [lib] @@ -19,4 +19,4 @@ casm = true test = "snforge test" [tool.snforge] -exit_first = false \ No newline at end of file +exit_first = false diff --git a/src/presets/erc3525_mintable_burnable_metadata_slot_approvable.cairo b/src/presets/erc3525_mintable_burnable_metadata_slot_approvable.cairo index 769ca0f..66bdafb 100644 --- a/src/presets/erc3525_mintable_burnable_metadata_slot_approvable.cairo +++ b/src/presets/erc3525_mintable_burnable_metadata_slot_approvable.cairo @@ -21,7 +21,9 @@ mod ERC3525MintableBurnableMSA { use openzeppelin::introspection::src5::SRC5Component; // ERC721 - use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl, interface::{IERC721, IERC721CamelOnly}}; + use openzeppelin::token::erc721::{ + ERC721Component, ERC721HooksEmptyImpl, interface::{IERC721, IERC721CamelOnly} + }; // ERC3525 use cairo_erc_3525::module::ERC3525Component; diff --git a/src/test_helpers/account.cairo b/src/test_helpers/account.cairo index 09bf60f..e9805bf 100644 --- a/src/test_helpers/account.cairo +++ b/src/test_helpers/account.cairo @@ -58,4 +58,4 @@ pub(crate) mod AccountUpgradeable { self.upgradeable.upgrade(new_class_hash); } } -} \ No newline at end of file +} From 4cc330692768c3b2823167dc8f152f79b0f95ca3 Mon Sep 17 00:00:00 2001 From: tekkac Date: Sat, 28 Sep 2024 20:53:31 +0200 Subject: [PATCH 14/14] =?UTF-8?q?=F0=9F=91=B7=20update=20ci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7feaaac..0b186f5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up Scarb uses: software-mansion/setup-scarb@v1 - with: - scarb-version: "2.6.5" + - name: Set up Starknet Foundry + uses: foundry-rs/setup-snfoundry@v3 - name: Run cairo tests run: scarb test