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..0b186f5 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 + - name: Set up Starknet Foundry + uses: foundry-rs/setup-snfoundry@v3 - name: Run cairo tests run: scarb test - \ No newline at end of file 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/.tool-versions b/.tool-versions new file mode 100644 index 0000000..b6e9b54 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +scarb 2.6.5 +starknet-foundry 0.27.0 \ 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 32163e4..d4caddc 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -2,18 +2,21 @@ name = "cairo_erc_3525" version = "2.1.0" edition = "2023_01" -cairo-version = "2.6.3" +cairo-version = "2.6.4" [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 +[scripts] +test = "snforge test" + [tool.snforge] -exit_first = false \ No newline at end of file +exit_first = false 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..90cd74d 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -22,4 +22,9 @@ mod presets { mod erc3525_mintable_burnable_metadata_slot_approvable_slot_enumerable; } -mod tests; +mod test_helpers { + mod account; + mod contracts; + mod receiver; + mod utils; +} 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..66bdafb 100644 --- a/src/presets/erc3525_mintable_burnable_metadata_slot_approvable.cairo +++ b/src/presets/erc3525_mintable_burnable_metadata_slot_approvable.cairo @@ -18,13 +18,11 @@ 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 @@ -59,8 +57,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/test_helpers/account.cairo b/src/test_helpers/account.cairo new file mode 100644 index 0000000..e9805bf --- /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); + } + } +} diff --git a/src/tests/mocks/contracts.cairo b/src/test_helpers/contracts.cairo similarity index 97% rename from src/tests/mocks/contracts.cairo rename to src/test_helpers/contracts.cairo index cccee78..0317dac 100644 --- a/src/tests/mocks/contracts.cairo +++ b/src/test_helpers/contracts.cairo @@ -1,7 +1,7 @@ #[starknet::contract] mod DualCaseERC3525Mock { use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc721::ERC721Component; + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; use cairo_erc_3525::module::ERC3525Component; use starknet::ContractAddress; @@ -71,7 +71,7 @@ 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 openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; use starknet::ContractAddress; component!(path: ERC721Component, storage: erc721, event: ERC721Event); @@ -81,7 +81,7 @@ mod DualCaseERC3525MetadataMock { path: ERC3525MetadataComponent, storage: erc3525_metadata, event: ERC3525MetadataEvent ); - // ERC721 + // ERC721 #[abi(embed_v0)] impl ERC721Impl = ERC721Component::ERC721Impl; #[abi(embed_v0)] @@ -157,7 +157,7 @@ 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 openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; use starknet::ContractAddress; component!(path: ERC721Component, storage: erc721, event: ERC721Event); @@ -246,7 +246,7 @@ 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 openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; use starknet::ContractAddress; component!(path: ERC721Component, storage: erc721, event: ERC721Event); diff --git a/src/tests/mocks/receiver.cairo b/src/test_helpers/receiver.cairo similarity index 100% rename from src/tests/mocks/receiver.cairo rename to src/test_helpers/receiver.cairo diff --git a/src/tests/utils.cairo b/src/test_helpers/utils.cairo similarity index 100% rename from src/tests/utils.cairo rename to src/test_helpers/utils.cairo 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_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/integration/constants.cairo b/tests/integration/constants.cairo similarity index 100% rename from src/tests/integration/constants.cairo rename to tests/integration/constants.cairo diff --git a/src/tests/integration/test_base.cairo b/tests/integration/test_base.cairo similarity index 69% rename from src/tests/integration/test_base.cairo rename to tests/integration/test_base.cairo index 0dc1b95..8b35f4b 100644 --- a/src/tests/integration/test_base.cairo +++ b/tests/integration/test_base.cairo @@ -4,21 +4,25 @@ 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}; -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 super::constants; + #[derive(Drop)] struct Signers { @@ -28,30 +32,29 @@ struct Signers { 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 + class_hash: snforge_std::cheatcodes::contract_class::ContractClass, public_key: felt252 ) -> ContractAddress { let calldata: Array = array![public_key]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); + let (address, _) = class_hash.deploy(@calldata).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 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(class_hash, 'OWNER'), - someone: deploy_account(class_hash, 'SOMEONE'), - anyone: deploy_account(class_hash, 'ANYONE'), - operator: deploy_account(class_hash, 'OPERATOR'), + 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) } @@ -92,16 +95,21 @@ fn test_integration_base_scenario() { assert(erc721.owner_of(one) == signers.owner, 'Wrong owner'); // Approve - testing::set_contract_address(signers.owner); + 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 - testing::set_contract_address(signers.anyone); + 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); @@ -112,11 +120,15 @@ fn test_integration_base_scenario() { assert(external.total_value(constants::SLOT_2) == 4 * constants::VALUE, 'Wrong total value'); // Burn value - testing::set_contract_address(signers.owner); + start_cheat_caller_address(contract_address, signers.owner); external.burn_value(one, 3); - testing::set_contract_address(signers.someone); + stop_cheat_caller_address(signers.owner); + + start_cheat_caller_address(contract_address, signers.someone); external.burn_value(height, 2); - testing::set_contract_address(signers.anyone); + 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'); @@ -126,9 +138,10 @@ fn test_integration_base_scenario() { assert( external.total_value(constants::SLOT_2) == 4 * constants::VALUE - 3, 'Wrong total value' ); + stop_cheat_caller_address(signers.anyone); // Burn token - testing::set_contract_address(signers.owner); + 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' @@ -136,4 +149,5 @@ fn test_integration_base_scenario() { assert( external.total_value(constants::SLOT_2) == 4 * constants::VALUE - 3, 'Wrong total value' ); + stop_cheat_caller_address(signers.owner); } diff --git a/src/tests/integration/test_metadata.cairo b/tests/integration/test_metadata.cairo similarity index 67% rename from src/tests/integration/test_metadata.cairo rename to tests/integration/test_metadata.cairo index 12570e3..f265069 100644 --- a/src/tests/integration/test_metadata.cairo +++ b/tests/integration/test_metadata.cairo @@ -4,14 +4,17 @@ 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}; -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}; @@ -21,7 +24,7 @@ use cairo_erc_3525::presets::erc3525_mintable_burnable_metadata::{ use cairo_erc_3525::extensions::metadata::interface::{ IERC3525MetadataDispatcher, IERC3525MetadataDispatcherTrait, IERC3525_METADATA_ID }; -use cairo_erc_3525::tests::integration::constants; +use super::constants; #[derive(Drop)] struct Signers { @@ -31,34 +34,32 @@ struct Signers { 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 + class_hash: snforge_std::cheatcodes::contract_class::ContractClass, public_key: felt252 ) -> ContractAddress { let calldata: Array = array![public_key]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); + let (address, _) = class_hash.deploy(@calldata).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 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(class_hash, 'OWNER'), - someone: deploy_account(class_hash, 'SOMEONE'), - anyone: deploy_account(class_hash, 'ANYONE'), - operator: deploy_account(class_hash, 'OPERATOR'), + 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) } diff --git a/src/tests/integration/test_receiver.cairo b/tests/integration/test_receiver.cairo similarity index 61% rename from src/tests/integration/test_receiver.cairo rename to tests/integration/test_receiver.cairo index 5045d40..6bc0add 100644 --- a/src/tests/integration/test_receiver.cairo +++ b/tests/integration/test_receiver.cairo @@ -4,23 +4,27 @@ 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}; -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; +use super::constants; +use cairo_erc_3525::test_helpers::receiver::{ + Receiver, IReceiverDispatcher, IReceiverDispatcherTrait +}; #[derive(Drop)] struct Signers { @@ -34,27 +38,34 @@ fn deploy_contract(class_hash: starknet::class_hash::ClassHash) -> ContractAddre } fn deploy_account( - class_hash: starknet::class_hash::ClassHash, public_key: felt252 + class_hash: snforge_std::cheatcodes::contract_class::ContractClass, public_key: felt252 ) -> ContractAddress { let calldata: Array = array![public_key]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); + let (address, _) = class_hash.deploy(@calldata).unwrap(); address } -fn deploy_receiver(class_hash: starknet::class_hash::ClassHash) -> ContractAddress { +fn deploy_receiver( + class_hash: snforge_std::cheatcodes::contract_class::ContractClass +) -> ContractAddress { let calldata: Array = array![]; - let (address, _) = starknet::deploy_syscall(class_hash, 0, calldata.span(), false).unwrap(); + let (address, _) = class_hash.deploy(@calldata).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); + 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) } diff --git a/src/tests/integration/test_slot_approvable.cairo b/tests/integration/test_slot_approvable.cairo similarity index 63% rename from src/tests/integration/test_slot_approvable.cairo rename to tests/integration/test_slot_approvable.cairo index 64bf236..bdf29ff 100644 --- a/src/tests/integration/test_slot_approvable.cairo +++ b/tests/integration/test_slot_approvable.cairo @@ -4,14 +4,17 @@ 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}; -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}; @@ -22,7 +25,7 @@ use cairo_erc_3525::extensions::slotapprovable::interface::{ IERC3525SlotApprovableDispatcher, IERC3525SlotApprovableDispatcherTrait, IERC3525_SLOT_APPROVABLE_ID }; -use cairo_erc_3525::tests::integration::constants; +use super::constants; #[derive(Drop)] struct Signers { @@ -30,34 +33,29 @@ struct Signers { 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 + class_hash: snforge_std::cheatcodes::contract_class::ContractClass, 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'); + let (address, _) = class_hash.deploy(@calldata).unwrap(); 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 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(class_hash, 'OWNER'), - operator: deploy_account(class_hash, 'OPERATOR'), + owner: deploy_account(AccountUpgradeable_class_hash, 'OWNER'), + operator: deploy_account(AccountUpgradeable_class_hash, 'OPERATOR'), }; (contract_address, signer) } @@ -89,11 +87,13 @@ fn test_integration_slot_aprovable_scenario() { let two = external.mint(signers.operator, constants::SLOT_1, constants::VALUE); // Slot approvals - testing::set_contract_address(signers.owner); + 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 - testing::set_contract_address(signers.operator); + 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/src/tests/integration/test_slot_enumerable.cairo b/tests/integration/test_slot_enumerable.cairo similarity index 75% rename from src/tests/integration/test_slot_enumerable.cairo rename to tests/integration/test_slot_enumerable.cairo index d24a167..b241e70 100644 --- a/src/tests/integration/test_slot_enumerable.cairo +++ b/tests/integration/test_slot_enumerable.cairo @@ -4,14 +4,17 @@ 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}; -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}; @@ -22,7 +25,7 @@ use cairo_erc_3525::extensions::slotenumerable::interface::{ IERC3525SlotEnumerableDispatcher, IERC3525SlotEnumerableDispatcherTrait, IERC3525_SLOT_ENUMERABLE_ID }; -use cairo_erc_3525::tests::integration::constants; +use super::constants; use debug::PrintTrait; @@ -34,36 +37,31 @@ struct Signers { 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 + class_hash: snforge_std::cheatcodes::contract_class::ContractClass, 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'); + let (address, _) = class_hash.deploy(@calldata).unwrap(); 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 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(class_hash, 'OWNER'), - someone: deploy_account(class_hash, 'SOMEONE'), - anyone: deploy_account(class_hash, 'ANYONE'), - operator: deploy_account(class_hash, 'OPERATOR'), + 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) } @@ -87,7 +85,7 @@ fn test_integration_slot_enumerable_scenario() { let external = IExternalDispatcher { contract_address }; let erc3525 = IERC3525Dispatcher { contract_address }; let erc3525_se = IERC3525SlotEnumerableDispatcher { contract_address }; - let _erc721 = IERC721Dispatcher { contract_address }; + let erc721 = IERC721Dispatcher { contract_address }; // Mint tokens let one = external.mint(signers.owner, constants::SLOT_1, constants::VALUE); @@ -149,16 +147,21 @@ fn test_integration_slot_enumerable_scenario() { ); // Approve - testing::set_contract_address(signers.owner); + 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 - testing::set_contract_address(signers.anyone); + 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); diff --git a/tests/lib.cairo b/tests/lib.cairo new file mode 100644 index 0000000..12f432b --- /dev/null +++ b/tests/lib.cairo @@ -0,0 +1,17 @@ +mod integration { + mod constants; + mod test_base; + mod test_metadata; + mod test_receiver; + mod test_slot_approvable; + mod test_slot_enumerable; +} + +mod unit { + mod constants; + mod test_approvals; + mod test_initialization; + mod test_metadata; + mod test_mint_burn; + mod test_slot_approvable; +} diff --git a/src/tests/unit/constants.cairo b/tests/unit/constants.cairo similarity index 98% rename from src/tests/unit/constants.cairo rename to tests/unit/constants.cairo index 9afb714..8332cec 100644 --- a/src/tests/unit/constants.cairo +++ b/tests/unit/constants.cairo @@ -1,25 +1,21 @@ // 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::{ +use cairo_erc_3525::test_helpers::contracts::{ DualCaseERC3525Mock, DualCaseERC3525MetadataMock, DualCaseERC3525SlotApprovableMock, DualCaseERC3525SlotEnumerableMock }; // Setup - pub type ERC3525ComponentState = ERC3525Component::ComponentState; pub type ERC3525MetadataComponentState = @@ -34,7 +30,6 @@ pub type ERC3525SlotEnumerableComponentState = >; // State - fn COMPONENT_STATE() -> ERC3525ComponentState { ERC3525Component::component_state_for_testing() } @@ -68,7 +63,6 @@ fn CONTRACT_STATE_SLOT_ENUMERABLE() -> DualCaseERC3525SlotEnumerableMock::Contra } // Constants - const NAME: felt252 = 'NAME'; const SYMBOL: felt252 = 'SYMBOL'; const VALUE_DECIMALS: u8 = 6; @@ -80,7 +74,6 @@ const SLOT_2: u256 = 'SLOT2'; const VALUE: u256 = 1000; // Addresses - fn ZERO() -> ContractAddress { Zeroable::zero() } diff --git a/src/tests/unit/test_approvals.cairo b/tests/unit/test_approvals.cairo similarity index 68% rename from src/tests/unit/test_approvals.cairo rename to tests/unit/test_approvals.cairo index bc59421..059c9a5 100644 --- a/src/tests/unit/test_approvals.cairo +++ b/tests/unit/test_approvals.cairo @@ -1,23 +1,25 @@ // Starknet imports - use starknet::testing::set_caller_address; -// External imports +// 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 cairo_erc_3525::tests::unit::constants::{ +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); @@ -31,47 +33,59 @@ fn setup() -> ERC3525ComponentState { #[available_gas(20000000)] fn test_owner_can_approve_operator_value() { let mut state = setup(); - set_caller_address(OWNER()); + 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(); - set_caller_address(OWNER()); + 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(); - set_caller_address(OWNER()); + 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(); - set_caller_address(OWNER()); + 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] @@ -79,14 +93,17 @@ fn test_owner_can_approve_someone_else() { fn test_721approved_for_all_can_approve_value() { let mut state = setup(); let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); + 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 - set_caller_address(SOMEONE()); // Prank + 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] @@ -94,14 +111,16 @@ fn test_721approved_for_all_can_approve_value() { fn test_721approved_can_approve_value() { let mut state = setup(); let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); // ERC721 setup mock_state.erc721.approve(SOMEONE(), TOKEN_ID_1); + stop_cheat_caller_address(OWNER()); // ERC3525 test - set_caller_address(SOMEONE()); // Prank + 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] @@ -109,9 +128,10 @@ fn test_721approved_can_approve_value() { #[available_gas(20000000)] fn test_cannot_approve_value_to_zero_address() { let mut state = setup(); - set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); // ERC3525 test state.approve_value(TOKEN_ID_1, ZERO(), VALUE); + stop_cheat_caller_address(OWNER()); } #[test] @@ -119,9 +139,10 @@ fn test_cannot_approve_value_to_zero_address() { #[available_gas(20000000)] fn test_cannot_approve_value_to_self() { let mut state = setup(); - set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); // ERC3525 test state.approve_value(TOKEN_ID_1, OWNER(), VALUE); + stop_cheat_caller_address(OWNER()); } #[test] @@ -129,9 +150,10 @@ fn test_cannot_approve_value_to_self() { #[available_gas(20000000)] fn test_anyone_cannot_approve_value() { let mut state = setup(); - set_caller_address(ANYONE()); + start_cheat_caller_address(test_address(), ANYONE()); // ERC3525 test state.approve_value(TOKEN_ID_1, ANYONE(), VALUE); + stop_cheat_caller_address(ANYONE()); } #[test] @@ -139,9 +161,10 @@ fn test_anyone_cannot_approve_value() { #[available_gas(20000000)] fn test_cannot_approve_value_nonexistent_token_id() { let mut state = setup(); - set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); // ERC3525 test state.approve_value(INVALID_TOKEN, OPERATOR(), VALUE); + stop_cheat_caller_address(OWNER()); } #[test] @@ -149,7 +172,9 @@ fn test_cannot_approve_value_nonexistent_token_id() { #[available_gas(20000000)] fn test_zero_address_cannot_approve_value() { let mut state = setup(); - set_caller_address(ZERO()); + start_cheat_caller_address(test_address(), ZERO()); // ERC3525 test state.approve_value(TOKEN_ID_1, OPERATOR(), VALUE); + stop_cheat_caller_address(ZERO()); } + diff --git a/src/tests/unit/test_initialization.cairo b/tests/unit/test_initialization.cairo similarity index 57% rename from src/tests/unit/test_initialization.cairo rename to tests/unit/test_initialization.cairo index 9ec4562..8815805 100644 --- a/src/tests/unit/test_initialization.cairo +++ b/tests/unit/test_initialization.cairo @@ -1,12 +1,9 @@ // 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}; +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() { diff --git a/src/tests/unit/test_metadata.cairo b/tests/unit/test_metadata.cairo similarity index 89% rename from src/tests/unit/test_metadata.cairo rename to tests/unit/test_metadata.cairo index 44a0ab3..ae7e443 100644 --- a/src/tests/unit/test_metadata.cairo +++ b/tests/unit/test_metadata.cairo @@ -1,26 +1,20 @@ // 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; +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 cairo_erc_3525::tests::unit::constants::{ +use super::constants::{ ERC3525MetadataComponentState, COMPONENT_STATE, COMPONENT_STATE_METADATA, VALUE_DECIMALS, SLOT_1, SLOT_2 }; 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); +} diff --git a/src/tests/unit/test_slot_approvable.cairo b/tests/unit/test_slot_approvable.cairo similarity index 73% rename from src/tests/unit/test_slot_approvable.cairo rename to tests/unit/test_slot_approvable.cairo index 14aca31..867c3fe 100644 --- a/src/tests/unit/test_slot_approvable.cairo +++ b/tests/unit/test_slot_approvable.cairo @@ -1,17 +1,16 @@ // Core imports - use debug::PrintTrait; -// 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, spy_events, EventSpyAssertionsTrait +}; // External imports - -use openzeppelin::token::erc721::erc721::ERC721Component; +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::{ @@ -19,7 +18,7 @@ use cairo_erc_3525::extensions::slotapprovable::module::ERC3525SlotApprovableCom ExternalTrait as ERC3525SlotApprovableExternalTrait }; use cairo_erc_3525::extensions::slotapprovable::module::ERC3525SlotApprovableComponent; -use cairo_erc_3525::tests::unit::constants::{ +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 }; @@ -42,13 +41,13 @@ fn setup() -> ERC3525SlotApprovableComponentState { // } // Tests approvals - #[test] #[available_gas(20000000)] fn test_slot_approvable_owner_can_approve_slot() { let mut state = setup(); - set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.set_approval_for_slot(OWNER(), SLOT_1, OPERATOR(), true); + stop_cheat_caller_address(OWNER()); } #[test] @@ -56,11 +55,13 @@ fn test_slot_approvable_owner_can_approve_slot() { fn test_slot_approvable_operator_can_approve_value() { let mut state = setup(); let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); + 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); - set_caller_address(OPERATOR()); + 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] @@ -69,11 +70,13 @@ fn test_slot_approvable_operator_can_approve_value() { fn test_slot_approvable_operator_cannot_approve_any_token() { let mut state = setup(); let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); + 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); - set_caller_address(OPERATOR()); + 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] @@ -82,12 +85,14 @@ fn test_slot_approvable_operator_cannot_approve_any_token() { fn test_slot_approvable_operator_cannot_transfer_anyones_value() { let mut state = setup(); let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); + 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); - set_caller_address(OPERATOR()); + 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] @@ -96,12 +101,14 @@ fn test_slot_approvable_operator_cannot_transfer_anyones_value() { fn test_slot_approvable_operator_transfer_value_revert_slot_mismatch() { let mut state = setup(); let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); + 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); - set_caller_address(OPERATOR()); + 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] @@ -110,10 +117,12 @@ fn test_slot_approvable_operator_transfer_value_revert_slot_mismatch() { fn test_slot_approvable_revoked_slot_operator_cannot_approve() { let mut state = setup(); let mut mock_state = CONTRACT_STATE(); - set_caller_address(OWNER()); + 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); - set_caller_address(OPERATOR()); + 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()); }