From 3cd9ed9d188a58f19e1c77848614674893e11b90 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 28 Jun 2024 11:33:57 +0200 Subject: [PATCH 01/20] Migrate erc20 tests (#1022) * feat: migrate event tests * feat: update workflow * feat: add foundry utils * feat: finish erc20 and erc20votes migration * feat: remove dual case tests * refactor: format files * refactor: variable name * Update src/tests/token.cairo Co-authored-by: Andrew Fleming * Update src/tests/token/erc20.cairo Co-authored-by: Andrew Fleming * Update src/tests/utils/foundry.cairo Co-authored-by: Andrew Fleming * Update src/tests/token/erc20/test_erc20_votes.cairo Co-authored-by: Andrew Fleming * feat: apply review updates --------- Co-authored-by: Andrew Fleming --- .github/workflows/test.yml | 9 +- Scarb.lock | 8 + Scarb.toml | 1 + src/tests.cairo | 29 +- src/tests/token.cairo | 4 +- src/tests/token/erc20.cairo | 1 - src/tests/token/erc20/common.cairo | 54 ++-- src/tests/token/erc20/test_dual20.cairo | 297 ------------------- src/tests/token/erc20/test_erc20.cairo | 105 ++++--- src/tests/token/erc20/test_erc20_votes.cairo | 140 +++++---- src/tests/utils.cairo | 4 + src/tests/utils/foundry.cairo | 24 ++ 12 files changed, 219 insertions(+), 457 deletions(-) delete mode 100644 src/tests/token/erc20/test_dual20.cairo create mode 100644 src/tests/utils/foundry.cairo diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 023569891..9f6fe7888 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,6 +21,13 @@ jobs: - uses: software-mansion/setup-scarb@v1 with: scarb-version: ${{ env.SCARB_VERSION }} + - name: Extract foundry version + run: | + FOUNDRY_VERSION=$(grep 'snforge_std = ' Scarb.toml | sed 's/snforge_std = .\+ tag = "v\(.*\)".*/\1/') + echo "FOUNDRY_VERSION=$FOUNDRY_VERSION" >> $GITHUB_ENV + - uses: foundry-rs/setup-snfoundry@v3 + with: + starknet-foundry-version: ${{ env.FOUNDRY_VERSION }} - name: Markdown lint uses: DavidAnson/markdownlint-cli2-action@5b7c9f74fec47e6b15667b2cc23c63dff11e449e # v9 with: @@ -30,4 +37,4 @@ jobs: - name: Cairo lint run: scarb fmt --check - name: Cairo test - run: scarb test + run: snforge test diff --git a/Scarb.lock b/Scarb.lock index 8e8b73b05..2c67a9112 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -4,3 +4,11 @@ version = 1 [[package]] name = "openzeppelin" version = "0.14.0" +dependencies = [ + "snforge_std", +] + +[[package]] +name = "snforge_std" +version = "0.25.0" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.25.0#5b366e24821e530fea97f11b211d220e8493fbea" diff --git a/Scarb.toml b/Scarb.toml index a99d0ba14..c51cc402b 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -14,6 +14,7 @@ keywords = ["openzeppelin", "starknet", "cairo", "contracts", "security", "stand [dependencies] starknet = "2.6.4" +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.25.0" } [lib] diff --git a/src/tests.cairo b/src/tests.cairo index 1f53e79de..6b45d6e2c 100644 --- a/src/tests.cairo +++ b/src/tests.cairo @@ -1,20 +1,19 @@ -#[cfg(test)] -mod access; -#[cfg(test)] -mod account; -#[cfg(test)] -mod cryptography; -#[cfg(test)] -mod introspection; -#[cfg(test)] +// #[cfg(test)] +// mod access; +// #[cfg(test)] +// mod account; +// #[cfg(test)] +// mod cryptography; +// #[cfg(test)] +// mod introspection; mod mocks; -#[cfg(test)] -mod presets; -#[cfg(test)] -mod security; +// #[cfg(test)] +// mod presets; +// #[cfg(test)] +// mod security; #[cfg(test)] mod token; -#[cfg(test)] -mod upgrades; +// #[cfg(test)] +// mod upgrades; pub mod utils; diff --git a/src/tests/token.cairo b/src/tests/token.cairo index 04f631ea8..8833c90c3 100644 --- a/src/tests/token.cairo +++ b/src/tests/token.cairo @@ -1,3 +1,3 @@ -pub(crate) mod erc1155; +// pub(crate) mod erc1155; pub(crate) mod erc20; -pub(crate) mod erc721; +// pub(crate) mod erc721; diff --git a/src/tests/token/erc20.cairo b/src/tests/token/erc20.cairo index 8d094274f..6b10a866e 100644 --- a/src/tests/token/erc20.cairo +++ b/src/tests/token/erc20.cairo @@ -1,5 +1,4 @@ pub(crate) mod common; -mod test_dual20; mod test_erc20; mod test_erc20_votes; diff --git a/src/tests/token/erc20/common.cairo b/src/tests/token/erc20/common.cairo index dfd3e7e7d..12c03ce34 100644 --- a/src/tests/token/erc20/common.cairo +++ b/src/tests/token/erc20/common.cairo @@ -1,49 +1,49 @@ -use openzeppelin::tests::utils; use openzeppelin::token::erc20::ERC20Component::{Approval, Transfer}; use openzeppelin::token::erc20::ERC20Component; use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::{EventSpy, EventAssertions}; use starknet::ContractAddress; pub(crate) fn assert_event_approval( - contract: ContractAddress, owner: ContractAddress, spender: ContractAddress, value: u256 + ref spy: EventSpy, + contract: ContractAddress, + owner: ContractAddress, + spender: ContractAddress, + value: u256 ) { - let event = utils::pop_log::(contract).unwrap(); let expected = ERC20Component::Event::Approval(Approval { owner, spender, value }); - assert!(event == expected); - - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("Approval")); - indexed_keys.append_serde(owner); - indexed_keys.append_serde(spender); - utils::assert_indexed_keys(event, indexed_keys.span()) + spy.assert_emitted(@array![(contract, expected)]); } pub(crate) fn assert_only_event_approval( - contract: ContractAddress, owner: ContractAddress, spender: ContractAddress, value: u256 + ref spy: EventSpy, + contract: ContractAddress, + owner: ContractAddress, + spender: ContractAddress, + value: u256 ) { - assert_event_approval(contract, owner, spender, value); - utils::assert_no_events_left(contract); + assert_event_approval(ref spy, contract, owner, spender, value); + assert(spy.events.len() == 0, 'Events remaining on queue'); } pub(crate) fn assert_event_transfer( - contract: ContractAddress, from: ContractAddress, to: ContractAddress, value: u256 + ref spy: EventSpy, + contract: ContractAddress, + from: ContractAddress, + to: ContractAddress, + value: u256 ) { - let event = utils::pop_log::(contract).unwrap(); let expected = ERC20Component::Event::Transfer(Transfer { from, to, value }); - assert!(event == expected); - - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("Transfer")); - indexed_keys.append_serde(from); - indexed_keys.append_serde(to); - utils::assert_indexed_keys(event, indexed_keys.span()); + spy.assert_emitted(@array![(contract, expected)]); } pub(crate) fn assert_only_event_transfer( - contract: ContractAddress, from: ContractAddress, to: ContractAddress, value: u256 + ref spy: EventSpy, + contract: ContractAddress, + from: ContractAddress, + to: ContractAddress, + value: u256 ) { - assert_event_transfer(contract, from, to, value); - utils::assert_no_events_left(contract); + assert_event_transfer(ref spy, contract, from, to, value); + assert(spy.events.len() == 0, 'Events remaining on queue'); } diff --git a/src/tests/token/erc20/test_dual20.cairo b/src/tests/token/erc20/test_dual20.cairo deleted file mode 100644 index ba9ba740d..000000000 --- a/src/tests/token/erc20/test_dual20.cairo +++ /dev/null @@ -1,297 +0,0 @@ -use openzeppelin::tests::mocks::erc20_mocks::{CamelERC20Mock, SnakeERC20Mock}; -use openzeppelin::tests::mocks::erc20_mocks::{CamelERC20Panic, SnakeERC20Panic}; -use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; -use openzeppelin::tests::utils::constants::{ - OWNER, RECIPIENT, SPENDER, OPERATOR, NAME, SYMBOL, DECIMALS, SUPPLY, VALUE -}; -use openzeppelin::tests::utils; -use openzeppelin::token::erc20::dual20::{DualCaseERC20, DualCaseERC20Trait}; -use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; -use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; -use openzeppelin::utils::serde::SerializedAppend; -use starknet::testing::set_contract_address; - -// -// Setup -// - -fn setup_snake() -> (DualCaseERC20, IERC20Dispatcher) { - let mut calldata = array![]; - calldata.append_serde(NAME()); - calldata.append_serde(SYMBOL()); - calldata.append_serde(SUPPLY); - calldata.append_serde(OWNER()); - let target = utils::deploy(SnakeERC20Mock::TEST_CLASS_HASH, calldata); - (DualCaseERC20 { contract_address: target }, IERC20Dispatcher { contract_address: target }) -} - -fn setup_camel() -> (DualCaseERC20, IERC20CamelDispatcher) { - let mut calldata = array![]; - calldata.append_serde(NAME()); - calldata.append_serde(SYMBOL()); - calldata.append_serde(SUPPLY); - calldata.append_serde(OWNER()); - let target = utils::deploy(CamelERC20Mock::TEST_CLASS_HASH, calldata); - (DualCaseERC20 { contract_address: target }, IERC20CamelDispatcher { contract_address: target }) -} - -fn setup_non_erc20() -> DualCaseERC20 { - let calldata = array![]; - let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); - DualCaseERC20 { contract_address: target } -} - -fn setup_erc20_panic() -> (DualCaseERC20, DualCaseERC20) { - let snake_target = utils::deploy(SnakeERC20Panic::TEST_CLASS_HASH, array![]); - let camel_target = utils::deploy(CamelERC20Panic::TEST_CLASS_HASH, array![]); - ( - DualCaseERC20 { contract_address: snake_target }, - DualCaseERC20 { contract_address: camel_target } - ) -} - -// -// Case agnostic methods -// - -#[test] -fn test_dual_name() { - let (snake_dispatcher, _) = setup_snake(); - assert_eq!(snake_dispatcher.name(), NAME()); - - let (camel_dispatcher, _) = setup_camel(); - assert_eq!(camel_dispatcher.name(), NAME()); -} - -#[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] -fn test_dual_no_name() { - let dispatcher = setup_non_erc20(); - dispatcher.name(); -} - -#[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] -fn test_dual_name_exists_and_panics() { - let (dispatcher, _) = setup_erc20_panic(); - dispatcher.name(); -} - -#[test] -fn test_dual_symbol() { - let (snake_dispatcher, _) = setup_snake(); - let (camel_dispatcher, _) = setup_camel(); - assert_eq!(snake_dispatcher.symbol(), SYMBOL()); - assert_eq!(camel_dispatcher.symbol(), SYMBOL()); -} - -#[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] -fn test_dual_no_symbol() { - let dispatcher = setup_non_erc20(); - dispatcher.symbol(); -} - -#[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] -fn test_dual_symbol_exists_and_panics() { - let (dispatcher, _) = setup_erc20_panic(); - dispatcher.symbol(); -} - -#[test] -fn test_dual_decimals() { - let (snake_dispatcher, _) = setup_snake(); - let (camel_dispatcher, _) = setup_camel(); - assert_eq!(snake_dispatcher.decimals(), DECIMALS); - assert_eq!(camel_dispatcher.decimals(), DECIMALS); -} - -#[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] -fn test_dual_no_decimals() { - let dispatcher = setup_non_erc20(); - dispatcher.decimals(); -} - -#[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] -fn test_dual_decimals_exists_and_panics() { - let (dispatcher, _) = setup_erc20_panic(); - dispatcher.decimals(); -} - -#[test] -fn test_dual_transfer() { - let (snake_dispatcher, snake_target) = setup_snake(); - set_contract_address(OWNER()); - assert!(snake_dispatcher.transfer(RECIPIENT(), VALUE)); - assert_eq!(snake_target.balance_of(RECIPIENT()), VALUE); - - let (camel_dispatcher, camel_target) = setup_camel(); - set_contract_address(OWNER()); - assert!(camel_dispatcher.transfer(RECIPIENT(), VALUE)); - assert_eq!(camel_target.balanceOf(RECIPIENT()), VALUE); -} - -#[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] -fn test_dual_no_transfer() { - let dispatcher = setup_non_erc20(); - dispatcher.transfer(RECIPIENT(), VALUE); -} - -#[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] -fn test_dual_transfer_exists_and_panics() { - let (dispatcher, _) = setup_erc20_panic(); - dispatcher.transfer(RECIPIENT(), VALUE); -} - -#[test] -fn test_dual_approve() { - let (snake_dispatcher, snake_target) = setup_snake(); - set_contract_address(OWNER()); - assert!(snake_dispatcher.approve(SPENDER(), VALUE)); - - let snake_allowance = snake_target.allowance(OWNER(), SPENDER()); - assert_eq!(snake_allowance, VALUE); - - let (camel_dispatcher, camel_target) = setup_camel(); - set_contract_address(OWNER()); - assert!(camel_dispatcher.approve(SPENDER(), VALUE)); - - let camel_allowance = camel_target.allowance(OWNER(), SPENDER()); - assert_eq!(camel_allowance, VALUE); -} - -#[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] -fn test_dual_no_approve() { - let dispatcher = setup_non_erc20(); - dispatcher.approve(SPENDER(), VALUE); -} - -#[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] -fn test_dual_approve_exists_and_panics() { - let (dispatcher, _) = setup_erc20_panic(); - dispatcher.approve(SPENDER(), VALUE); -} - -// -// snake_case target -// - -#[test] -fn test_dual_total_supply() { - let (dispatcher, _) = setup_snake(); - assert_eq!(dispatcher.total_supply(), SUPPLY); -} - -#[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] -fn test_dual_no_total_supply() { - let dispatcher = setup_non_erc20(); - dispatcher.total_supply(); -} - -#[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] -fn test_dual_total_supply_exists_and_panics() { - let (dispatcher, _) = setup_erc20_panic(); - dispatcher.total_supply(); -} - -#[test] -fn test_dual_balance_of() { - let (dispatcher, _) = setup_snake(); - assert_eq!(dispatcher.balance_of(OWNER()), SUPPLY); -} - -#[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] -fn test_dual_no_balance_of() { - let dispatcher = setup_non_erc20(); - dispatcher.balance_of(OWNER()); -} - -#[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] -fn test_dual_balance_of_exists_and_panics() { - let (dispatcher, _) = setup_erc20_panic(); - dispatcher.balance_of(OWNER()); -} - -#[test] -fn test_dual_transfer_from() { - let (dispatcher, target) = setup_snake(); - set_contract_address(OWNER()); - target.approve(OPERATOR(), VALUE); - - set_contract_address(OPERATOR()); - dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); - assert_eq!(target.balance_of(RECIPIENT()), VALUE); -} - -#[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] -fn test_dual_no_transfer_from() { - let dispatcher = setup_non_erc20(); - dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); -} - -#[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] -fn test_dual_transfer_from_exists_and_panics() { - let (dispatcher, _) = setup_erc20_panic(); - dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); -} - -// -// camelCase target -// - -#[test] -fn test_dual_totalSupply() { - let (dispatcher, _) = setup_camel(); - assert_eq!(dispatcher.total_supply(), SUPPLY); -} - -#[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] -fn test_dual_totalSupply_exists_and_panics() { - let (_, dispatcher) = setup_erc20_panic(); - dispatcher.total_supply(); -} - -#[test] -fn test_dual_balanceOf() { - let (dispatcher, _) = setup_camel(); - assert_eq!(dispatcher.balance_of(OWNER()), SUPPLY); -} - -#[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] -fn test_dual_balanceOf_exists_and_panics() { - let (_, dispatcher) = setup_erc20_panic(); - dispatcher.balance_of(OWNER()); -} - -#[test] -fn test_dual_transferFrom() { - let (dispatcher, target) = setup_camel(); - set_contract_address(OWNER()); - target.approve(OPERATOR(), VALUE); - - set_contract_address(OPERATOR()); - dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); - assert_eq!(target.balanceOf(RECIPIENT()), VALUE); -} - -#[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] -fn test_dual_transferFrom_exists_and_panics() { - let (_, dispatcher) = setup_erc20_panic(); - dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); -} diff --git a/src/tests/token/erc20/test_erc20.cairo b/src/tests/token/erc20/test_erc20.cairo index 2c41a7971..c83e16576 100644 --- a/src/tests/token/erc20/test_erc20.cairo +++ b/src/tests/token/erc20/test_erc20.cairo @@ -4,13 +4,11 @@ use openzeppelin::tests::utils::constants::{ ZERO, OWNER, SPENDER, RECIPIENT, NAME, SYMBOL, DECIMALS, SUPPLY, VALUE }; use openzeppelin::tests::utils; -use openzeppelin::token::erc20::ERC20Component::{Approval, Transfer}; use openzeppelin::token::erc20::ERC20Component::{ERC20CamelOnlyImpl, ERC20Impl}; use openzeppelin::token::erc20::ERC20Component::{ERC20MetadataImpl, InternalImpl}; use openzeppelin::token::erc20::ERC20Component; -use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::{test_address, start_cheat_caller_address}; use starknet::ContractAddress; -use starknet::testing; use super::common::{assert_event_approval, assert_only_event_approval, assert_only_event_transfer}; @@ -28,7 +26,6 @@ fn setup() -> ComponentState { let mut state = COMPONENT_STATE(); state.initializer(NAME(), SYMBOL()); state.mint(OWNER(), SUPPLY); - utils::drop_event(ZERO()); state } @@ -82,7 +79,7 @@ fn test_balanceOf() { #[test] fn test_allowance() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.approve(SPENDER(), VALUE); let allowance = state.allowance(OWNER(), SPENDER()); @@ -96,10 +93,13 @@ fn test_allowance() { #[test] fn test_approve() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let contract_address = test_address(); + let mut spy = utils::spy_on(contract_address); + + start_cheat_caller_address(contract_address, OWNER()); assert!(state.approve(SPENDER(), VALUE)); - assert_only_event_approval(ZERO(), OWNER(), SPENDER(), VALUE); + assert_only_event_approval(ref spy, contract_address, OWNER(), SPENDER(), VALUE); let allowance = state.allowance(OWNER(), SPENDER()); assert_eq!(allowance, VALUE); @@ -116,17 +116,20 @@ fn test_approve_from_zero() { #[should_panic(expected: ('ERC20: approve to 0',))] fn test_approve_to_zero() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.approve(ZERO(), VALUE); } #[test] fn test__approve() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let contract_address = test_address(); + let mut spy = utils::spy_on(contract_address); + + start_cheat_caller_address(contract_address, OWNER()); state._approve(OWNER(), SPENDER(), VALUE); - assert_only_event_approval(ZERO(), OWNER(), SPENDER(), VALUE); + assert_only_event_approval(ref spy, contract_address, OWNER(), SPENDER(), VALUE); let allowance = state.allowance(OWNER(), SPENDER()); assert_eq!(allowance, VALUE); @@ -143,7 +146,7 @@ fn test__approve_from_zero() { #[should_panic(expected: ('ERC20: approve to 0',))] fn test__approve_to_zero() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state._approve(OWNER(), ZERO(), VALUE); } @@ -154,10 +157,13 @@ fn test__approve_to_zero() { #[test] fn test_transfer() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let contract_address = test_address(); + let mut spy = utils::spy_on(contract_address); + + start_cheat_caller_address(contract_address, OWNER()); assert!(state.transfer(RECIPIENT(), VALUE)); - assert_only_event_transfer(ZERO(), OWNER(), RECIPIENT(), VALUE); + assert_only_event_transfer(ref spy, contract_address, OWNER(), RECIPIENT(), VALUE); assert_eq!(state.balance_of(RECIPIENT()), VALUE); assert_eq!(state.balance_of(OWNER()), SUPPLY - VALUE); assert_eq!(state.total_supply(), SUPPLY); @@ -167,7 +173,7 @@ fn test_transfer() { #[should_panic(expected: ('ERC20: insufficient balance',))] fn test_transfer_not_enough_balance() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); let balance_plus_one = SUPPLY + 1; state.transfer(RECIPIENT(), balance_plus_one); @@ -184,17 +190,19 @@ fn test_transfer_from_zero() { #[should_panic(expected: ('ERC20: transfer to 0',))] fn test_transfer_to_zero() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.transfer(ZERO(), VALUE); } #[test] fn test__transfer() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = utils::spy_on(contract_address); state._transfer(OWNER(), RECIPIENT(), VALUE); - assert_only_event_transfer(ZERO(), OWNER(), RECIPIENT(), VALUE); + assert_only_event_transfer(ref spy, contract_address, OWNER(), RECIPIENT(), VALUE); assert_eq!(state.balance_of(RECIPIENT()), VALUE); assert_eq!(state.balance_of(OWNER()), SUPPLY - VALUE); assert_eq!(state.total_supply(), SUPPLY); @@ -204,7 +212,7 @@ fn test__transfer() { #[should_panic(expected: ('ERC20: insufficient balance',))] fn test__transfer_not_enough_balance() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); let balance_plus_one = SUPPLY + 1; state._transfer(OWNER(), RECIPIENT(), balance_plus_one); @@ -231,15 +239,17 @@ fn test__transfer_to_zero() { #[test] fn test_transfer_from() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let contract_address = test_address(); + + start_cheat_caller_address(contract_address, OWNER()); state.approve(SPENDER(), VALUE); - utils::drop_event(ZERO()); - testing::set_caller_address(SPENDER()); + let mut spy = utils::spy_on(contract_address); + start_cheat_caller_address(contract_address, SPENDER()); assert!(state.transfer_from(OWNER(), RECIPIENT(), VALUE)); - assert_event_approval(ZERO(), OWNER(), SPENDER(), 0); - assert_only_event_transfer(ZERO(), OWNER(), RECIPIENT(), VALUE); + assert_event_approval(ref spy, contract_address, OWNER(), SPENDER(), 0); + assert_only_event_transfer(ref spy, contract_address, OWNER(), RECIPIENT(), VALUE); let allowance = state.allowance(OWNER(), SPENDER()); assert_eq!(allowance, 0); @@ -252,10 +262,10 @@ fn test_transfer_from() { #[test] fn test_transfer_from_doesnt_consume_infinite_allowance() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.approve(SPENDER(), BoundedInt::max()); - testing::set_caller_address(SPENDER()); + start_cheat_caller_address(test_address(), SPENDER()); state.transfer_from(OWNER(), RECIPIENT(), VALUE); let allowance = state.allowance(OWNER(), SPENDER()); @@ -266,10 +276,10 @@ fn test_transfer_from_doesnt_consume_infinite_allowance() { #[should_panic(expected: ('ERC20: insufficient allowance',))] fn test_transfer_from_greater_than_allowance() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.approve(SPENDER(), VALUE); - testing::set_caller_address(SPENDER()); + start_cheat_caller_address(test_address(), SPENDER()); let allowance_plus_one = VALUE + 1; state.transfer_from(OWNER(), RECIPIENT(), allowance_plus_one); } @@ -278,10 +288,10 @@ fn test_transfer_from_greater_than_allowance() { #[should_panic(expected: ('ERC20: transfer to 0',))] fn test_transfer_from_to_zero_address() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.approve(SPENDER(), VALUE); - testing::set_caller_address(SPENDER()); + start_cheat_caller_address(test_address(), SPENDER()); state.transfer_from(OWNER(), ZERO(), VALUE); } @@ -295,15 +305,17 @@ fn test_transfer_from_from_zero_address() { #[test] fn test_transferFrom() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let contract_address = test_address(); + + start_cheat_caller_address(contract_address, OWNER()); state.approve(SPENDER(), VALUE); - utils::drop_event(ZERO()); - testing::set_caller_address(SPENDER()); + let mut spy = utils::spy_on(contract_address); + start_cheat_caller_address(contract_address, SPENDER()); assert!(state.transferFrom(OWNER(), RECIPIENT(), VALUE)); - assert_event_approval(ZERO(), OWNER(), SPENDER(), 0); - assert_only_event_transfer(ZERO(), OWNER(), RECIPIENT(), VALUE); + assert_event_approval(ref spy, contract_address, OWNER(), SPENDER(), 0); + assert_only_event_transfer(ref spy, contract_address, OWNER(), RECIPIENT(), VALUE); let allowance = state.allowance(OWNER(), SPENDER()); assert_eq!(allowance, 0); @@ -317,10 +329,10 @@ fn test_transferFrom() { #[test] fn test_transferFrom_doesnt_consume_infinite_allowance() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.approve(SPENDER(), BoundedInt::max()); - testing::set_caller_address(SPENDER()); + start_cheat_caller_address(test_address(), SPENDER()); state.transferFrom(OWNER(), RECIPIENT(), VALUE); let allowance = state.allowance(OWNER(), SPENDER()); @@ -331,10 +343,10 @@ fn test_transferFrom_doesnt_consume_infinite_allowance() { #[should_panic(expected: ('ERC20: insufficient allowance',))] fn test_transferFrom_greater_than_allowance() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.approve(SPENDER(), VALUE); - testing::set_caller_address(SPENDER()); + start_cheat_caller_address(test_address(), SPENDER()); let allowance_plus_one = VALUE + 1; state.transferFrom(OWNER(), RECIPIENT(), allowance_plus_one); } @@ -343,10 +355,10 @@ fn test_transferFrom_greater_than_allowance() { #[should_panic(expected: ('ERC20: transfer to 0',))] fn test_transferFrom_to_zero_address() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.approve(SPENDER(), VALUE); - testing::set_caller_address(SPENDER()); + start_cheat_caller_address(test_address(), SPENDER()); state.transferFrom(OWNER(), ZERO(), VALUE); } @@ -364,13 +376,14 @@ fn test_transferFrom_from_zero_address() { #[test] fn test__spend_allowance_not_unlimited() { let mut state = setup(); + let contract_address = test_address(); state._approve(OWNER(), SPENDER(), SUPPLY); - utils::drop_event(ZERO()); + let mut spy = utils::spy_on(contract_address); state._spend_allowance(OWNER(), SPENDER(), VALUE); - assert_only_event_approval(ZERO(), OWNER(), SPENDER(), SUPPLY - VALUE); + assert_only_event_approval(ref spy, contract_address, OWNER(), SPENDER(), SUPPLY - VALUE); let allowance = state.allowance(OWNER(), SPENDER()); assert_eq!(allowance, SUPPLY - VALUE); @@ -395,9 +408,12 @@ fn test__spend_allowance_unlimited() { #[test] fn test_mint() { let mut state = COMPONENT_STATE(); + let contract_address = test_address(); + + let mut spy = utils::spy_on(contract_address); state.mint(OWNER(), VALUE); - assert_only_event_transfer(ZERO(), ZERO(), OWNER(), VALUE); + assert_only_event_transfer(ref spy, contract_address, ZERO(), OWNER(), VALUE); assert_eq!(state.balance_of(OWNER()), VALUE); assert_eq!(state.total_supply(), VALUE); } @@ -416,9 +432,12 @@ fn test_mint_to_zero() { #[test] fn test_burn() { let mut state = setup(); + let contract_address = test_address(); + + let mut spy = utils::spy_on(contract_address); state.burn(OWNER(), VALUE); - assert_only_event_transfer(ZERO(), OWNER(), ZERO(), VALUE); + assert_only_event_transfer(ref spy, contract_address, OWNER(), ZERO(), VALUE); assert_eq!(state.total_supply(), SUPPLY - VALUE); assert_eq!(state.balance_of(OWNER()), SUPPLY - VALUE); } diff --git a/src/tests/token/erc20/test_erc20_votes.cairo b/src/tests/token/erc20/test_erc20_votes.cairo index e241e891c..f8453ebd6 100644 --- a/src/tests/token/erc20/test_erc20_votes.cairo +++ b/src/tests/token/erc20/test_erc20_votes.cairo @@ -1,9 +1,8 @@ use core::integer::BoundedInt; use core::num::traits::Zero; -use openzeppelin::tests::mocks::account_mocks::DualCaseAccountMock; use openzeppelin::tests::mocks::erc20_votes_mocks::DualCaseERC20VotesMock::SNIP12MetadataImpl; use openzeppelin::tests::mocks::erc20_votes_mocks::DualCaseERC20VotesMock; -use openzeppelin::tests::utils::constants::{SUPPLY, ZERO, OWNER, PUBKEY, RECIPIENT}; +use openzeppelin::tests::utils::constants::{SUPPLY, ZERO, OWNER, RECIPIENT}; use openzeppelin::tests::utils; use openzeppelin::token::erc20::ERC20Component::InternalImpl as ERC20Impl; use openzeppelin::token::erc20::extensions::ERC20VotesComponent::{ @@ -14,13 +13,16 @@ use openzeppelin::token::erc20::extensions::ERC20VotesComponent; use openzeppelin::token::erc20::extensions::erc20_votes::Delegation; use openzeppelin::utils::cryptography::snip12::OffchainMessageHash; use openzeppelin::utils::serde::SerializedAppend; -use openzeppelin::utils::structs::checkpoint::{Checkpoint, Trace, TraceTrait}; +use openzeppelin::utils::structs::checkpoint::{Checkpoint, TraceTrait}; +use snforge_std::signature::KeyPairTrait; +use snforge_std::signature::stark_curve::{StarkCurveKeyPairImpl, StarkCurveSignerImpl}; +use snforge_std::{ + cheat_block_timestamp_global, start_cheat_caller_address, cheat_chain_id_global, test_address +}; +use snforge_std::{EventSpy, EventAssertions}; use starknet::ContractAddress; use starknet::contract_address_const; use starknet::storage::{StorageMapMemberAccessTrait, StorageMemberAccessTrait}; -use starknet::testing; - -use super::common::{assert_event_approval, assert_only_event_approval, assert_only_event_transfer}; // // Setup @@ -41,13 +43,12 @@ fn setup() -> ComponentState { mock_state.erc20.mint(OWNER(), SUPPLY); state.transfer_voting_units(ZERO(), OWNER(), SUPPLY); - utils::drop_event(ZERO()); state } -fn setup_account() -> ContractAddress { - let mut calldata = array![0x26da8d11938b76025862be14fdb8b28438827f73e75e86f7bfa38b196951fa7]; - utils::deploy(DualCaseAccountMock::TEST_CLASS_HASH, calldata) +fn setup_account(public_key: felt252) -> ContractAddress { + let mut calldata = array![public_key]; + utils::declare_and_deploy("DualCaseAccountMock", calldata) } // Checkpoints unordered insertion @@ -58,7 +59,7 @@ fn test__delegate_checkpoints_unordered_insertion() { let mut state = setup(); let mut trace = state.ERC20Votes_delegate_checkpoints.read(OWNER()); - testing::set_block_timestamp('ts10'); + cheat_block_timestamp_global('ts10'); trace.push('ts2', 0x222); trace.push('ts1', 0x111); } @@ -69,7 +70,7 @@ fn test__total_checkpoints_unordered_insertion() { let mut state = setup(); let mut trace = state.ERC20Votes_total_checkpoints.read(); - testing::set_block_timestamp('ts10'); + cheat_block_timestamp_global('ts10'); trace.push('ts2', 0x222); trace.push('ts1', 0x111); } @@ -81,8 +82,7 @@ fn test__total_checkpoints_unordered_insertion() { #[test] fn test_get_votes() { let mut state = setup(); - - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.delegate(OWNER()); assert_eq!(state.get_votes(OWNER()), SUPPLY); @@ -94,7 +94,7 @@ fn test_get_past_votes() { let mut trace = state.ERC20Votes_delegate_checkpoints.read(OWNER()); // Future timestamp. - testing::set_block_timestamp('ts10'); + cheat_block_timestamp_global('ts10'); trace.push('ts1', 0x111); trace.push('ts2', 0x222); trace.push('ts3', 0x333); @@ -118,7 +118,7 @@ fn test_get_past_votes_future_lookup() { let state = setup(); // Past timestamp. - testing::set_block_timestamp('ts1'); + cheat_block_timestamp_global('ts1'); state.get_past_votes(OWNER(), 'ts2'); } @@ -128,7 +128,7 @@ fn test_get_past_total_supply() { let mut trace = state.ERC20Votes_total_checkpoints.read(); // Future timestamp. - testing::set_block_timestamp('ts10'); + cheat_block_timestamp_global('ts10'); trace.push('ts1', 0x111); trace.push('ts2', 0x222); trace.push('ts3', 0x333); @@ -152,7 +152,7 @@ fn test_get_past_total_supply_future_lookup() { let state = setup(); // Past timestamp. - testing::set_block_timestamp('ts1'); + cheat_block_timestamp_global('ts1'); state.get_past_total_supply('ts2'); } @@ -163,41 +163,44 @@ fn test_get_past_total_supply_future_lookup() { #[test] fn test_delegate() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let contract_address = test_address(); + let mut spy = utils::spy_on(contract_address); + + start_cheat_caller_address(contract_address, OWNER()); // Delegate from zero state.delegate(OWNER()); - assert_event_delegate_changed(ZERO(), OWNER(), ZERO(), OWNER()); - assert_only_event_delegate_votes_changed(ZERO(), OWNER(), 0, SUPPLY); + assert_event_delegate_changed(ref spy, contract_address, OWNER(), ZERO(), OWNER()); + assert_only_event_delegate_votes_changed(ref spy, contract_address, OWNER(), 0, SUPPLY); assert_eq!(state.get_votes(OWNER()), SUPPLY); // Delegate from non-zero to non-zero state.delegate(RECIPIENT()); - assert_event_delegate_changed(ZERO(), OWNER(), OWNER(), RECIPIENT()); - assert_event_delegate_votes_changed(ZERO(), OWNER(), SUPPLY, 0); - assert_only_event_delegate_votes_changed(ZERO(), RECIPIENT(), 0, SUPPLY); + assert_event_delegate_changed(ref spy, contract_address, OWNER(), OWNER(), RECIPIENT()); + assert_event_delegate_votes_changed(ref spy, contract_address, OWNER(), SUPPLY, 0); + assert_only_event_delegate_votes_changed(ref spy, contract_address, RECIPIENT(), 0, SUPPLY); assert!(state.get_votes(OWNER()).is_zero()); assert_eq!(state.get_votes(RECIPIENT()), SUPPLY); // Delegate to zero state.delegate(ZERO()); - assert_event_delegate_changed(ZERO(), OWNER(), RECIPIENT(), ZERO()); - assert_event_delegate_votes_changed(ZERO(), RECIPIENT(), SUPPLY, 0); + assert_event_delegate_changed(ref spy, contract_address, OWNER(), RECIPIENT(), ZERO()); + assert_event_delegate_votes_changed(ref spy, contract_address, RECIPIENT(), SUPPLY, 0); assert!(state.get_votes(RECIPIENT()).is_zero()); // Delegate from zero to zero state.delegate(ZERO()); - assert_only_event_delegate_changed(ZERO(), OWNER(), ZERO(), ZERO()); + assert_only_event_delegate_changed(ref spy, contract_address, OWNER(), ZERO(), ZERO()); } #[test] fn test_delegates() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.delegate(OWNER()); assert_eq!(state.delegates(OWNER()), OWNER()); @@ -210,12 +213,12 @@ fn test_delegates() { #[test] fn test_delegate_by_sig_hash_generation() { - testing::set_chain_id('SN_TEST'); + cheat_chain_id_global('SN_TEST'); let nonce = 0; let expiry = 'ts2'; let delegator = contract_address_const::< - 0x19dcd9e412145354a3328fb68b5975bded85972893eb42eed11355d4cfbb58a + 0x70b0526a4bfbc9ca717c96aeb5a8afac85181f4585662273668928585a0d628 >(); let delegatee = RECIPIENT(); let delegation = Delegation { delegatee, nonce, expiry }; @@ -226,50 +229,51 @@ fn test_delegate_by_sig_hash_generation() { // - name: 'DAPP_NAME' // - version: 'DAPP_VERSION' // - chainId: 'SN_TEST' - // - account: 0x19dcd9e412145354a3328fb68b5975bded85972893eb42eed11355d4cfbb58a + // - account: 0x70b0526a4bfbc9ca717c96aeb5a8afac85181f4585662273668928585a0d628 // - delegatee: 'RECIPIENT' // - nonce: 0 // - expiry: 'ts2' // - revision: '1' - let expected_hash = 0x5b9e8190392425e06024b1eedfbbe9dd3631ddd07a84154185d39ec1d657511; + let expected_hash = 0x314bd38b22b62d576691d8dafd9f8ea0601329ebe686bc64ca28e4d8821d5a0; assert_eq!(hash, expected_hash); } #[test] fn test_delegate_by_sig() { + cheat_chain_id_global('SN_TEST'); + cheat_block_timestamp_global('ts1'); + let mut state = setup(); - let account = setup_account(); - testing::set_chain_id('SN_TEST'); + let contract_address = test_address(); + let key_pair = KeyPairTrait::::generate(); + let account = setup_account(key_pair.public_key); let nonce = 0; let expiry = 'ts2'; let delegator = account; let delegatee = RECIPIENT(); - // This signature was computed using starknet js sdk from the following values: - // - private_key: '1234' - // - public_key: 0x26da8d11938b76025862be14fdb8b28438827f73e75e86f7bfa38b196951fa7 - // - msg_hash: 0x5b9e8190392425e06024b1eedfbbe9dd3631ddd07a84154185d39ec1d657511 - let signature = array![ - 0x4b2ca5c3cb47eafc1263db0fb7a1c4ee54eb9cc6605607a072894c0a9ae3b08, - 0x313dc5b5f05ab680db7d51b391fadd52e679c971551f0017a8ceba37bacc5c6 - ]; + let delegation = Delegation { delegatee, nonce, expiry }; + let msg_hash = delegation.get_message_hash(delegator); + let (r, s) = key_pair.sign(msg_hash).unwrap(); - testing::set_block_timestamp('ts1'); - state.delegate_by_sig(delegator, delegatee, nonce, expiry, signature); + let mut spy = utils::spy_on(contract_address); - assert_only_event_delegate_changed(ZERO(), delegator, ZERO(), delegatee); + state.delegate_by_sig(delegator, delegatee, nonce, expiry, array![r, s]); + + assert_only_event_delegate_changed(ref spy, contract_address, delegator, ZERO(), delegatee); assert_eq!(state.delegates(account), delegatee); } #[test] #[should_panic(expected: ('Votes: expired signature',))] fn test_delegate_by_sig_past_expiry() { + cheat_block_timestamp_global('ts5'); + let mut state = setup(); let expiry = 'ts4'; let signature = array![0, 0]; - testing::set_block_timestamp('ts5'); state.delegate_by_sig(OWNER(), RECIPIENT(), 0, expiry, signature); } @@ -286,7 +290,7 @@ fn test_delegate_by_sig_invalid_nonce() { #[should_panic(expected: ('Votes: invalid signature',))] fn test_delegate_by_sig_invalid_signature() { let mut state = setup(); - let account = setup_account(); + let account = setup_account(0x123); let signature = array![0, 0]; state.delegate_by_sig(account, RECIPIENT(), 0, 0, signature); @@ -351,55 +355,49 @@ fn test_get_voting_units() { // fn assert_event_delegate_changed( + ref spy: EventSpy, contract: ContractAddress, delegator: ContractAddress, from_delegate: ContractAddress, to_delegate: ContractAddress ) { - let event = utils::pop_log::(contract).unwrap(); let expected = ERC20VotesComponent::Event::DelegateChanged( DelegateChanged { delegator, from_delegate, to_delegate } ); - assert!(event == expected); - - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("DelegateChanged")); - indexed_keys.append_serde(delegator); - indexed_keys.append_serde(from_delegate); - indexed_keys.append_serde(to_delegate); - utils::assert_indexed_keys(event, indexed_keys.span()) + spy.assert_emitted(@array![(contract, expected)]); } fn assert_only_event_delegate_changed( + ref spy: EventSpy, contract: ContractAddress, delegator: ContractAddress, from_delegate: ContractAddress, to_delegate: ContractAddress ) { - assert_event_delegate_changed(contract, delegator, from_delegate, to_delegate); - utils::assert_no_events_left(contract); + assert_event_delegate_changed(ref spy, contract, delegator, from_delegate, to_delegate); + assert(spy.events.len() == 0, 'Events remaining on queue'); } fn assert_event_delegate_votes_changed( - contract: ContractAddress, delegate: ContractAddress, previous_votes: u256, new_votes: u256 + ref spy: EventSpy, + contract: ContractAddress, + delegate: ContractAddress, + previous_votes: u256, + new_votes: u256 ) { - let event = utils::pop_log::(contract).unwrap(); let expected = ERC20VotesComponent::Event::DelegateVotesChanged( DelegateVotesChanged { delegate, previous_votes, new_votes } ); - assert!(event == expected); - - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("DelegateVotesChanged")); - indexed_keys.append_serde(delegate); - utils::assert_indexed_keys(event, indexed_keys.span()) + spy.assert_emitted(@array![(contract, expected)]); } fn assert_only_event_delegate_votes_changed( - contract: ContractAddress, delegate: ContractAddress, previous_votes: u256, new_votes: u256 + ref spy: EventSpy, + contract: ContractAddress, + delegate: ContractAddress, + previous_votes: u256, + new_votes: u256 ) { - assert_event_delegate_votes_changed(contract, delegate, previous_votes, new_votes); - utils::assert_no_events_left(contract); + assert_event_delegate_votes_changed(ref spy, contract, delegate, previous_votes, new_votes); + assert(spy.events.len() == 0, 'Events remaining on queue'); } diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index 658b94645..6edad0000 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -1,4 +1,8 @@ pub(crate) mod constants; +pub mod foundry; +pub use foundry::declare_and_deploy; + +pub use foundry::spy_on; use starknet::ContractAddress; use starknet::SyscallResultTrait; diff --git a/src/tests/utils/foundry.cairo b/src/tests/utils/foundry.cairo new file mode 100644 index 000000000..d624d9a79 --- /dev/null +++ b/src/tests/utils/foundry.cairo @@ -0,0 +1,24 @@ +use snforge_std::{declare, ContractClassTrait, spy_events, EventSpy, SpyOn}; +use starknet::ContractAddress; + +pub fn declare_and_deploy(contract_name: ByteArray, calldata: Array) -> ContractAddress { + let contract_class = declare(contract_name).unwrap(); + match contract_class.deploy(@calldata) { + Result::Ok((contract_address, _)) => contract_address, + Result::Err(panic_data) => panic!("Failed to deploy, error: ${:?}", panic_data) + } +} + +pub fn declare_and_deploy_at( + contract_name: ByteArray, target_address: ContractAddress, calldata: Array +) { + let contract_class = declare(contract_name).unwrap(); + if let Result::Err(panic_data) = contract_class.deploy_at(@calldata, target_address) { + panic!("Failed to deploy, error: ${:?}", panic_data) + } +} + +pub fn spy_on(contract_address: ContractAddress) -> EventSpy { + spy_events(SpyOn::One(contract_address)) +} + From a96b50a4b952eaa3fd987e17fe3ecf7440da1870 Mon Sep 17 00:00:00 2001 From: immrsd <103599616+immrsd@users.noreply.github.com> Date: Thu, 4 Jul 2024 11:11:28 +0200 Subject: [PATCH 02/20] Snforge utils and some common changes (#1030) * Update gitignore with snfoundry cache dir * Delete foundry utils file * Add snfoundry-related test utils, comment out previous utils * Update ERC20 event helpers * Update ERC20 tests * Update ERC20Votes tests * Run linter * Add util function to drop single event * Fix review issues * Make Github workflow run on every PR * Fix review issues * Bump snforge version * Support snforge 0.26 in test utils * Update ERC20 tests to support snforge upgrade * Run linter --- .github/workflows/test.yml | 2 - .gitignore | 3 + Scarb.lock | 4 +- Scarb.toml | 2 +- src/tests/token.cairo | 2 +- src/tests/token/erc20/common.cairo | 84 +++++++------ src/tests/token/erc20/test_erc20.cairo | 44 +++---- src/tests/token/erc20/test_erc20_votes.cairo | 123 ++++++++++--------- src/tests/utils.cairo | 76 +----------- src/tests/utils/common.cairo | 34 +++++ src/tests/utils/events.cairo | 62 ++++++++++ src/tests/utils/foundry.cairo | 24 ---- src/tests/utils/signing.cairo | 11 ++ 13 files changed, 249 insertions(+), 222 deletions(-) create mode 100644 src/tests/utils/common.cairo create mode 100644 src/tests/utils/events.cairo delete mode 100644 src/tests/utils/foundry.cairo create mode 100644 src/tests/utils/signing.cairo diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9f6fe7888..1d37a35d6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,8 +2,6 @@ name: Lint and test on: pull_request: - branches: - - main push: branches: - main diff --git a/.gitignore b/.gitignore index 869665ba4..2956e1b18 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ corelib/ # Scarb target/ +# Starknet Foundry +.snfoundry_cache/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/Scarb.lock b/Scarb.lock index 2c67a9112..524489827 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -10,5 +10,5 @@ dependencies = [ [[package]] name = "snforge_std" -version = "0.25.0" -source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.25.0#5b366e24821e530fea97f11b211d220e8493fbea" +version = "0.26.0" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.26.0#50eb589db65e113efe4f09241feb59b574228c7e" diff --git a/Scarb.toml b/Scarb.toml index c51cc402b..935224176 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -14,7 +14,7 @@ keywords = ["openzeppelin", "starknet", "cairo", "contracts", "security", "stand [dependencies] starknet = "2.6.4" -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.25.0" } +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.26.0" } [lib] diff --git a/src/tests/token.cairo b/src/tests/token.cairo index 8833c90c3..25979f784 100644 --- a/src/tests/token.cairo +++ b/src/tests/token.cairo @@ -1,3 +1,3 @@ // pub(crate) mod erc1155; -pub(crate) mod erc20; // pub(crate) mod erc721; +pub(crate) mod erc20; diff --git a/src/tests/token/erc20/common.cairo b/src/tests/token/erc20/common.cairo index 12c03ce34..be65d0241 100644 --- a/src/tests/token/erc20/common.cairo +++ b/src/tests/token/erc20/common.cairo @@ -1,49 +1,53 @@ +use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::token::erc20::ERC20Component::{Approval, Transfer}; use openzeppelin::token::erc20::ERC20Component; use openzeppelin::utils::serde::SerializedAppend; -use snforge_std::{EventSpy, EventAssertions}; +use snforge_std::EventSpy; use starknet::ContractAddress; -pub(crate) fn assert_event_approval( - ref spy: EventSpy, - contract: ContractAddress, - owner: ContractAddress, - spender: ContractAddress, - value: u256 -) { - let expected = ERC20Component::Event::Approval(Approval { owner, spender, value }); - spy.assert_emitted(@array![(contract, expected)]); -} +#[generate_trait] +pub(crate) impl ERC20SpyHelpersImpl of ERC20SpyHelpers { + fn assert_event_approval( + ref self: EventSpy, + contract: ContractAddress, + owner: ContractAddress, + spender: ContractAddress, + value: u256 + ) { + let expected = ERC20Component::Event::Approval(Approval { owner, spender, value }); + self.assert_emitted_single(contract, expected); + } -pub(crate) fn assert_only_event_approval( - ref spy: EventSpy, - contract: ContractAddress, - owner: ContractAddress, - spender: ContractAddress, - value: u256 -) { - assert_event_approval(ref spy, contract, owner, spender, value); - assert(spy.events.len() == 0, 'Events remaining on queue'); -} + fn assert_only_event_approval( + ref self: EventSpy, + contract: ContractAddress, + owner: ContractAddress, + spender: ContractAddress, + value: u256 + ) { + self.assert_event_approval(contract, owner, spender, value); + self.assert_no_events_left_from(contract); + } -pub(crate) fn assert_event_transfer( - ref spy: EventSpy, - contract: ContractAddress, - from: ContractAddress, - to: ContractAddress, - value: u256 -) { - let expected = ERC20Component::Event::Transfer(Transfer { from, to, value }); - spy.assert_emitted(@array![(contract, expected)]); -} + fn assert_event_transfer( + ref self: EventSpy, + contract: ContractAddress, + from: ContractAddress, + to: ContractAddress, + value: u256 + ) { + let expected = ERC20Component::Event::Transfer(Transfer { from, to, value }); + self.assert_emitted_single(contract, expected); + } -pub(crate) fn assert_only_event_transfer( - ref spy: EventSpy, - contract: ContractAddress, - from: ContractAddress, - to: ContractAddress, - value: u256 -) { - assert_event_transfer(ref spy, contract, from, to, value); - assert(spy.events.len() == 0, 'Events remaining on queue'); + fn assert_only_event_transfer( + ref self: EventSpy, + contract: ContractAddress, + from: ContractAddress, + to: ContractAddress, + value: u256 + ) { + self.assert_event_transfer(contract, from, to, value); + self.assert_no_events_left_from(contract); + } } diff --git a/src/tests/token/erc20/test_erc20.cairo b/src/tests/token/erc20/test_erc20.cairo index c83e16576..c3315e8b7 100644 --- a/src/tests/token/erc20/test_erc20.cairo +++ b/src/tests/token/erc20/test_erc20.cairo @@ -7,10 +7,10 @@ use openzeppelin::tests::utils; use openzeppelin::token::erc20::ERC20Component::{ERC20CamelOnlyImpl, ERC20Impl}; use openzeppelin::token::erc20::ERC20Component::{ERC20MetadataImpl, InternalImpl}; use openzeppelin::token::erc20::ERC20Component; -use snforge_std::{test_address, start_cheat_caller_address}; +use snforge_std::{spy_events, test_address, start_cheat_caller_address}; use starknet::ContractAddress; -use super::common::{assert_event_approval, assert_only_event_approval, assert_only_event_transfer}; +use super::common::ERC20SpyHelpers; // // Setup @@ -94,12 +94,12 @@ fn test_allowance() { fn test_approve() { let mut state = setup(); let contract_address = test_address(); - let mut spy = utils::spy_on(contract_address); + let mut spy = spy_events(); start_cheat_caller_address(contract_address, OWNER()); assert!(state.approve(SPENDER(), VALUE)); - assert_only_event_approval(ref spy, contract_address, OWNER(), SPENDER(), VALUE); + spy.assert_only_event_approval(contract_address, OWNER(), SPENDER(), VALUE); let allowance = state.allowance(OWNER(), SPENDER()); assert_eq!(allowance, VALUE); @@ -124,12 +124,12 @@ fn test_approve_to_zero() { fn test__approve() { let mut state = setup(); let contract_address = test_address(); - let mut spy = utils::spy_on(contract_address); + let mut spy = spy_events(); start_cheat_caller_address(contract_address, OWNER()); state._approve(OWNER(), SPENDER(), VALUE); - assert_only_event_approval(ref spy, contract_address, OWNER(), SPENDER(), VALUE); + spy.assert_only_event_approval(contract_address, OWNER(), SPENDER(), VALUE); let allowance = state.allowance(OWNER(), SPENDER()); assert_eq!(allowance, VALUE); @@ -158,12 +158,12 @@ fn test__approve_to_zero() { fn test_transfer() { let mut state = setup(); let contract_address = test_address(); - let mut spy = utils::spy_on(contract_address); + let mut spy = spy_events(); start_cheat_caller_address(contract_address, OWNER()); assert!(state.transfer(RECIPIENT(), VALUE)); - assert_only_event_transfer(ref spy, contract_address, OWNER(), RECIPIENT(), VALUE); + spy.assert_only_event_transfer(contract_address, OWNER(), RECIPIENT(), VALUE); assert_eq!(state.balance_of(RECIPIENT()), VALUE); assert_eq!(state.balance_of(OWNER()), SUPPLY - VALUE); assert_eq!(state.total_supply(), SUPPLY); @@ -198,11 +198,11 @@ fn test_transfer_to_zero() { fn test__transfer() { let mut state = setup(); let contract_address = test_address(); - let mut spy = utils::spy_on(contract_address); + let mut spy = spy_events(); state._transfer(OWNER(), RECIPIENT(), VALUE); - assert_only_event_transfer(ref spy, contract_address, OWNER(), RECIPIENT(), VALUE); + spy.assert_only_event_transfer(contract_address, OWNER(), RECIPIENT(), VALUE); assert_eq!(state.balance_of(RECIPIENT()), VALUE); assert_eq!(state.balance_of(OWNER()), SUPPLY - VALUE); assert_eq!(state.total_supply(), SUPPLY); @@ -244,12 +244,12 @@ fn test_transfer_from() { start_cheat_caller_address(contract_address, OWNER()); state.approve(SPENDER(), VALUE); - let mut spy = utils::spy_on(contract_address); + let mut spy = spy_events(); start_cheat_caller_address(contract_address, SPENDER()); assert!(state.transfer_from(OWNER(), RECIPIENT(), VALUE)); - assert_event_approval(ref spy, contract_address, OWNER(), SPENDER(), 0); - assert_only_event_transfer(ref spy, contract_address, OWNER(), RECIPIENT(), VALUE); + spy.assert_event_approval(contract_address, OWNER(), SPENDER(), 0); + spy.assert_only_event_transfer(contract_address, OWNER(), RECIPIENT(), VALUE); let allowance = state.allowance(OWNER(), SPENDER()); assert_eq!(allowance, 0); @@ -310,12 +310,12 @@ fn test_transferFrom() { start_cheat_caller_address(contract_address, OWNER()); state.approve(SPENDER(), VALUE); - let mut spy = utils::spy_on(contract_address); + let mut spy = spy_events(); start_cheat_caller_address(contract_address, SPENDER()); assert!(state.transferFrom(OWNER(), RECIPIENT(), VALUE)); - assert_event_approval(ref spy, contract_address, OWNER(), SPENDER(), 0); - assert_only_event_transfer(ref spy, contract_address, OWNER(), RECIPIENT(), VALUE); + spy.assert_event_approval(contract_address, OWNER(), SPENDER(), 0); + spy.assert_only_event_transfer(contract_address, OWNER(), RECIPIENT(), VALUE); let allowance = state.allowance(OWNER(), SPENDER()); assert_eq!(allowance, 0); @@ -380,10 +380,10 @@ fn test__spend_allowance_not_unlimited() { state._approve(OWNER(), SPENDER(), SUPPLY); - let mut spy = utils::spy_on(contract_address); + let mut spy = spy_events(); state._spend_allowance(OWNER(), SPENDER(), VALUE); - assert_only_event_approval(ref spy, contract_address, OWNER(), SPENDER(), SUPPLY - VALUE); + spy.assert_only_event_approval(contract_address, OWNER(), SPENDER(), SUPPLY - VALUE); let allowance = state.allowance(OWNER(), SPENDER()); assert_eq!(allowance, SUPPLY - VALUE); @@ -410,10 +410,10 @@ fn test_mint() { let mut state = COMPONENT_STATE(); let contract_address = test_address(); - let mut spy = utils::spy_on(contract_address); + let mut spy = spy_events(); state.mint(OWNER(), VALUE); - assert_only_event_transfer(ref spy, contract_address, ZERO(), OWNER(), VALUE); + spy.assert_only_event_transfer(contract_address, ZERO(), OWNER(), VALUE); assert_eq!(state.balance_of(OWNER()), VALUE); assert_eq!(state.total_supply(), VALUE); } @@ -434,10 +434,10 @@ fn test_burn() { let mut state = setup(); let contract_address = test_address(); - let mut spy = utils::spy_on(contract_address); + let mut spy = spy_events(); state.burn(OWNER(), VALUE); - assert_only_event_transfer(ref spy, contract_address, OWNER(), ZERO(), VALUE); + spy.assert_only_event_transfer(contract_address, OWNER(), ZERO(), VALUE); assert_eq!(state.total_supply(), SUPPLY - VALUE); assert_eq!(state.balance_of(OWNER()), SUPPLY - VALUE); } diff --git a/src/tests/token/erc20/test_erc20_votes.cairo b/src/tests/token/erc20/test_erc20_votes.cairo index f8453ebd6..269ec1d44 100644 --- a/src/tests/token/erc20/test_erc20_votes.cairo +++ b/src/tests/token/erc20/test_erc20_votes.cairo @@ -3,6 +3,7 @@ use core::num::traits::Zero; use openzeppelin::tests::mocks::erc20_votes_mocks::DualCaseERC20VotesMock::SNIP12MetadataImpl; use openzeppelin::tests::mocks::erc20_votes_mocks::DualCaseERC20VotesMock; use openzeppelin::tests::utils::constants::{SUPPLY, ZERO, OWNER, RECIPIENT}; +use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::tests::utils; use openzeppelin::token::erc20::ERC20Component::InternalImpl as ERC20Impl; use openzeppelin::token::erc20::extensions::ERC20VotesComponent::{ @@ -17,9 +18,10 @@ use openzeppelin::utils::structs::checkpoint::{Checkpoint, TraceTrait}; use snforge_std::signature::KeyPairTrait; use snforge_std::signature::stark_curve::{StarkCurveKeyPairImpl, StarkCurveSignerImpl}; use snforge_std::{ - cheat_block_timestamp_global, start_cheat_caller_address, cheat_chain_id_global, test_address + cheat_block_timestamp_global, start_cheat_caller_address, spy_events, cheat_chain_id_global, + test_address }; -use snforge_std::{EventSpy, EventAssertions}; +use snforge_std::{EventSpy, EventSpyAssertionsTrait}; use starknet::ContractAddress; use starknet::contract_address_const; use starknet::storage::{StorageMapMemberAccessTrait, StorageMemberAccessTrait}; @@ -164,37 +166,37 @@ fn test_get_past_total_supply_future_lookup() { fn test_delegate() { let mut state = setup(); let contract_address = test_address(); - let mut spy = utils::spy_on(contract_address); + let mut spy = spy_events(); start_cheat_caller_address(contract_address, OWNER()); // Delegate from zero state.delegate(OWNER()); - assert_event_delegate_changed(ref spy, contract_address, OWNER(), ZERO(), OWNER()); - assert_only_event_delegate_votes_changed(ref spy, contract_address, OWNER(), 0, SUPPLY); + spy.assert_event_delegate_changed(contract_address, OWNER(), ZERO(), OWNER()); + spy.assert_only_event_delegate_votes_changed(contract_address, OWNER(), 0, SUPPLY); assert_eq!(state.get_votes(OWNER()), SUPPLY); // Delegate from non-zero to non-zero state.delegate(RECIPIENT()); - assert_event_delegate_changed(ref spy, contract_address, OWNER(), OWNER(), RECIPIENT()); - assert_event_delegate_votes_changed(ref spy, contract_address, OWNER(), SUPPLY, 0); - assert_only_event_delegate_votes_changed(ref spy, contract_address, RECIPIENT(), 0, SUPPLY); + spy.assert_event_delegate_changed(contract_address, OWNER(), OWNER(), RECIPIENT()); + spy.assert_event_delegate_votes_changed(contract_address, OWNER(), SUPPLY, 0); + spy.assert_only_event_delegate_votes_changed(contract_address, RECIPIENT(), 0, SUPPLY); assert!(state.get_votes(OWNER()).is_zero()); assert_eq!(state.get_votes(RECIPIENT()), SUPPLY); // Delegate to zero state.delegate(ZERO()); - assert_event_delegate_changed(ref spy, contract_address, OWNER(), RECIPIENT(), ZERO()); - assert_event_delegate_votes_changed(ref spy, contract_address, RECIPIENT(), SUPPLY, 0); + spy.assert_event_delegate_changed(contract_address, OWNER(), RECIPIENT(), ZERO()); + spy.assert_event_delegate_votes_changed(contract_address, RECIPIENT(), SUPPLY, 0); assert!(state.get_votes(RECIPIENT()).is_zero()); // Delegate from zero to zero state.delegate(ZERO()); - assert_only_event_delegate_changed(ref spy, contract_address, OWNER(), ZERO(), ZERO()); + spy.assert_only_event_delegate_changed(contract_address, OWNER(), ZERO(), ZERO()); } #[test] @@ -257,11 +259,11 @@ fn test_delegate_by_sig() { let msg_hash = delegation.get_message_hash(delegator); let (r, s) = key_pair.sign(msg_hash).unwrap(); - let mut spy = utils::spy_on(contract_address); + let mut spy = spy_events(); state.delegate_by_sig(delegator, delegatee, nonce, expiry, array![r, s]); - assert_only_event_delegate_changed(ref spy, contract_address, delegator, ZERO(), delegatee); + spy.assert_only_event_delegate_changed(contract_address, delegator, ZERO(), delegatee); assert_eq!(state.delegates(account), delegatee); } @@ -354,50 +356,53 @@ fn test_get_voting_units() { // Helpers // -fn assert_event_delegate_changed( - ref spy: EventSpy, - contract: ContractAddress, - delegator: ContractAddress, - from_delegate: ContractAddress, - to_delegate: ContractAddress -) { - let expected = ERC20VotesComponent::Event::DelegateChanged( - DelegateChanged { delegator, from_delegate, to_delegate } - ); - spy.assert_emitted(@array![(contract, expected)]); -} - -fn assert_only_event_delegate_changed( - ref spy: EventSpy, - contract: ContractAddress, - delegator: ContractAddress, - from_delegate: ContractAddress, - to_delegate: ContractAddress -) { - assert_event_delegate_changed(ref spy, contract, delegator, from_delegate, to_delegate); - assert(spy.events.len() == 0, 'Events remaining on queue'); -} - -fn assert_event_delegate_votes_changed( - ref spy: EventSpy, - contract: ContractAddress, - delegate: ContractAddress, - previous_votes: u256, - new_votes: u256 -) { - let expected = ERC20VotesComponent::Event::DelegateVotesChanged( - DelegateVotesChanged { delegate, previous_votes, new_votes } - ); - spy.assert_emitted(@array![(contract, expected)]); -} - -fn assert_only_event_delegate_votes_changed( - ref spy: EventSpy, - contract: ContractAddress, - delegate: ContractAddress, - previous_votes: u256, - new_votes: u256 -) { - assert_event_delegate_votes_changed(ref spy, contract, delegate, previous_votes, new_votes); - assert(spy.events.len() == 0, 'Events remaining on queue'); +#[generate_trait] +impl VotesSpyHelpersImpl of VotesSpyHelpers { + fn assert_event_delegate_changed( + ref self: EventSpy, + contract: ContractAddress, + delegator: ContractAddress, + from_delegate: ContractAddress, + to_delegate: ContractAddress + ) { + let expected = ERC20VotesComponent::Event::DelegateChanged( + DelegateChanged { delegator, from_delegate, to_delegate } + ); + self.assert_emitted_single(contract, expected); + } + + fn assert_only_event_delegate_changed( + ref self: EventSpy, + contract: ContractAddress, + delegator: ContractAddress, + from_delegate: ContractAddress, + to_delegate: ContractAddress + ) { + self.assert_event_delegate_changed(contract, delegator, from_delegate, to_delegate); + self.assert_no_events_left_from(contract); + } + + fn assert_event_delegate_votes_changed( + ref self: EventSpy, + contract: ContractAddress, + delegate: ContractAddress, + previous_votes: u256, + new_votes: u256 + ) { + let expected = ERC20VotesComponent::Event::DelegateVotesChanged( + DelegateVotesChanged { delegate, previous_votes, new_votes } + ); + self.assert_emitted_single(contract, expected); + } + + fn assert_only_event_delegate_votes_changed( + ref self: EventSpy, + contract: ContractAddress, + delegate: ContractAddress, + previous_votes: u256, + new_votes: u256 + ) { + self.assert_event_delegate_votes_changed(contract, delegate, previous_votes, new_votes); + self.assert_no_events_left_from(contract); + } } diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index 6edad0000..34b29154d 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -1,73 +1,7 @@ +pub(crate) mod common; pub(crate) mod constants; -pub mod foundry; -pub use foundry::declare_and_deploy; +pub(crate) mod events; +pub(crate) mod signing; +pub use common::{declare_class, declare_and_deploy, declare_and_deploy_at, deploy, deploy_at}; -pub use foundry::spy_on; - -use starknet::ContractAddress; -use starknet::SyscallResultTrait; -use starknet::testing; - -pub fn deploy(contract_class_hash: felt252, calldata: Array) -> ContractAddress { - deploy_with_salt(contract_class_hash, calldata, 0) -} - -pub fn deploy_with_salt( - contract_class_hash: felt252, calldata: Array, salt: felt252 -) -> ContractAddress { - let (address, _) = starknet::syscalls::deploy_syscall( - contract_class_hash.try_into().unwrap(), salt, calldata.span(), false - ) - .unwrap_syscall(); - address -} - -/// Pop the earliest unpopped logged event enum for the contract and checks -/// there's no more keys or data left on the event, preventing unaccounted params. -/// -/// CAUTION: If the event enum contains two `flat` events with the same structure (member types), -/// this function will always match the first event, even when the second one is emitted. -pub fn pop_log, +starknet::Event>(address: ContractAddress) -> Option { - let (mut keys, mut data) = testing::pop_log_raw(address)?; - - let ret = starknet::Event::deserialize(ref keys, ref data); - assert!(data.is_empty(), "Event has extra data"); - assert!(keys.is_empty(), "Event has extra keys"); - ret -} - -/// Asserts that `expected_keys` exactly matches the indexed keys from `event`. -/// -/// `expected_keys` must include all indexed event keys for `event` in the order -/// that they're defined. -/// -/// If the event is not flattened, the first key will be the event member name -/// e.g. selector!("EnumMemberName"). -pub fn assert_indexed_keys, +starknet::Event>( - event: T, expected_keys: Span -) { - let mut keys = array![]; - let mut data = array![]; - - event.append_keys_and_data(ref keys, ref data); - assert!(expected_keys == keys.span()); -} - -pub fn assert_no_events_left(address: ContractAddress) { - assert!(testing::pop_log_raw(address).is_none(), "Events remaining on queue"); -} - -pub fn drop_event(address: ContractAddress) { - let _ = testing::pop_log_raw(address); -} - -pub fn drop_events(address: ContractAddress, n_events: felt252) { - let mut count = n_events; - loop { - if count == 0 { - break; - } - drop_event(address); - count -= 1; - } -} +pub use events::EventSpyExt; diff --git a/src/tests/utils/common.cairo b/src/tests/utils/common.cairo new file mode 100644 index 000000000..27a7698db --- /dev/null +++ b/src/tests/utils/common.cairo @@ -0,0 +1,34 @@ +use snforge_std::{declare, ContractClass, ContractClassTrait}; +use starknet::ContractAddress; + +pub fn deploy(contract_class: ContractClass, calldata: Array) -> ContractAddress { + match contract_class.deploy(@calldata) { + Result::Ok((contract_address, _)) => contract_address, + Result::Err(panic_data) => panic!("Failed to deploy, error: ${:?}", panic_data) + } +} + +pub fn deploy_at( + contract_class: ContractClass, contract_address: ContractAddress, calldata: Array +) { + match contract_class.deploy_at(@calldata, contract_address) { + Result::Ok(_) => (), + Result::Err(panic_data) => panic!("Failed to deploy, error: ${:?}", panic_data) + }; +} + +pub fn declare_class(contract_name: ByteArray) -> ContractClass { + declare(contract_name).unwrap() +} + +pub fn declare_and_deploy(contract_name: ByteArray, calldata: Array) -> ContractAddress { + let contract_class = declare(contract_name).unwrap(); + deploy(contract_class, calldata) +} + +pub fn declare_and_deploy_at( + contract_name: ByteArray, target_address: ContractAddress, calldata: Array +) { + let contract_class = declare(contract_name).expect('Failed to declare contract'); + deploy_at(contract_class, target_address, calldata) +} diff --git a/src/tests/utils/events.cairo b/src/tests/utils/events.cairo new file mode 100644 index 000000000..302d1a917 --- /dev/null +++ b/src/tests/utils/events.cairo @@ -0,0 +1,62 @@ +use snforge_std::{EventSpyTrait, EventSpy, EventSpyAssertionsTrait}; +use starknet::ContractAddress; + +#[generate_trait] +pub impl EventSpyExtImpl of EventSpyExt { + fn assert_only_event, +Drop>( + ref self: EventSpy, from_address: ContractAddress, event: T + ) { + self.assert_emitted_single(from_address, event); + self.assert_no_events_left_from(from_address); + } + + fn assert_emitted_single, +Drop>( + ref self: EventSpy, from_address: ContractAddress, expected_event: T + ) { + self.assert_emitted(@array![(from_address, expected_event)]); + self._event_offset += 1; + } + + fn drop_event(ref self: EventSpy) { + self.drop_n_events(1); + } + + fn drop_n_events(ref self: EventSpy, number_to_drop: u32) { + let events = self.get_events().events; + let len = events.len(); + assert!( + len >= number_to_drop, + "Not enough events to drop. ${len} events, ${number_to_drop} to drop" + ); + self._event_offset += number_to_drop; + } + + fn drop_all_events(ref self: EventSpy) { + let events = self.get_events().events; + self._event_offset += events.len(); + } + + fn assert_no_events_left(ref self: EventSpy) { + let events = self.get_events().events; + assert!(events.len() == 0, "Events remaining on queue"); + } + + fn assert_no_events_left_from(ref self: EventSpy, from_address: ContractAddress) { + assert!(self.count_events_from(from_address) == 0, "Events remaining on queue"); + } + + fn count_events_from(ref self: EventSpy, from_address: ContractAddress) -> u32 { + let mut result = 0; + let mut events = self.get_events().events; + let mut index = 0; + let length = events.len(); + while index < length { + let (from, _) = events.at(index); + if from_address == *from { + result += 1; + } + index += 1; + }; + result + } +} diff --git a/src/tests/utils/foundry.cairo b/src/tests/utils/foundry.cairo deleted file mode 100644 index d624d9a79..000000000 --- a/src/tests/utils/foundry.cairo +++ /dev/null @@ -1,24 +0,0 @@ -use snforge_std::{declare, ContractClassTrait, spy_events, EventSpy, SpyOn}; -use starknet::ContractAddress; - -pub fn declare_and_deploy(contract_name: ByteArray, calldata: Array) -> ContractAddress { - let contract_class = declare(contract_name).unwrap(); - match contract_class.deploy(@calldata) { - Result::Ok((contract_address, _)) => contract_address, - Result::Err(panic_data) => panic!("Failed to deploy, error: ${:?}", panic_data) - } -} - -pub fn declare_and_deploy_at( - contract_name: ByteArray, target_address: ContractAddress, calldata: Array -) { - let contract_class = declare(contract_name).unwrap(); - if let Result::Err(panic_data) = contract_class.deploy_at(@calldata, target_address) { - panic!("Failed to deploy, error: ${:?}", panic_data) - } -} - -pub fn spy_on(contract_address: ContractAddress) -> EventSpy { - spy_events(SpyOn::One(contract_address)) -} - diff --git a/src/tests/utils/signing.cairo b/src/tests/utils/signing.cairo new file mode 100644 index 000000000..90be99ed5 --- /dev/null +++ b/src/tests/utils/signing.cairo @@ -0,0 +1,11 @@ +use snforge_std::signature::stark_curve::{StarkCurveSignerImpl, StarkCurveKeyPairImpl}; +use snforge_std::signature::{KeyPair, KeyPairTrait}; + +pub type StarkKeyPair = KeyPair; + +pub fn KEY_PAIR() -> StarkKeyPair { + KeyPairTrait::from_secret_key('SECRET_KEY') +} +pub fn KEY_PAIR_2() -> StarkKeyPair { + KeyPairTrait::from_secret_key('SECRET_KEY_2') +} From 9b333cc2c56322a4941a8466c3a2a0cd3f5ddbdb Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 4 Jul 2024 12:33:05 +0200 Subject: [PATCH 03/20] Re-add dual20 tests (#1028) * feat: migrate event tests * feat: update workflow * feat: add foundry utils * feat: finish erc20 and erc20votes migration * feat: remove dual case tests * refactor: format files * refactor: variable name * Update src/tests/token.cairo Co-authored-by: Andrew Fleming * Update src/tests/token/erc20.cairo Co-authored-by: Andrew Fleming * Update src/tests/utils/foundry.cairo Co-authored-by: Andrew Fleming * Update src/tests/token/erc20/test_erc20_votes.cairo Co-authored-by: Andrew Fleming * feat: apply review updates * feat: readd dual20 tests * refactor: remove unnecessary import * feat: add dual20 import --------- Co-authored-by: Andrew Fleming --- src/tests/token/erc20.cairo | 1 + src/tests/token/erc20/test_dual20.cairo | 325 ++++++++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 src/tests/token/erc20/test_dual20.cairo diff --git a/src/tests/token/erc20.cairo b/src/tests/token/erc20.cairo index 6b10a866e..8d094274f 100644 --- a/src/tests/token/erc20.cairo +++ b/src/tests/token/erc20.cairo @@ -1,4 +1,5 @@ pub(crate) mod common; +mod test_dual20; mod test_erc20; mod test_erc20_votes; diff --git a/src/tests/token/erc20/test_dual20.cairo b/src/tests/token/erc20/test_dual20.cairo new file mode 100644 index 000000000..dc37ce836 --- /dev/null +++ b/src/tests/token/erc20/test_dual20.cairo @@ -0,0 +1,325 @@ +use openzeppelin::tests::utils::constants::{ + OWNER, RECIPIENT, SPENDER, OPERATOR, NAME, SYMBOL, DECIMALS, SUPPLY, VALUE +}; +use openzeppelin::tests::utils; +use openzeppelin::token::erc20::dual20::{DualCaseERC20, DualCaseERC20Trait}; +use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; +use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; +use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::{test_address, start_cheat_caller_address}; + +// +// Setup +// + +fn setup_snake() -> (DualCaseERC20, IERC20Dispatcher) { + let mut calldata = array![]; + calldata.append_serde(NAME()); + calldata.append_serde(SYMBOL()); + calldata.append_serde(SUPPLY); + calldata.append_serde(OWNER()); + let target = utils::declare_and_deploy("SnakeERC20Mock", calldata); + (DualCaseERC20 { contract_address: target }, IERC20Dispatcher { contract_address: target }) +} + +fn setup_camel() -> (DualCaseERC20, IERC20CamelDispatcher) { + let mut calldata = array![]; + calldata.append_serde(NAME()); + calldata.append_serde(SYMBOL()); + calldata.append_serde(SUPPLY); + calldata.append_serde(OWNER()); + let target = utils::declare_and_deploy("CamelERC20Mock", calldata); + (DualCaseERC20 { contract_address: target }, IERC20CamelDispatcher { contract_address: target }) +} + +fn setup_non_erc20() -> DualCaseERC20 { + let calldata = array![]; + let target = utils::declare_and_deploy("NonImplementingMock", calldata); + DualCaseERC20 { contract_address: target } +} + +fn setup_erc20_panic() -> (DualCaseERC20, DualCaseERC20) { + let snake_target = utils::declare_and_deploy("SnakeERC20Panic", array![]); + let camel_target = utils::declare_and_deploy("CamelERC20Panic", array![]); + ( + DualCaseERC20 { contract_address: snake_target }, + DualCaseERC20 { contract_address: camel_target } + ) +} + +// +// Case agnostic methods +// + +#[test] +#[ignore] +fn test_dual_name() { + let (snake_dispatcher, _) = setup_snake(); + assert_eq!(snake_dispatcher.name(), NAME()); + + let (camel_dispatcher, _) = setup_camel(); + assert_eq!(camel_dispatcher.name(), NAME()); +} + +#[test] +#[ignore] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +fn test_dual_no_name() { + let dispatcher = setup_non_erc20(); + dispatcher.name(); +} + +#[test] +#[ignore] +#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +fn test_dual_name_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.name(); +} + +#[test] +#[ignore] +fn test_dual_symbol() { + let (snake_dispatcher, _) = setup_snake(); + let (camel_dispatcher, _) = setup_camel(); + assert_eq!(snake_dispatcher.symbol(), SYMBOL()); + assert_eq!(camel_dispatcher.symbol(), SYMBOL()); +} + +#[test] +#[ignore] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +fn test_dual_no_symbol() { + let dispatcher = setup_non_erc20(); + dispatcher.symbol(); +} + +#[test] +#[ignore] +#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +fn test_dual_symbol_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.symbol(); +} + +#[test] +#[ignore] +fn test_dual_decimals() { + let (snake_dispatcher, _) = setup_snake(); + let (camel_dispatcher, _) = setup_camel(); + assert_eq!(snake_dispatcher.decimals(), DECIMALS); + assert_eq!(camel_dispatcher.decimals(), DECIMALS); +} + +#[test] +#[ignore] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +fn test_dual_no_decimals() { + let dispatcher = setup_non_erc20(); + dispatcher.decimals(); +} + +#[test] +#[ignore] +#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +fn test_dual_decimals_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.decimals(); +} + +#[test] +#[ignore] +fn test_dual_transfer() { + let (snake_dispatcher, snake_target) = setup_snake(); + + start_cheat_caller_address(test_address(), OWNER()); + assert!(snake_dispatcher.transfer(RECIPIENT(), VALUE)); + assert_eq!(snake_target.balance_of(RECIPIENT()), VALUE); + + let (camel_dispatcher, camel_target) = setup_camel(); + start_cheat_caller_address(test_address(), OWNER()); + assert!(camel_dispatcher.transfer(RECIPIENT(), VALUE)); + assert_eq!(camel_target.balanceOf(RECIPIENT()), VALUE); +} + +#[test] +#[ignore] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +fn test_dual_no_transfer() { + let dispatcher = setup_non_erc20(); + dispatcher.transfer(RECIPIENT(), VALUE); +} + +#[test] +#[ignore] +#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +fn test_dual_transfer_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.transfer(RECIPIENT(), VALUE); +} + +#[test] +#[ignore] +fn test_dual_approve() { + let (snake_dispatcher, snake_target) = setup_snake(); + start_cheat_caller_address(test_address(), OWNER()); + assert!(snake_dispatcher.approve(SPENDER(), VALUE)); + + let snake_allowance = snake_target.allowance(OWNER(), SPENDER()); + assert_eq!(snake_allowance, VALUE); + + let (camel_dispatcher, camel_target) = setup_camel(); + start_cheat_caller_address(test_address(), OWNER()); + assert!(camel_dispatcher.approve(SPENDER(), VALUE)); + + let camel_allowance = camel_target.allowance(OWNER(), SPENDER()); + assert_eq!(camel_allowance, VALUE); +} + +#[test] +#[ignore] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +fn test_dual_no_approve() { + let dispatcher = setup_non_erc20(); + dispatcher.approve(SPENDER(), VALUE); +} + +#[test] +#[ignore] +#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +fn test_dual_approve_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.approve(SPENDER(), VALUE); +} + +// +// snake_case target +// + +#[test] +#[ignore] +fn test_dual_total_supply() { + let (dispatcher, _) = setup_snake(); + assert_eq!(dispatcher.total_supply(), SUPPLY); +} + +#[test] +#[ignore] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +fn test_dual_no_total_supply() { + let dispatcher = setup_non_erc20(); + dispatcher.total_supply(); +} + +#[test] +#[ignore] +#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +fn test_dual_total_supply_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.total_supply(); +} + +#[test] +#[ignore] +fn test_dual_balance_of() { + let (dispatcher, _) = setup_snake(); + assert_eq!(dispatcher.balance_of(OWNER()), SUPPLY); +} + +#[test] +#[ignore] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +fn test_dual_no_balance_of() { + let dispatcher = setup_non_erc20(); + dispatcher.balance_of(OWNER()); +} + +#[test] +#[ignore] +#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +fn test_dual_balance_of_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.balance_of(OWNER()); +} + +#[test] +#[ignore] +fn test_dual_transfer_from() { + let (dispatcher, target) = setup_snake(); + start_cheat_caller_address(test_address(), OWNER()); + target.approve(OPERATOR(), VALUE); + + start_cheat_caller_address(test_address(), OPERATOR()); + dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); + assert_eq!(target.balance_of(RECIPIENT()), VALUE); +} + +#[test] +#[ignore] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +fn test_dual_no_transfer_from() { + let dispatcher = setup_non_erc20(); + dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); +} + +#[test] +#[ignore] +#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +fn test_dual_transfer_from_exists_and_panics() { + let (dispatcher, _) = setup_erc20_panic(); + dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); +} + +// +// camelCase target +// + +#[test] +#[ignore] +fn test_dual_totalSupply() { + let (dispatcher, _) = setup_camel(); + assert_eq!(dispatcher.total_supply(), SUPPLY); +} + +#[test] +#[ignore] +#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +fn test_dual_totalSupply_exists_and_panics() { + let (_, dispatcher) = setup_erc20_panic(); + dispatcher.total_supply(); +} + +#[test] +#[ignore] +fn test_dual_balanceOf() { + let (dispatcher, _) = setup_camel(); + assert_eq!(dispatcher.balance_of(OWNER()), SUPPLY); +} + +#[test] +#[ignore] +#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +fn test_dual_balanceOf_exists_and_panics() { + let (_, dispatcher) = setup_erc20_panic(); + dispatcher.balance_of(OWNER()); +} + +#[test] +#[ignore] +fn test_dual_transferFrom() { + let (dispatcher, target) = setup_camel(); + start_cheat_caller_address(test_address(), OWNER()); + target.approve(OPERATOR(), VALUE); + + start_cheat_caller_address(test_address(), OPERATOR()); + dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); + assert_eq!(target.balanceOf(RECIPIENT()), VALUE); +} + +#[test] +#[ignore] +#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +fn test_dual_transferFrom_exists_and_panics() { + let (_, dispatcher) = setup_erc20_panic(); + dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); +} From 0d149da76627cff3d132d61a71c156b0f941c92c Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Fri, 5 Jul 2024 11:32:21 -0400 Subject: [PATCH 04/20] Migrate security tests (#1034) * migrate pausable and initializable tests * migrate reentrancy guard tests * clean up tests * fix fmt * add event trait to pausable, fix tests * Apply suggestions from code review Co-authored-by: Eric Nordelo * fix fmt --------- Co-authored-by: Eric Nordelo --- src/tests.cairo | 4 +- src/tests/security/test_pausable.cairo | 64 ++++++++++++------- src/tests/security/test_reentrancyguard.cairo | 23 ++----- 3 files changed, 51 insertions(+), 40 deletions(-) diff --git a/src/tests.cairo b/src/tests.cairo index 6b45d6e2c..eceac55c6 100644 --- a/src/tests.cairo +++ b/src/tests.cairo @@ -9,8 +9,8 @@ mod mocks; // #[cfg(test)] // mod presets; -// #[cfg(test)] -// mod security; +#[cfg(test)] +mod security; #[cfg(test)] mod token; // #[cfg(test)] diff --git a/src/tests/security/test_pausable.cairo b/src/tests/security/test_pausable.cairo index 8fd170df0..ea4ff3878 100644 --- a/src/tests/security/test_pausable.cairo +++ b/src/tests/security/test_pausable.cairo @@ -2,11 +2,11 @@ use openzeppelin::security::PausableComponent::{InternalImpl, PausableImpl}; use openzeppelin::security::PausableComponent::{Paused, Unpaused}; use openzeppelin::security::PausableComponent; use openzeppelin::tests::mocks::pausable_mocks::PausableMock; -use openzeppelin::tests::utils::constants::{CALLER, ZERO}; -use openzeppelin::tests::utils; +use openzeppelin::tests::utils::constants::CALLER; +use openzeppelin::tests::utils::events::EventSpyExt; +use snforge_std::EventSpy; +use snforge_std::{spy_events, test_address, start_cheat_caller_address}; use starknet::ContractAddress; -use starknet::contract_address_const; -use starknet::testing; type ComponentState = PausableComponent::ComponentState; @@ -73,11 +73,13 @@ fn test_assert_not_paused_when_not_paused() { #[test] fn test_pause_when_unpaused() { let mut state = COMPONENT_STATE(); - testing::set_caller_address(CALLER()); + let contract_address = test_address(); + let mut spy = spy_events(); + start_cheat_caller_address(contract_address, CALLER()); state.pause(); - assert_event_paused(CALLER()); + spy.assert_only_event_paused(contract_address, CALLER()); assert!(state.is_paused()); } @@ -96,14 +98,15 @@ fn test_pause_when_paused() { #[test] fn test_unpause_when_paused() { let mut state = COMPONENT_STATE(); - testing::set_caller_address(CALLER()); + let contract_address = test_address(); + let mut spy = spy_events(); + start_cheat_caller_address(test_address(), CALLER()); state.pause(); - utils::drop_event(ZERO()); - state.unpause(); - assert_event_unpaused(CALLER()); + spy.assert_event_paused(contract_address, CALLER()); + spy.assert_only_event_unpaused(contract_address, CALLER()); assert!(!state.is_paused()); } @@ -119,16 +122,33 @@ fn test_unpause_when_unpaused() { // Helpers // -fn assert_event_paused(account: ContractAddress) { - let event = utils::pop_log::(ZERO()).unwrap(); - let expected = PausableComponent::Event::Paused(Paused { account }); - assert!(event == expected); - utils::assert_no_events_left(ZERO()); -} - -fn assert_event_unpaused(account: ContractAddress) { - let event = utils::pop_log::(ZERO()).unwrap(); - let expected = PausableComponent::Event::Unpaused(Unpaused { account }); - assert!(event == expected); - utils::assert_no_events_left(ZERO()); +#[generate_trait] +impl PausableSpyHelpersImpl of PausableSpyHelpers { + fn assert_event_paused( + ref self: EventSpy, contract: ContractAddress, account: ContractAddress + ) { + let expected = PausableComponent::Event::Paused(Paused { account }); + self.assert_emitted_single(contract, expected); + } + + fn assert_only_event_paused( + ref self: EventSpy, contract: ContractAddress, account: ContractAddress, + ) { + self.assert_event_paused(contract, account); + self.assert_no_events_left_from(contract); + } + + fn assert_event_unpaused( + ref self: EventSpy, contract: ContractAddress, account: ContractAddress + ) { + let expected = PausableComponent::Event::Unpaused(Unpaused { account }); + self.assert_emitted_single(contract, expected); + } + + fn assert_only_event_unpaused( + ref self: EventSpy, contract: ContractAddress, account: ContractAddress, + ) { + self.assert_event_unpaused(contract, account); + self.assert_no_events_left_from(contract); + } } diff --git a/src/tests/security/test_reentrancyguard.cairo b/src/tests/security/test_reentrancyguard.cairo index 327f72fa4..4a12bc918 100644 --- a/src/tests/security/test_reentrancyguard.cairo +++ b/src/tests/security/test_reentrancyguard.cairo @@ -1,7 +1,7 @@ use openzeppelin::security::ReentrancyGuardComponent::InternalImpl; use openzeppelin::security::ReentrancyGuardComponent; use openzeppelin::tests::mocks::reentrancy_mocks::{ - Attacker, ReentrancyMock, IReentrancyMockDispatcher, IReentrancyMockDispatcherTrait + ReentrancyMock, IReentrancyMockDispatcher, IReentrancyMockDispatcherTrait }; use openzeppelin::tests::utils; use starknet::storage::StorageMemberAccessTrait; @@ -14,7 +14,7 @@ fn COMPONENT_STATE() -> ComponentState { fn deploy_mock() -> IReentrancyMockDispatcher { let calldata = array![]; - let address = utils::deploy(ReentrancyMock::TEST_CLASS_HASH, calldata); + let address = utils::declare_and_deploy("ReentrancyMock", calldata); IReentrancyMockDispatcher { contract_address: address } } @@ -64,35 +64,26 @@ fn test_reentrancy_guard_end() { // #[test] -#[should_panic( - expected: ( - 'ReentrancyGuard: reentrant call', - 'ENTRYPOINT_FAILED', - 'ENTRYPOINT_FAILED', - 'ENTRYPOINT_FAILED' - ), -)] +#[should_panic(expected: ('ReentrancyGuard: reentrant call',))] fn test_remote_callback() { let contract = deploy_mock(); // Deploy attacker - let calldata = ArrayTrait::new(); - let attacker_addr = utils::deploy(Attacker::TEST_CLASS_HASH, calldata); + let calldata = array![]; + let attacker_addr = utils::declare_and_deploy("Attacker", calldata); contract.count_and_call(attacker_addr); } #[test] -#[should_panic(expected: ('ReentrancyGuard: reentrant call', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ReentrancyGuard: reentrant call',))] fn test_local_recursion() { let contract = deploy_mock(); contract.count_local_recursive(10); } #[test] -#[should_panic( - expected: ('ReentrancyGuard: reentrant call', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED') -)] +#[should_panic(expected: ('ReentrancyGuard: reentrant call',))] fn test_external_recursion() { let contract = deploy_mock(); contract.count_external_recursive(10); From c3df2885516b59b4dc3faa11885be34d7334845f Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 10 Jul 2024 13:52:23 +0200 Subject: [PATCH 05/20] Migrate erc1155 tests (#1037) * feat: update common module * feat: migrate dual modules * feat: finish component test migration * refactor: remove unnecessary imports * feat: apply review updates --- src/tests/token.cairo | 2 +- src/tests/token/erc1155/common.cairo | 205 ++++--- src/tests/token/erc1155/test_dual1155.cairo | 101 ++-- .../erc1155/test_dual1155_receiver.cairo | 43 +- src/tests/token/erc1155/test_erc1155.cairo | 524 ++++++++++++------ src/tests/utils.cairo | 4 +- src/tests/utils/common.cairo | 18 +- 7 files changed, 566 insertions(+), 331 deletions(-) diff --git a/src/tests/token.cairo b/src/tests/token.cairo index 25979f784..2ecd8ae11 100644 --- a/src/tests/token.cairo +++ b/src/tests/token.cairo @@ -1,3 +1,3 @@ -// pub(crate) mod erc1155; +pub(crate) mod erc1155; // pub(crate) mod erc721; pub(crate) mod erc20; diff --git a/src/tests/token/erc1155/common.cairo b/src/tests/token/erc1155/common.cairo index 3230c45b0..579b5931b 100644 --- a/src/tests/token/erc1155/common.cairo +++ b/src/tests/token/erc1155/common.cairo @@ -1,134 +1,36 @@ -use openzeppelin::tests::mocks::account_mocks::SnakeAccountMock; -use openzeppelin::tests::mocks::erc1155_receiver_mocks::{ - CamelERC1155ReceiverMock, SnakeERC1155ReceiverMock -}; -use openzeppelin::tests::mocks::src5_mocks::SRC5Mock; use openzeppelin::tests::utils::constants::{ PUBKEY, TOKEN_ID, TOKEN_ID_2, TOKEN_VALUE, TOKEN_VALUE_2 }; +use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::tests::utils; use openzeppelin::token::erc1155::ERC1155Component::{TransferBatch, ApprovalForAll, TransferSingle}; use openzeppelin::token::erc1155::ERC1155Component; -use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::EventSpy; use starknet::ContractAddress; pub(crate) fn setup_receiver() -> ContractAddress { - utils::deploy(SnakeERC1155ReceiverMock::TEST_CLASS_HASH, array![]) + utils::declare_and_deploy("SnakeERC1155ReceiverMock", array![]) } pub(crate) fn setup_camel_receiver() -> ContractAddress { - utils::deploy(CamelERC1155ReceiverMock::TEST_CLASS_HASH, array![]) + utils::declare_and_deploy("CamelERC1155ReceiverMock", array![]) } pub(crate) fn setup_account() -> ContractAddress { - let mut calldata = array![PUBKEY]; - utils::deploy(SnakeAccountMock::TEST_CLASS_HASH, calldata) -} - -pub(crate) fn setup_account_with_salt(salt: felt252) -> ContractAddress { - let mut calldata = array![PUBKEY]; - utils::deploy_with_salt(SnakeAccountMock::TEST_CLASS_HASH, calldata, salt) + let calldata = array![PUBKEY]; + utils::declare_and_deploy("SnakeAccountMock", calldata) } -pub(crate) fn setup_src5() -> ContractAddress { - utils::deploy(SRC5Mock::TEST_CLASS_HASH, array![]) -} - -pub(crate) fn assert_event_approval_for_all( - contract: ContractAddress, owner: ContractAddress, operator: ContractAddress, approved: bool +pub(crate) fn deploy_another_account_at( + existing: ContractAddress, target_address: ContractAddress ) { - let event = utils::pop_log::(contract).unwrap(); - let expected = ERC1155Component::Event::ApprovalForAll( - ApprovalForAll { owner, operator, approved } - ); - assert!(event == expected); - - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("ApprovalForAll")); - indexed_keys.append_serde(owner); - indexed_keys.append_serde(operator); - utils::assert_indexed_keys(event, indexed_keys.span()); + let calldata = array![PUBKEY]; + utils::deploy_another_at(existing, target_address, calldata); } -pub(crate) fn assert_event_transfer_single( - contract: ContractAddress, - operator: ContractAddress, - from: ContractAddress, - to: ContractAddress, - token_id: u256, - value: u256 -) { - let event = utils::pop_log::(contract).unwrap(); - let id = token_id; - let expected = ERC1155Component::Event::TransferSingle( - TransferSingle { operator, from, to, id, value } - ); - assert!(event == expected); - - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("TransferSingle")); - indexed_keys.append_serde(operator); - indexed_keys.append_serde(from); - indexed_keys.append_serde(to); - utils::assert_indexed_keys(event, indexed_keys.span()); -} - -pub(crate) fn assert_event_transfer_batch( - contract: ContractAddress, - operator: ContractAddress, - from: ContractAddress, - to: ContractAddress, - token_ids: Span, - values: Span -) { - let event = utils::pop_log::(contract).unwrap(); - let ids = token_ids; - let expected = ERC1155Component::Event::TransferBatch( - TransferBatch { operator, from, to, ids, values } - ); - assert!(event == expected); - - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("TransferBatch")); - indexed_keys.append_serde(operator); - indexed_keys.append_serde(from); - indexed_keys.append_serde(to); - utils::assert_indexed_keys(event, indexed_keys.span()); -} - -pub(crate) fn assert_only_event_transfer_single( - contract: ContractAddress, - operator: ContractAddress, - from: ContractAddress, - to: ContractAddress, - token_id: u256, - value: u256 -) { - assert_event_transfer_single(contract, operator, from, to, token_id, value); - utils::assert_no_events_left(contract); -} - -pub(crate) fn assert_only_event_transfer_batch( - contract: ContractAddress, - operator: ContractAddress, - from: ContractAddress, - to: ContractAddress, - token_ids: Span, - values: Span -) { - assert_event_transfer_batch(contract, operator, from, to, token_ids, values); - utils::assert_no_events_left(contract); -} - -pub(crate) fn assert_only_event_approval_for_all( - contract: ContractAddress, owner: ContractAddress, operator: ContractAddress, approved: bool -) { - assert_event_approval_for_all(contract, owner, operator, approved); - utils::assert_no_events_left(contract); +pub(crate) fn setup_src5() -> ContractAddress { + utils::declare_and_deploy("SRC5Mock", array![]) } pub(crate) fn get_ids_and_values() -> (Span, Span) { @@ -142,3 +44,86 @@ pub(crate) fn get_ids_and_split_values(split: u256) -> (Span, Span) let values = array![TOKEN_VALUE - split, split].span(); (ids, values) } + +#[generate_trait] +pub(crate) impl ERC1155SpyHelpersImpl of ERC1155SpyHelpers { + fn assert_event_approval_for_all( + ref self: EventSpy, + contract: ContractAddress, + owner: ContractAddress, + operator: ContractAddress, + approved: bool + ) { + let expected = ERC1155Component::Event::ApprovalForAll( + ApprovalForAll { owner, operator, approved } + ); + self.assert_emitted_single(contract, expected); + } + + fn assert_only_event_approval_for_all( + ref self: EventSpy, + contract: ContractAddress, + owner: ContractAddress, + operator: ContractAddress, + approved: bool + ) { + self.assert_event_approval_for_all(contract, owner, operator, approved); + self.assert_no_events_left_from(contract); + } + + fn assert_event_transfer_single( + ref self: EventSpy, + contract: ContractAddress, + operator: ContractAddress, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + value: u256 + ) { + let expected = ERC1155Component::Event::TransferSingle( + TransferSingle { operator, from, to, id: token_id, value } + ); + self.assert_emitted_single(contract, expected); + } + + fn assert_only_event_transfer_single( + ref self: EventSpy, + contract: ContractAddress, + operator: ContractAddress, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + value: u256 + ) { + self.assert_event_transfer_single(contract, operator, from, to, token_id, value); + self.assert_no_events_left_from(contract); + } + + fn assert_event_transfer_batch( + ref self: EventSpy, + contract: ContractAddress, + operator: ContractAddress, + from: ContractAddress, + to: ContractAddress, + token_ids: Span, + values: Span + ) { + let expected = ERC1155Component::Event::TransferBatch( + TransferBatch { operator, from, to, ids: token_ids, values } + ); + self.assert_emitted_single(contract, expected); + } + + fn assert_only_event_transfer_batch( + ref self: EventSpy, + contract: ContractAddress, + operator: ContractAddress, + from: ContractAddress, + to: ContractAddress, + token_ids: Span, + values: Span + ) { + self.assert_event_transfer_batch(contract, operator, from, to, token_ids, values); + self.assert_no_events_left_from(contract); + } +} diff --git a/src/tests/token/erc1155/test_dual1155.cairo b/src/tests/token/erc1155/test_dual1155.cairo index 16bfe4bfa..4589cd2c1 100644 --- a/src/tests/token/erc1155/test_dual1155.cairo +++ b/src/tests/token/erc1155/test_dual1155.cairo @@ -1,7 +1,4 @@ use core::num::traits::Zero; -use openzeppelin::tests::mocks::erc1155_mocks::{CamelERC1155Mock, SnakeERC1155Mock}; -use openzeppelin::tests::mocks::erc1155_mocks::{CamelERC1155PanicMock, SnakeERC1155PanicMock}; -use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils::constants::{ EMPTY_DATA, OWNER, RECIPIENT, OPERATOR, TOKEN_ID, TOKEN_ID_2, TOKEN_VALUE }; @@ -13,8 +10,8 @@ use openzeppelin::token::erc1155::interface::{ }; use openzeppelin::token::erc1155::interface::{IERC1155Dispatcher, IERC1155DispatcherTrait}; use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::start_cheat_caller_address; use starknet::ContractAddress; -use starknet::testing; use super::common::{setup_account, setup_receiver}; @@ -30,7 +27,7 @@ fn setup_snake() -> (DualCaseERC1155, IERC1155Dispatcher, ContractAddress) { calldata.append_serde(owner); calldata.append_serde(TOKEN_ID); calldata.append_serde(TOKEN_VALUE); - let target = utils::deploy(SnakeERC1155Mock::TEST_CLASS_HASH, calldata); + let target = utils::declare_and_deploy("SnakeERC1155Mock", calldata); ( DualCaseERC1155 { contract_address: target }, IERC1155Dispatcher { contract_address: target }, @@ -46,7 +43,7 @@ fn setup_camel() -> (DualCaseERC1155, IERC1155CamelDispatcher, ContractAddress) calldata.append_serde(owner); calldata.append_serde(TOKEN_ID); calldata.append_serde(TOKEN_VALUE); - let target = utils::deploy(CamelERC1155Mock::TEST_CLASS_HASH, calldata); + let target = utils::declare_and_deploy("CamelERC1155Mock", calldata); ( DualCaseERC1155 { contract_address: target }, IERC1155CamelDispatcher { contract_address: target }, @@ -56,13 +53,13 @@ fn setup_camel() -> (DualCaseERC1155, IERC1155CamelDispatcher, ContractAddress) fn setup_non_erc1155() -> DualCaseERC1155 { let calldata = array![]; - let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); + let target = utils::declare_and_deploy("NonImplementingMock", calldata); DualCaseERC1155 { contract_address: target } } fn setup_erc1155_panic() -> (DualCaseERC1155, DualCaseERC1155) { - let snake_target = utils::deploy(SnakeERC1155PanicMock::TEST_CLASS_HASH, array![]); - let camel_target = utils::deploy(CamelERC1155PanicMock::TEST_CLASS_HASH, array![]); + let snake_target = utils::declare_and_deploy("SnakeERC1155PanicMock", array![]); + let camel_target = utils::declare_and_deploy("CamelERC1155PanicMock", array![]); ( DualCaseERC1155 { contract_address: snake_target }, DualCaseERC1155 { contract_address: camel_target } @@ -74,6 +71,7 @@ fn setup_erc1155_panic() -> (DualCaseERC1155, DualCaseERC1155) { // #[test] +#[ignore] fn test_dual_uri() { let (snake_dispatcher, _, _) = setup_snake(); let (camel_dispatcher, _, _) = setup_camel(); @@ -82,6 +80,7 @@ fn test_dual_uri() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_uri() { let dispatcher = setup_non_erc1155(); @@ -89,7 +88,8 @@ fn test_dual_no_uri() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_uri_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); dispatcher.uri(TOKEN_ID); @@ -100,12 +100,14 @@ fn test_dual_uri_exists_and_panics() { // #[test] +#[ignore] fn test_dual_balance_of() { let (dispatcher, _, owner) = setup_snake(); assert_eq!(dispatcher.balance_of(owner, TOKEN_ID), TOKEN_VALUE); } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_balance_of() { let dispatcher = setup_non_erc1155(); @@ -113,13 +115,15 @@ fn test_dual_no_balance_of() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_balance_of_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); dispatcher.balance_of(OWNER(), TOKEN_ID); } #[test] +#[ignore] fn test_dual_balance_of_batch() { let (dispatcher, _, owner) = setup_snake(); let accounts = array![owner, RECIPIENT()].span(); @@ -131,6 +135,7 @@ fn test_dual_balance_of_batch() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_balance_of_batch() { let dispatcher = setup_non_erc1155(); @@ -139,7 +144,8 @@ fn test_dual_no_balance_of_batch() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_balance_of_batch_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); let (accounts, token_ids) = get_accounts_and_ids(); @@ -147,15 +153,18 @@ fn test_dual_balance_of_batch_exists_and_panics() { } #[test] +#[ignore] fn test_dual_safe_transfer_from() { let (dispatcher, target, owner) = setup_snake(); let receiver = setup_receiver(); - testing::set_contract_address(owner); + + start_cheat_caller_address(dispatcher.contract_address, owner); dispatcher.safe_transfer_from(owner, receiver, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); assert_eq!(target.balance_of(receiver, TOKEN_ID), TOKEN_VALUE); } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_safe_transfer_from() { let dispatcher = setup_non_erc1155(); @@ -163,26 +172,29 @@ fn test_dual_no_safe_transfer_from() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_safe_transfer_from_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } #[test] +#[ignore] fn test_dual_safe_batch_transfer_from() { let (dispatcher, target, owner) = setup_snake(); let token_ids = array![TOKEN_ID, TOKEN_ID_2].span(); let values = array![TOKEN_VALUE, 0].span(); let receiver = setup_receiver(); - testing::set_contract_address(owner); + start_cheat_caller_address(dispatcher.contract_address, owner); dispatcher.safe_batch_transfer_from(owner, receiver, token_ids, values, EMPTY_DATA()); assert_eq!(target.balance_of(receiver, TOKEN_ID), TOKEN_VALUE); assert!(target.balance_of(receiver, TOKEN_ID_2).is_zero()); } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_safe_batch_transfer_from() { let dispatcher = setup_non_erc1155(); @@ -192,7 +204,8 @@ fn test_dual_no_safe_batch_transfer_from() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_safe_batch_transfer_from_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); let token_ids = array![TOKEN_ID, TOKEN_ID_2].span(); @@ -201,9 +214,11 @@ fn test_dual_safe_batch_transfer_from_exists_and_panics() { } #[test] +#[ignore] fn test_dual_is_approved_for_all() { let (dispatcher, target, _) = setup_snake(); - testing::set_contract_address(OWNER()); + + start_cheat_caller_address(dispatcher.contract_address, OWNER()); target.set_approval_for_all(OPERATOR(), true); let is_approved_for_all = dispatcher.is_approved_for_all(OWNER(), OPERATOR()); @@ -211,6 +226,7 @@ fn test_dual_is_approved_for_all() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_is_approved_for_all() { let dispatcher = setup_non_erc1155(); @@ -218,16 +234,19 @@ fn test_dual_no_is_approved_for_all() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_is_approved_for_all_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); dispatcher.is_approved_for_all(OWNER(), OPERATOR()); } #[test] +#[ignore] fn test_dual_set_approval_for_all() { let (dispatcher, target, _) = setup_snake(); - testing::set_contract_address(OWNER()); + + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.set_approval_for_all(OPERATOR(), true); let is_approved_for_all = target.is_approved_for_all(OWNER(), OPERATOR()); @@ -235,6 +254,7 @@ fn test_dual_set_approval_for_all() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_set_approval_for_all() { let dispatcher = setup_non_erc1155(); @@ -242,13 +262,15 @@ fn test_dual_no_set_approval_for_all() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_set_approval_for_all_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); dispatcher.set_approval_for_all(OPERATOR(), true); } #[test] +#[ignore] fn test_dual_supports_interface() { let (dispatcher, _, _) = setup_snake(); let supports_ierc1155 = dispatcher.supports_interface(IERC1155_ID); @@ -256,6 +278,7 @@ fn test_dual_supports_interface() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_supports_interface() { let dispatcher = setup_non_erc1155(); @@ -263,7 +286,8 @@ fn test_dual_no_supports_interface() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_supports_interface_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); dispatcher.supports_interface(IERC1155_ID); @@ -274,19 +298,22 @@ fn test_dual_supports_interface_exists_and_panics() { // #[test] +#[ignore] fn test_dual_balanceOf() { let (dispatcher, _, owner) = setup_camel(); assert_eq!(dispatcher.balance_of(owner, TOKEN_ID), TOKEN_VALUE); } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_balanceOf_exists_and_panics() { let (_, dispatcher) = setup_erc1155_panic(); dispatcher.balance_of(OWNER(), TOKEN_ID); } #[test] +#[ignore] fn test_dual_balanceOfBatch() { let (dispatcher, _, owner) = setup_camel(); let accounts = array![owner, RECIPIENT()].span(); @@ -298,7 +325,8 @@ fn test_dual_balanceOfBatch() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_balanceOfBatch_exists_and_panics() { let (_, dispatcher) = setup_erc1155_panic(); let (accounts, token_ids) = get_accounts_and_ids(); @@ -306,36 +334,41 @@ fn test_dual_balanceOfBatch_exists_and_panics() { } #[test] +#[ignore] fn test_dual_safeTransferFrom() { let (dispatcher, target, owner) = setup_camel(); let receiver = setup_receiver(); - testing::set_contract_address(owner); + + start_cheat_caller_address(dispatcher.contract_address, owner); dispatcher.safe_transfer_from(owner, receiver, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); assert_eq!(target.balanceOf(receiver, TOKEN_ID), TOKEN_VALUE); } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_safeTransferFrom_exists_and_panics() { let (_, dispatcher) = setup_erc1155_panic(); dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } #[test] +#[ignore] fn test_dual_safeBatchTransferFrom() { let (dispatcher, target, owner) = setup_camel(); let token_ids = array![TOKEN_ID, TOKEN_ID_2].span(); let values = array![TOKEN_VALUE, 0].span(); let receiver = setup_receiver(); - testing::set_contract_address(owner); + start_cheat_caller_address(dispatcher.contract_address, owner); dispatcher.safe_batch_transfer_from(owner, receiver, token_ids, values, EMPTY_DATA()); assert_eq!(target.balanceOf(receiver, TOKEN_ID), TOKEN_VALUE); assert!(target.balanceOf(receiver, TOKEN_ID_2).is_zero()); } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_safeBatchTransferFrom_exists_and_panics() { let (_, dispatcher) = setup_erc1155_panic(); let token_ids = array![TOKEN_ID, TOKEN_ID_2].span(); @@ -344,9 +377,11 @@ fn test_dual_safeBatchTransferFrom_exists_and_panics() { } #[test] +#[ignore] fn test_dual_isApprovedForAll() { let (dispatcher, target, _) = setup_camel(); - testing::set_contract_address(OWNER()); + + start_cheat_caller_address(dispatcher.contract_address, OWNER()); target.setApprovalForAll(OPERATOR(), true); let is_approved_for_all = dispatcher.is_approved_for_all(OWNER(), OPERATOR()); @@ -354,16 +389,19 @@ fn test_dual_isApprovedForAll() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_isApprovedForAll_exists_and_panics() { let (_, dispatcher) = setup_erc1155_panic(); dispatcher.is_approved_for_all(OWNER(), OPERATOR()); } #[test] +#[ignore] fn test_dual_setApprovalForAll() { let (dispatcher, target, _) = setup_camel(); - testing::set_contract_address(OWNER()); + + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.set_approval_for_all(OPERATOR(), true); let is_approved_for_all = target.isApprovedForAll(OWNER(), OPERATOR()); @@ -371,7 +409,8 @@ fn test_dual_setApprovalForAll() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_setApprovalForAll_exists_and_panics() { let (_, dispatcher) = setup_erc1155_panic(); dispatcher.set_approval_for_all(OPERATOR(), true); diff --git a/src/tests/token/erc1155/test_dual1155_receiver.cairo b/src/tests/token/erc1155/test_dual1155_receiver.cairo index da0b87bba..c207d3e71 100644 --- a/src/tests/token/erc1155/test_dual1155_receiver.cairo +++ b/src/tests/token/erc1155/test_dual1155_receiver.cairo @@ -1,8 +1,3 @@ -use openzeppelin::tests::mocks::erc1155_receiver_mocks::{ - CamelERC1155ReceiverMock, CamelERC1155ReceiverPanicMock, SnakeERC1155ReceiverMock, - SnakeERC1155ReceiverPanicMock -}; -use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils::constants::{EMPTY_DATA, OPERATOR, OWNER, TOKEN_ID, TOKEN_VALUE}; use openzeppelin::tests::utils; use openzeppelin::token::erc1155::dual1155_receiver::{ @@ -21,8 +16,8 @@ use openzeppelin::token::erc1155::interface::{ // fn setup_snake() -> (DualCaseERC1155Receiver, IERC1155ReceiverDispatcher) { - let mut calldata = ArrayTrait::new(); - let target = utils::deploy(SnakeERC1155ReceiverMock::TEST_CLASS_HASH, calldata); + let mut calldata = array![]; + let target = utils::declare_and_deploy("SnakeERC1155ReceiverMock", calldata); ( DualCaseERC1155Receiver { contract_address: target }, IERC1155ReceiverDispatcher { contract_address: target } @@ -30,8 +25,8 @@ fn setup_snake() -> (DualCaseERC1155Receiver, IERC1155ReceiverDispatcher) { } fn setup_camel() -> (DualCaseERC1155Receiver, IERC1155ReceiverCamelDispatcher) { - let mut calldata = ArrayTrait::new(); - let target = utils::deploy(CamelERC1155ReceiverMock::TEST_CLASS_HASH, calldata); + let mut calldata = array![]; + let target = utils::declare_and_deploy("CamelERC1155ReceiverMock", calldata); ( DualCaseERC1155Receiver { contract_address: target }, IERC1155ReceiverCamelDispatcher { contract_address: target } @@ -39,18 +34,14 @@ fn setup_camel() -> (DualCaseERC1155Receiver, IERC1155ReceiverCamelDispatcher) { } fn setup_non_erc1155_receiver() -> DualCaseERC1155Receiver { - let calldata = ArrayTrait::new(); - let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); + let calldata = array![]; + let target = utils::declare_and_deploy("NonImplementingMock", calldata); DualCaseERC1155Receiver { contract_address: target } } fn setup_erc1155_receiver_panic() -> (DualCaseERC1155Receiver, DualCaseERC1155Receiver) { - let snake_target = utils::deploy( - SnakeERC1155ReceiverPanicMock::TEST_CLASS_HASH, ArrayTrait::new() - ); - let camel_target = utils::deploy( - CamelERC1155ReceiverPanicMock::TEST_CLASS_HASH, ArrayTrait::new() - ); + let snake_target = utils::declare_and_deploy("SnakeERC1155ReceiverPanicMock", array![]); + let camel_target = utils::declare_and_deploy("CamelERC1155ReceiverPanicMock", array![]); ( DualCaseERC1155Receiver { contract_address: snake_target }, DualCaseERC1155Receiver { contract_address: camel_target } @@ -62,6 +53,7 @@ fn setup_erc1155_receiver_panic() -> (DualCaseERC1155Receiver, DualCaseERC1155Re // #[test] +#[ignore] fn test_dual_on_erc1155_received() { let (dispatcher, _) = setup_snake(); let result = dispatcher @@ -70,6 +62,7 @@ fn test_dual_on_erc1155_received() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_on_erc1155_received() { let dispatcher = setup_non_erc1155_receiver(); @@ -77,13 +70,15 @@ fn test_dual_no_on_erc1155_received() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_on_erc1155_received_exists_and_panics() { let (dispatcher, _) = setup_erc1155_receiver_panic(); dispatcher.on_erc1155_received(OPERATOR(), OWNER(), TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } #[test] +#[ignore] fn test_dual_on_erc1155_batch_received() { let (dispatcher, _) = setup_snake(); let (token_ids, values) = get_ids_and_values(); @@ -94,6 +89,7 @@ fn test_dual_on_erc1155_batch_received() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_on_erc1155_batch_received() { let dispatcher = setup_non_erc1155_receiver(); @@ -102,7 +98,8 @@ fn test_dual_no_on_erc1155_batch_received() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_on_erc1155_batch_received_exists_and_panics() { let (dispatcher, _) = setup_erc1155_receiver_panic(); let (token_ids, values) = get_ids_and_values(); @@ -114,6 +111,7 @@ fn test_dual_on_erc1155_batch_received_exists_and_panics() { // #[test] +#[ignore] fn test_dual_onERC1155Received() { let (dispatcher, _) = setup_camel(); let result = dispatcher @@ -122,13 +120,15 @@ fn test_dual_onERC1155Received() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_onERC1155Received_exists_and_panics() { let (_, dispatcher) = setup_erc1155_receiver_panic(); dispatcher.on_erc1155_received(OPERATOR(), OWNER(), TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } #[test] +#[ignore] fn test_dual_onERC1155BatchReceived() { let (dispatcher, _) = setup_camel(); let (token_ids, values) = get_ids_and_values(); @@ -139,7 +139,8 @@ fn test_dual_onERC1155BatchReceived() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_onERC1155BatchReceived_exists_and_panics() { let (_, dispatcher) = setup_erc1155_receiver_panic(); let (token_ids, values) = get_ids_and_values(); diff --git a/src/tests/token/erc1155/test_erc1155.cairo b/src/tests/token/erc1155/test_erc1155.cairo index 978bc1634..db07832e5 100644 --- a/src/tests/token/erc1155/test_erc1155.cairo +++ b/src/tests/token/erc1155/test_erc1155.cairo @@ -7,26 +7,17 @@ use openzeppelin::tests::utils::constants::{ EMPTY_DATA, ZERO, OWNER, RECIPIENT, OPERATOR, OTHER, TOKEN_ID, TOKEN_ID_2, TOKEN_VALUE, TOKEN_VALUE_2 }; -use openzeppelin::tests::utils; use openzeppelin::token::erc1155::ERC1155Component::ERC1155CamelImpl; -use openzeppelin::token::erc1155::ERC1155Component::{ - ERC1155Impl, ERC1155MetadataURIImpl, InternalImpl -}; -use openzeppelin::token::erc1155::ERC1155Component::{TransferBatch, ApprovalForAll, TransferSingle}; +use openzeppelin::token::erc1155::ERC1155Component::{ERC1155Impl, InternalImpl}; use openzeppelin::token::erc1155::ERC1155Component; use openzeppelin::token::erc1155; -use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::{spy_events, test_address, start_cheat_caller_address}; use starknet::ContractAddress; -use starknet::testing; use super::common::{ - assert_only_event_transfer_single, assert_only_event_transfer_batch, - assert_only_event_approval_for_all -}; -use super::common::{ - setup_account, setup_account_with_salt, setup_src5, setup_receiver, setup_camel_receiver + setup_account, deploy_another_account_at, setup_src5, setup_receiver, setup_camel_receiver }; -use super::common::{get_ids_and_values, get_ids_and_split_values}; +use super::common::{ERC1155SpyHelpers, get_ids_and_values, get_ids_and_split_values}; // // Setup @@ -50,7 +41,6 @@ fn setup() -> (ComponentState, ContractAddress) { let values = array![TOKEN_VALUE, TOKEN_VALUE_2].span(); state.batch_mint_with_acceptance_check(owner, token_ids, values, array![].span()); - utils::drop_events(ZERO(), 2); (state, owner) } @@ -152,26 +142,37 @@ fn test_balanceOfBatch_invalid_inputs() { fn test_safe_transfer_from_owner_to_receiver() { let (mut state, owner) = setup(); let recipient = setup_receiver(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_single(owner, recipient, TOKEN_ID); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_single(owner, recipient, TOKEN_ID); state.safe_transfer_from(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(ZERO(), owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE + ); assert_state_after_transfer_single(owner, recipient, TOKEN_ID); } #[test] +#[ignore] fn test_safe_transfer_from_owner_to_camel_receiver() { let (mut state, owner) = setup(); let recipient = setup_camel_receiver(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_single(owner, recipient, TOKEN_ID); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_single(owner, recipient, TOKEN_ID); state.safe_transfer_from(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(ZERO(), owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE + ); assert_state_after_transfer_single(owner, recipient, TOKEN_ID); } @@ -180,26 +181,37 @@ fn test_safe_transfer_from_owner_to_camel_receiver() { fn test_safeTransferFrom_owner_to_receiver() { let (mut state, owner) = setup(); let recipient = setup_receiver(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_single(owner, recipient, TOKEN_ID); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_single(owner, recipient, TOKEN_ID); state.safeTransferFrom(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(ZERO(), owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE + ); assert_state_after_transfer_single(owner, recipient, TOKEN_ID); } #[test] +#[ignore] fn test_safeTransferFrom_owner_to_camel_receiver() { let (mut state, owner) = setup(); let recipient = setup_camel_receiver(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_single(owner, recipient, TOKEN_ID); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_single(owner, recipient, TOKEN_ID); state.safeTransferFrom(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(ZERO(), owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE + ); assert_state_after_transfer_single(owner, recipient, TOKEN_ID); } @@ -207,13 +219,19 @@ fn test_safeTransferFrom_owner_to_camel_receiver() { #[test] fn test_safe_transfer_from_owner_to_account() { let (mut state, owner) = setup(); - let recipient = setup_account_with_salt(1); - testing::set_caller_address(owner); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_single(owner, recipient, TOKEN_ID); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_single(owner, recipient, TOKEN_ID); state.safe_transfer_from(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(ZERO(), owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE + ); assert_state_after_transfer_single(owner, recipient, TOKEN_ID); } @@ -221,13 +239,19 @@ fn test_safe_transfer_from_owner_to_account() { #[test] fn test_safeTransferFrom_owner_to_account() { let (mut state, owner) = setup(); - let recipient = setup_account_with_salt(1); - testing::set_caller_address(owner); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_single(owner, recipient, TOKEN_ID); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_single(owner, recipient, TOKEN_ID); state.safeTransferFrom(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(ZERO(), owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE + ); assert_state_after_transfer_single(owner, recipient, TOKEN_ID); } @@ -235,18 +259,24 @@ fn test_safeTransferFrom_owner_to_account() { #[test] fn test_safe_transfer_from_approved_operator() { let (mut state, owner) = setup(); - let recipient = setup_account_with_salt(1); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); let operator = OPERATOR(); + let mut spy = spy_events(); + let contract_address = test_address(); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.set_approval_for_all(operator, true); - assert_only_event_approval_for_all(ZERO(), owner, operator, true); + spy.assert_only_event_approval_for_all(contract_address, owner, operator, true); assert_state_before_transfer_single(owner, recipient, TOKEN_ID); - testing::set_caller_address(operator); + start_cheat_caller_address(contract_address, operator); state.safe_transfer_from(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(ZERO(), operator, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, operator, owner, recipient, TOKEN_ID, TOKEN_VALUE + ); assert_state_after_transfer_single(owner, recipient, TOKEN_ID); } @@ -254,18 +284,24 @@ fn test_safe_transfer_from_approved_operator() { #[test] fn test_safeTransferFrom_approved_operator() { let (mut state, owner) = setup(); - let recipient = setup_account_with_salt(1); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); let operator = OPERATOR(); + let mut spy = spy_events(); + let contract_address = test_address(); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.set_approval_for_all(operator, true); - assert_only_event_approval_for_all(ZERO(), owner, operator, true); + spy.assert_only_event_approval_for_all(contract_address, owner, operator, true); assert_state_before_transfer_single(owner, recipient, TOKEN_ID); - testing::set_caller_address(operator); + start_cheat_caller_address(contract_address, operator); state.safeTransferFrom(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(ZERO(), operator, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, operator, owner, recipient, TOKEN_ID, TOKEN_VALUE + ); assert_state_after_transfer_single(owner, recipient, TOKEN_ID); } @@ -274,7 +310,7 @@ fn test_safeTransferFrom_approved_operator() { #[should_panic(expected: ('ERC1155: invalid sender',))] fn test_safe_transfer_from_from_zero() { let (mut state, owner) = setup(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safe_transfer_from(ZERO(), owner, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } @@ -283,7 +319,7 @@ fn test_safe_transfer_from_from_zero() { #[should_panic(expected: ('ERC1155: invalid sender',))] fn test_safeTransferFrom_from_zero() { let (mut state, owner) = setup(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safeTransferFrom(ZERO(), owner, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } @@ -292,7 +328,7 @@ fn test_safeTransferFrom_from_zero() { #[should_panic(expected: ('ERC1155: invalid receiver',))] fn test_safe_transfer_from_to_zero() { let (mut state, owner) = setup(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safe_transfer_from(owner, ZERO(), TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } @@ -301,7 +337,7 @@ fn test_safe_transfer_from_to_zero() { #[should_panic(expected: ('ERC1155: invalid receiver',))] fn test_safeTransferFrom_to_zero() { let (mut state, owner) = setup(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safeTransferFrom(owner, ZERO(), TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } @@ -310,7 +346,7 @@ fn test_safeTransferFrom_to_zero() { #[should_panic(expected: ('ERC1155: unauthorized operator',))] fn test_safe_transfer_from_unauthorized() { let (mut state, owner) = setup(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safe_transfer_from(OTHER(), owner, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } @@ -319,7 +355,7 @@ fn test_safe_transfer_from_unauthorized() { #[should_panic(expected: ('ERC1155: unauthorized operator',))] fn test_safeTransferFrom_unauthorized() { let (mut state, owner) = setup(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safeTransferFrom(OTHER(), owner, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } @@ -328,7 +364,7 @@ fn test_safeTransferFrom_unauthorized() { #[should_panic(expected: ('ERC1155: insufficient balance',))] fn test_safe_transfer_from_insufficient_balance() { let (mut state, owner) = setup(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safe_transfer_from(owner, OTHER(), TOKEN_ID, TOKEN_VALUE + 1, EMPTY_DATA()); } @@ -337,7 +373,7 @@ fn test_safe_transfer_from_insufficient_balance() { #[should_panic(expected: ('ERC1155: insufficient balance',))] fn test_safeTransferFrom_insufficient_balance() { let (mut state, owner) = setup(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safeTransferFrom(owner, OTHER(), TOKEN_ID, TOKEN_VALUE + 1, EMPTY_DATA()); } @@ -347,7 +383,7 @@ fn test_safeTransferFrom_insufficient_balance() { fn test_safe_transfer_from_non_account_non_receiver() { let (mut state, owner) = setup(); let non_receiver = setup_src5(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safe_transfer_from(owner, non_receiver, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } @@ -357,7 +393,7 @@ fn test_safe_transfer_from_non_account_non_receiver() { fn test_safeTransferFrom_non_account_non_receiver() { let (mut state, owner) = setup(); let non_receiver = setup_src5(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safeTransferFrom(owner, non_receiver, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } @@ -371,27 +407,38 @@ fn test_safe_batch_transfer_from_owner_to_receiver() { let (mut state, owner) = setup(); let recipient = setup_receiver(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_batch(owner, recipient, token_ids, values); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_batch(owner, recipient, token_ids, values); state.safe_batch_transfer_from(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), owner, owner, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, owner, recipient, token_ids, values + ); assert_state_after_transfer_batch(owner, recipient, token_ids, values); } #[test] +#[ignore] fn test_safe_batch_transfer_from_owner_to_camel_receiver() { let (mut state, owner) = setup(); let recipient = setup_camel_receiver(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_batch(owner, recipient, token_ids, values); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_batch(owner, recipient, token_ids, values); state.safe_batch_transfer_from(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), owner, owner, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, owner, recipient, token_ids, values + ); assert_state_after_transfer_batch(owner, recipient, token_ids, values); } @@ -401,27 +448,38 @@ fn test_safeBatchTransferFrom_owner_to_receiver() { let (mut state, owner) = setup(); let recipient = setup_receiver(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_batch(owner, recipient, token_ids, values); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_batch(owner, recipient, token_ids, values); state.safeBatchTransferFrom(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), owner, owner, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, owner, recipient, token_ids, values + ); assert_state_after_transfer_batch(owner, recipient, token_ids, values); } #[test] +#[ignore] fn test_safeBatchTransferFrom_owner_to_camel_receiver() { let (mut state, owner) = setup(); let recipient = setup_camel_receiver(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_batch(owner, recipient, token_ids, values); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_batch(owner, recipient, token_ids, values); state.safeBatchTransferFrom(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), owner, owner, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, owner, recipient, token_ids, values + ); assert_state_after_transfer_batch(owner, recipient, token_ids, values); } @@ -429,14 +487,20 @@ fn test_safeBatchTransferFrom_owner_to_camel_receiver() { #[test] fn test_safe_batch_transfer_from_owner_to_account() { let (mut state, owner) = setup(); - let recipient = setup_account_with_salt(1); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_batch(owner, recipient, token_ids, values); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_batch(owner, recipient, token_ids, values); state.safe_batch_transfer_from(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), owner, owner, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, owner, recipient, token_ids, values + ); assert_state_after_transfer_batch(owner, recipient, token_ids, values); } @@ -444,14 +508,20 @@ fn test_safe_batch_transfer_from_owner_to_account() { #[test] fn test_safeBatchTransferFrom_owner_to_account() { let (mut state, owner) = setup(); - let recipient = setup_account_with_salt(1); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_batch(owner, recipient, token_ids, values); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_batch(owner, recipient, token_ids, values); state.safeBatchTransferFrom(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), owner, owner, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, owner, recipient, token_ids, values + ); assert_state_after_transfer_batch(owner, recipient, token_ids, values); } @@ -460,19 +530,25 @@ fn test_safeBatchTransferFrom_owner_to_account() { #[test] fn test_safe_batch_transfer_from_approved_operator() { let (mut state, owner) = setup(); - let recipient = setup_account_with_salt(1); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); let operator = OPERATOR(); let (token_ids, values) = get_ids_and_values(); + let mut spy = spy_events(); + let contract_address = test_address(); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.set_approval_for_all(operator, true); - assert_only_event_approval_for_all(ZERO(), owner, operator, true); + spy.assert_only_event_approval_for_all(contract_address, owner, operator, true); assert_state_before_transfer_batch(owner, recipient, token_ids, values); - testing::set_caller_address(operator); + start_cheat_caller_address(contract_address, operator); state.safe_batch_transfer_from(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), operator, owner, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, operator, owner, recipient, token_ids, values + ); assert_state_after_transfer_batch(owner, recipient, token_ids, values); } @@ -480,19 +556,25 @@ fn test_safe_batch_transfer_from_approved_operator() { #[test] fn test_safeBatchTransferFrom_approved_operator() { let (mut state, owner) = setup(); - let recipient = setup_account_with_salt(1); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); let operator = OPERATOR(); let (token_ids, values) = get_ids_and_values(); + let mut spy = spy_events(); + let contract_address = test_address(); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.set_approval_for_all(operator, true); - assert_only_event_approval_for_all(ZERO(), owner, operator, true); + spy.assert_only_event_approval_for_all(contract_address, owner, operator, true); assert_state_before_transfer_batch(owner, recipient, token_ids, values); - testing::set_caller_address(operator); + start_cheat_caller_address(contract_address, operator); state.safeBatchTransferFrom(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), operator, owner, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, operator, owner, recipient, token_ids, values + ); assert_state_after_transfer_batch(owner, recipient, token_ids, values); } @@ -502,7 +584,8 @@ fn test_safeBatchTransferFrom_approved_operator() { fn test_safe_batch_transfer_from_from_zero() { let (mut state, owner) = setup(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + + start_cheat_caller_address(test_address(), owner); state.safe_batch_transfer_from(ZERO(), owner, token_ids, values, EMPTY_DATA()); } @@ -512,7 +595,8 @@ fn test_safe_batch_transfer_from_from_zero() { fn test_safeBatchTransferFrom_from_zero() { let (mut state, owner) = setup(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + + start_cheat_caller_address(test_address(), owner); state.safeBatchTransferFrom(ZERO(), owner, token_ids, values, EMPTY_DATA()); } @@ -522,7 +606,7 @@ fn test_safeBatchTransferFrom_from_zero() { fn test_safe_batch_transfer_from_to_zero() { let (mut state, owner) = setup(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safe_batch_transfer_from(owner, ZERO(), token_ids, values, EMPTY_DATA()); } @@ -532,7 +616,7 @@ fn test_safe_batch_transfer_from_to_zero() { fn test_safeBatchTransferFrom_to_zero() { let (mut state, owner) = setup(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safeBatchTransferFrom(owner, ZERO(), token_ids, values, EMPTY_DATA()); } @@ -542,7 +626,7 @@ fn test_safeBatchTransferFrom_to_zero() { fn test_safe_batch_transfer_from_unauthorized() { let (mut state, owner) = setup(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safe_batch_transfer_from(OTHER(), owner, token_ids, values, EMPTY_DATA()); } @@ -552,7 +636,7 @@ fn test_safe_batch_transfer_from_unauthorized() { fn test_safeBatchTransferFrom_unauthorized() { let (mut state, owner) = setup(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safeBatchTransferFrom(OTHER(), owner, token_ids, values, EMPTY_DATA()); } @@ -563,7 +647,7 @@ fn test_safe_batch_transfer_from_insufficient_balance() { let (mut state, owner) = setup(); let token_ids = array![TOKEN_ID, TOKEN_ID_2].span(); let values = array![TOKEN_VALUE + 1, TOKEN_VALUE_2].span(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safe_batch_transfer_from(owner, OTHER(), token_ids, values, EMPTY_DATA()); } @@ -574,7 +658,7 @@ fn test_safeBatchTransferFrom_insufficient_balance() { let (mut state, owner) = setup(); let token_ids = array![TOKEN_ID, TOKEN_ID_2].span(); let values = array![TOKEN_VALUE + 1, TOKEN_VALUE_2].span(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safeBatchTransferFrom(owner, OTHER(), token_ids, values, EMPTY_DATA()); } @@ -585,7 +669,7 @@ fn test_safe_batch_transfer_from_non_account_non_receiver() { let (mut state, owner) = setup(); let (token_ids, values) = get_ids_and_split_values(5); let non_receiver = setup_src5(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safe_batch_transfer_from(owner, non_receiver, token_ids, values, EMPTY_DATA()); } @@ -596,7 +680,7 @@ fn test_safeBatchTransferFrom_non_account_non_receiver() { let (mut state, owner) = setup(); let (token_ids, values) = get_ids_and_split_values(5); let non_receiver = setup_src5(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safeBatchTransferFrom(owner, non_receiver, token_ids, values, EMPTY_DATA()); } @@ -608,19 +692,22 @@ fn test_safeBatchTransferFrom_non_account_non_receiver() { #[test] fn test_set_approval_for_all_and_is_approved_for_all() { let mut state = COMPONENT_STATE(); - testing::set_caller_address(OWNER()); + let mut spy = spy_events(); + let contract_address = test_address(); + + start_cheat_caller_address(contract_address, OWNER()); let not_approved_for_all = !state.is_approved_for_all(OWNER(), OPERATOR()); assert!(not_approved_for_all); state.set_approval_for_all(OPERATOR(), true); - assert_only_event_approval_for_all(ZERO(), OWNER(), OPERATOR(), true); + spy.assert_only_event_approval_for_all(contract_address, OWNER(), OPERATOR(), true); let is_approved_for_all = state.is_approved_for_all(OWNER(), OPERATOR()); assert!(is_approved_for_all); state.set_approval_for_all(OPERATOR(), false); - assert_only_event_approval_for_all(ZERO(), OWNER(), OPERATOR(), false); + spy.assert_only_event_approval_for_all(contract_address, OWNER(), OPERATOR(), false); let not_approved_for_all = !state.is_approved_for_all(OWNER(), OPERATOR()); assert!(not_approved_for_all); @@ -630,7 +717,7 @@ fn test_set_approval_for_all_and_is_approved_for_all() { #[should_panic(expected: ('ERC1155: self approval',))] fn test_set_approval_for_all_owner_equal_operator_true() { let mut state = COMPONENT_STATE(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.set_approval_for_all(OWNER(), true); } @@ -638,7 +725,7 @@ fn test_set_approval_for_all_owner_equal_operator_true() { #[should_panic(expected: ('ERC1155: self approval',))] fn test_set_approval_for_all_owner_equal_operator_false() { let mut state = COMPONENT_STATE(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.set_approval_for_all(OWNER(), false); } @@ -649,19 +736,22 @@ fn test_set_approval_for_all_owner_equal_operator_false() { #[test] fn test_setApprovalForAll_and_isApprovedForAll() { let mut state = COMPONENT_STATE(); - testing::set_caller_address(OWNER()); + let mut spy = spy_events(); + let contract_address = test_address(); + + start_cheat_caller_address(contract_address, OWNER()); let not_approved_for_all = !state.isApprovedForAll(OWNER(), OPERATOR()); assert!(not_approved_for_all); state.setApprovalForAll(OPERATOR(), true); - assert_only_event_approval_for_all(ZERO(), OWNER(), OPERATOR(), true); + spy.assert_only_event_approval_for_all(contract_address, OWNER(), OPERATOR(), true); let is_approved_for_all = state.isApprovedForAll(OWNER(), OPERATOR()); assert!(is_approved_for_all); state.setApprovalForAll(OPERATOR(), false); - assert_only_event_approval_for_all(ZERO(), OWNER(), OPERATOR(), false); + spy.assert_only_event_approval_for_all(contract_address, OWNER(), OPERATOR(), false); let not_approved_for_all = !state.isApprovedForAll(OWNER(), OPERATOR()); assert!(not_approved_for_all); @@ -671,7 +761,7 @@ fn test_setApprovalForAll_and_isApprovedForAll() { #[should_panic(expected: ('ERC1155: self approval',))] fn test_setApprovalForAll_owner_equal_operator_true() { let mut state = COMPONENT_STATE(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.set_approval_for_all(OWNER(), true); } @@ -679,7 +769,7 @@ fn test_setApprovalForAll_owner_equal_operator_true() { #[should_panic(expected: ('ERC1155: self approval',))] fn test_setApprovalForAll_owner_equal_operator_false() { let mut state = COMPONENT_STATE(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.setApprovalForAll(OWNER(), false); } @@ -693,12 +783,17 @@ fn test_update_single_from_non_zero_to_non_zero() { let recipient = RECIPIENT(); let token_ids = array![TOKEN_ID].span(); let values = array![TOKEN_VALUE].span(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_single(owner, recipient, TOKEN_ID); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_single(owner, recipient, TOKEN_ID); state.update(owner, recipient, token_ids, values); - assert_only_event_transfer_single(ZERO(), owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE + ); assert_state_after_transfer_single(owner, recipient, TOKEN_ID); } @@ -708,12 +803,17 @@ fn test_update_batch_from_non_zero_to_non_zero() { let (mut state, owner) = setup(); let recipient = RECIPIENT(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_batch(owner, recipient, token_ids, values); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_batch(owner, recipient, token_ids, values); state.update(owner, recipient, token_ids, values); - assert_only_event_transfer_batch(ZERO(), owner, owner, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, owner, recipient, token_ids, values + ); assert_state_after_transfer_batch(owner, recipient, token_ids, values); } @@ -723,12 +823,17 @@ fn test_update_from_non_zero_to_zero() { let (mut state, owner) = setup(); let recipient = ZERO(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_batch(owner, recipient, token_ids, values); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_batch(owner, recipient, token_ids, values); state.update(owner, recipient, token_ids, values); - assert_only_event_transfer_batch(ZERO(), owner, owner, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, owner, recipient, token_ids, values + ); assert_state_after_transfer_to_zero_batch(owner, recipient, token_ids); } @@ -739,12 +844,17 @@ fn test_update_from_zero_to_non_zero() { let recipient = RECIPIENT(); let sender = ZERO(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_from_zero_batch(sender, recipient, token_ids); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_from_zero_batch(sender, recipient, token_ids); state.update(sender, recipient, token_ids, values); - assert_only_event_transfer_batch(ZERO(), owner, sender, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, sender, recipient, token_ids, values + ); assert_state_after_transfer_from_zero_batch(sender, recipient, token_ids, values); } @@ -793,28 +903,39 @@ fn test_update_wac_single_from_non_zero_to_non_zero() { let recipient = setup_receiver(); let token_ids = array![TOKEN_ID].span(); let values = array![TOKEN_VALUE].span(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_single(owner, recipient, TOKEN_ID); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_single(owner, recipient, TOKEN_ID); state.update_with_acceptance_check(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_single(ZERO(), owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE + ); assert_state_after_transfer_single(owner, recipient, TOKEN_ID); } #[test] +#[ignore] fn test_update_wac_single_from_non_zero_to_non_zero_camel_receiver() { let (mut state, owner) = setup(); let recipient = setup_camel_receiver(); let token_ids = array![TOKEN_ID].span(); let values = array![TOKEN_VALUE].span(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_single(owner, recipient, TOKEN_ID); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_single(owner, recipient, TOKEN_ID); state.update_with_acceptance_check(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_single(ZERO(), owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE + ); assert_state_after_transfer_single(owner, recipient, TOKEN_ID); } @@ -822,15 +943,21 @@ fn test_update_wac_single_from_non_zero_to_non_zero_camel_receiver() { #[test] fn test_update_wac_single_from_non_zero_to_non_zero_account() { let (mut state, owner) = setup(); - let recipient = setup_account_with_salt(1); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); let token_ids = array![TOKEN_ID].span(); let values = array![TOKEN_VALUE].span(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_single(owner, recipient, TOKEN_ID); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_single(owner, recipient, TOKEN_ID); state.update_with_acceptance_check(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_single(ZERO(), owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE + ); assert_state_after_transfer_single(owner, recipient, TOKEN_ID); } @@ -840,27 +967,38 @@ fn test_update_wac_batch_from_non_zero_to_non_zero() { let (mut state, owner) = setup(); let recipient = setup_receiver(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_batch(owner, recipient, token_ids, values); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_batch(owner, recipient, token_ids, values); state.update_with_acceptance_check(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), owner, owner, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, owner, recipient, token_ids, values + ); assert_state_after_transfer_batch(owner, recipient, token_ids, values); } #[test] +#[ignore] fn test_update_wac_batch_from_non_zero_to_non_zero_camel_receiver() { let (mut state, owner) = setup(); let recipient = setup_camel_receiver(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_batch(owner, recipient, token_ids, values); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_batch(owner, recipient, token_ids, values); state.update_with_acceptance_check(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), owner, owner, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, owner, recipient, token_ids, values + ); assert_state_after_transfer_batch(owner, recipient, token_ids, values); } @@ -868,25 +1006,35 @@ fn test_update_wac_batch_from_non_zero_to_non_zero_camel_receiver() { #[test] fn test_update_wac_batch_from_non_zero_to_non_zero_account() { let (mut state, owner) = setup(); - let recipient = setup_account_with_salt(1); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, owner); assert_state_before_transfer_batch(owner, recipient, token_ids, values); state.update_with_acceptance_check(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), owner, owner, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, owner, recipient, token_ids, values + ); assert_state_after_transfer_batch(owner, recipient, token_ids, values); } #[test] -#[should_panic(expected: ('CONTRACT_NOT_DEPLOYED',))] +#[should_panic( + expected: ( + "Contract not deployed at address: 0x0000000000000000000000000000000000000000000000000000000000000000", + ) +)] fn test_update_wac_from_non_zero_to_zero() { let (mut state, owner) = setup(); let recipient = ZERO(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.update_with_acceptance_check(owner, recipient, token_ids, values, EMPTY_DATA()); } @@ -897,28 +1045,39 @@ fn test_update_wac_from_zero_to_non_zero() { let recipient = setup_receiver(); let sender = ZERO(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_from_zero_batch(sender, recipient, token_ids); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_from_zero_batch(sender, recipient, token_ids); state.update_with_acceptance_check(sender, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), owner, sender, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, sender, recipient, token_ids, values + ); assert_state_after_transfer_from_zero_batch(sender, recipient, token_ids, values); } #[test] +#[ignore] fn test_update_wac_from_zero_to_non_zero_camel_receiver() { let (mut state, owner) = setup(); let recipient = setup_camel_receiver(); let sender = ZERO(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_from_zero_batch(sender, recipient, token_ids); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_from_zero_batch(sender, recipient, token_ids); state.update_with_acceptance_check(sender, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), owner, sender, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, sender, recipient, token_ids, values + ); assert_state_after_transfer_from_zero_batch(sender, recipient, token_ids, values); } @@ -926,15 +1085,21 @@ fn test_update_wac_from_zero_to_non_zero_camel_receiver() { #[test] fn test_update_wac_from_zero_to_non_zero_account() { let (mut state, owner) = setup(); - let recipient = setup_account_with_salt(1); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); let sender = ZERO(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); - assert_state_before_transfer_from_zero_batch(sender, recipient, token_ids); + start_cheat_caller_address(contract_address, owner); + assert_state_before_transfer_from_zero_batch(sender, recipient, token_ids); state.update_with_acceptance_check(sender, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), owner, sender, recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, owner, sender, recipient, token_ids, values + ); assert_state_after_transfer_from_zero_batch(sender, recipient, token_ids, values); } @@ -979,7 +1144,7 @@ fn test_update_wac_single_to_non_receiver() { let recipient = setup_src5(); let token_ids = array![TOKEN_ID].span(); let values = array![TOKEN_VALUE].span(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.update_with_acceptance_check(owner, recipient, token_ids, values, EMPTY_DATA()); } @@ -990,7 +1155,7 @@ fn test_update_wac_batch_to_non_receiver() { let (mut state, owner) = setup(); let recipient = setup_src5(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.update_with_acceptance_check(owner, recipient, token_ids, values, EMPTY_DATA()); } @@ -1003,13 +1168,19 @@ fn test_update_wac_batch_to_non_receiver() { fn test_mint_wac_to_receiver() { let mut state = COMPONENT_STATE(); let recipient = setup_receiver(); - testing::set_caller_address(OTHER()); + let mut spy = spy_events(); + let contract_address = test_address(); + + start_cheat_caller_address(test_address(), OTHER()); let balance_of_recipient = state.balance_of(recipient, TOKEN_ID); assert!(balance_of_recipient.is_zero()); state.mint_with_acceptance_check(recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(ZERO(), OTHER(), ZERO(), recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, OTHER(), ZERO(), recipient, TOKEN_ID, TOKEN_VALUE + ); let balance_of_recipient = state.balance_of(recipient, TOKEN_ID); assert_eq!(balance_of_recipient, TOKEN_VALUE); @@ -1018,14 +1189,20 @@ fn test_mint_wac_to_receiver() { #[test] fn test_mint_wac_to_account() { let mut state = COMPONENT_STATE(); - let recipient = setup_account_with_salt(1); - testing::set_caller_address(OTHER()); + let recipient = setup_account(); + let mut spy = spy_events(); + let contract_address = test_address(); + + start_cheat_caller_address(test_address(), OTHER()); let balance_of_recipient = state.balance_of(recipient, TOKEN_ID); assert!(balance_of_recipient.is_zero()); state.mint_with_acceptance_check(recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(ZERO(), OTHER(), ZERO(), recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, OTHER(), ZERO(), recipient, TOKEN_ID, TOKEN_VALUE + ); let balance_of_recipient = state.balance_of(recipient, TOKEN_ID); assert_eq!(balance_of_recipient, TOKEN_VALUE); @@ -1058,7 +1235,10 @@ fn test_batch_mint_wac_to_receiver() { let mut state = COMPONENT_STATE(); let recipient = setup_receiver(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(OTHER()); + let mut spy = spy_events(); + let contract_address = test_address(); + + start_cheat_caller_address(contract_address, OTHER()); let balance_of_recipient_token_1_before = state.balance_of(recipient, TOKEN_ID); assert!(balance_of_recipient_token_1_before.is_zero()); @@ -1066,7 +1246,10 @@ fn test_batch_mint_wac_to_receiver() { assert!(balance_of_recipient_token_2_before.is_zero()); state.batch_mint_with_acceptance_check(recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), OTHER(), ZERO(), recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, OTHER(), ZERO(), recipient, token_ids, values + ); let balance_of_recipient_token_1_after = state.balance_of(recipient, TOKEN_ID); assert_eq!(balance_of_recipient_token_1_after, TOKEN_VALUE); @@ -1077,9 +1260,12 @@ fn test_batch_mint_wac_to_receiver() { #[test] fn test_batch_mint_wac_to_account() { let mut state = COMPONENT_STATE(); - let recipient = setup_account_with_salt(1); + let recipient = setup_account(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(OTHER()); + let mut spy = spy_events(); + let contract_address = test_address(); + + start_cheat_caller_address(contract_address, OTHER()); let balance_of_recipient_token_1_before = state.balance_of(recipient, TOKEN_ID); assert!(balance_of_recipient_token_1_before.is_zero()); @@ -1087,7 +1273,10 @@ fn test_batch_mint_wac_to_account() { assert!(balance_of_recipient_token_2_before.is_zero()); state.batch_mint_with_acceptance_check(recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(ZERO(), OTHER(), ZERO(), recipient, token_ids, values); + spy + .assert_only_event_transfer_batch( + contract_address, OTHER(), ZERO(), recipient, token_ids, values + ); let balance_of_recipient_token_1_after = state.balance_of(recipient, TOKEN_ID); assert_eq!(balance_of_recipient_token_1_after, TOKEN_VALUE); @@ -1122,13 +1311,19 @@ fn test_batch_mint_wac_to_non_receiver() { #[test] fn test_burn() { let (mut state, owner) = setup(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); + + start_cheat_caller_address(contract_address, owner); let balance_of_owner = state.balance_of(owner, TOKEN_ID); assert_eq!(balance_of_owner, TOKEN_VALUE); state.burn(owner, TOKEN_ID, TOKEN_VALUE); - assert_only_event_transfer_single(ZERO(), owner, owner, ZERO(), TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract_address, owner, owner, ZERO(), TOKEN_ID, TOKEN_VALUE + ); let balance_of_owner = state.balance_of(owner, TOKEN_ID); assert!(balance_of_owner.is_zero()); @@ -1146,7 +1341,10 @@ fn test_burn_from_zero() { fn test_batch_burn() { let (mut state, owner) = setup(); let (token_ids, values) = get_ids_and_values(); - testing::set_caller_address(owner); + let mut spy = spy_events(); + let contract_address = test_address(); + + start_cheat_caller_address(contract_address, owner); let balance_of_owner_token_1_before = state.balance_of(owner, TOKEN_ID); assert_eq!(balance_of_owner_token_1_before, TOKEN_VALUE); @@ -1154,7 +1352,7 @@ fn test_batch_burn() { assert_eq!(balance_of_owner_token_2_before, TOKEN_VALUE_2); state.batch_burn(owner, token_ids, values); - assert_only_event_transfer_batch(ZERO(), owner, owner, ZERO(), token_ids, values); + spy.assert_only_event_transfer_batch(contract_address, owner, owner, ZERO(), token_ids, values); let balance_of_owner_token_1_after = state.balance_of(owner, TOKEN_ID); assert!(balance_of_owner_token_1_after.is_zero()); diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index 34b29154d..64abc3023 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -2,6 +2,8 @@ pub(crate) mod common; pub(crate) mod constants; pub(crate) mod events; pub(crate) mod signing; -pub use common::{declare_class, declare_and_deploy, declare_and_deploy_at, deploy, deploy_at}; +pub use common::{ + declare_class, declare_and_deploy, declare_and_deploy_at, deploy, deploy_at, deploy_another_at +}; pub use events::EventSpyExt; diff --git a/src/tests/utils/common.cairo b/src/tests/utils/common.cairo index 27a7698db..88c443cfb 100644 --- a/src/tests/utils/common.cairo +++ b/src/tests/utils/common.cairo @@ -1,4 +1,5 @@ -use snforge_std::{declare, ContractClass, ContractClassTrait}; +use core::starknet::SyscallResultTrait; +use snforge_std::{declare, get_class_hash, ContractClass, ContractClassTrait}; use starknet::ContractAddress; pub fn deploy(contract_class: ContractClass, calldata: Array) -> ContractAddress { @@ -17,18 +18,27 @@ pub fn deploy_at( }; } +/// Deploys a contract from the class hash of another contract which is already deployed. +pub fn deploy_another_at( + existing: ContractAddress, target_address: ContractAddress, calldata: Array +) { + let class_hash = get_class_hash(existing); + let contract_class = ContractClassTrait::new(class_hash); + deploy_at(contract_class, target_address, calldata) +} + pub fn declare_class(contract_name: ByteArray) -> ContractClass { - declare(contract_name).unwrap() + declare(contract_name).unwrap_syscall() } pub fn declare_and_deploy(contract_name: ByteArray, calldata: Array) -> ContractAddress { - let contract_class = declare(contract_name).unwrap(); + let contract_class = declare(contract_name).unwrap_syscall(); deploy(contract_class, calldata) } pub fn declare_and_deploy_at( contract_name: ByteArray, target_address: ContractAddress, calldata: Array ) { - let contract_class = declare(contract_name).expect('Failed to declare contract'); + let contract_class = declare(contract_name).unwrap_syscall(); deploy_at(contract_class, target_address, calldata) } From ef89fb273f1dbe5353000c6440b42a95078c269d Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 11 Jul 2024 13:25:17 +0200 Subject: [PATCH 06/20] Migrate erc721 tests (#1027) * feat: migrate event tests * feat: update workflow * feat: add foundry utils * feat: finish erc20 and erc20votes migration * feat: remove dual case tests * refactor: format files * refactor: variable name * Update src/tests/token.cairo Co-authored-by: Andrew Fleming * Update src/tests/token/erc20.cairo Co-authored-by: Andrew Fleming * Update src/tests/utils/foundry.cairo Co-authored-by: Andrew Fleming * Update src/tests/token/erc20/test_erc20_votes.cairo Co-authored-by: Andrew Fleming * feat: apply review updates * feat: migrate erc721 components tests * refactor: remove extra line * feat: apply review updates * feat: update utilities * feat: apply review updates * fix: ignore tests --------- Co-authored-by: Andrew Fleming --- src/tests/token.cairo | 2 +- src/tests/token/erc20/common.cairo | 1 - src/tests/token/erc721.cairo | 1 + src/tests/token/erc721/common.cairo | 131 ++--- src/tests/token/erc721/test_dual721.cairo | 135 +++-- .../token/erc721/test_dual721_receiver.cairo | 35 +- src/tests/token/erc721/test_erc721.cairo | 480 +++++++++++------- .../token/erc721/test_erc721_receiver.cairo | 2 +- 8 files changed, 473 insertions(+), 314 deletions(-) diff --git a/src/tests/token.cairo b/src/tests/token.cairo index 2ecd8ae11..04f631ea8 100644 --- a/src/tests/token.cairo +++ b/src/tests/token.cairo @@ -1,3 +1,3 @@ pub(crate) mod erc1155; -// pub(crate) mod erc721; pub(crate) mod erc20; +pub(crate) mod erc721; diff --git a/src/tests/token/erc20/common.cairo b/src/tests/token/erc20/common.cairo index be65d0241..a50e5ff2e 100644 --- a/src/tests/token/erc20/common.cairo +++ b/src/tests/token/erc20/common.cairo @@ -1,7 +1,6 @@ use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::token::erc20::ERC20Component::{Approval, Transfer}; use openzeppelin::token::erc20::ERC20Component; -use openzeppelin::utils::serde::SerializedAppend; use snforge_std::EventSpy; use starknet::ContractAddress; diff --git a/src/tests/token/erc721.cairo b/src/tests/token/erc721.cairo index 0f6616806..d8b44e48b 100644 --- a/src/tests/token/erc721.cairo +++ b/src/tests/token/erc721.cairo @@ -3,3 +3,4 @@ pub(crate) mod common; mod test_dual721; mod test_dual721_receiver; mod test_erc721; +mod test_erc721_receiver; diff --git a/src/tests/token/erc721/common.cairo b/src/tests/token/erc721/common.cairo index a6601e5e1..97e94f886 100644 --- a/src/tests/token/erc721/common.cairo +++ b/src/tests/token/erc721/common.cairo @@ -1,75 +1,76 @@ -use openzeppelin::tests::utils; +use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::token::erc721::ERC721Component::{Approval, ApprovalForAll, Transfer}; use openzeppelin::token::erc721::ERC721Component; -use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::EventSpy; use starknet::ContractAddress; -pub(crate) fn assert_event_approval_for_all( - contract: ContractAddress, owner: ContractAddress, operator: ContractAddress, approved: bool -) { - let event = utils::pop_log::(contract).unwrap(); - let expected = ERC721Component::Event::ApprovalForAll( - ApprovalForAll { owner, operator, approved } - ); - assert!(event == expected); +#[generate_trait] +pub(crate) impl ERC721SpyHelpersImpl of ERC721SpyHelpers { + fn assert_event_approval_for_all( + ref self: EventSpy, + contract: ContractAddress, + owner: ContractAddress, + operator: ContractAddress, + approved: bool + ) { + let expected = ERC721Component::Event::ApprovalForAll( + ApprovalForAll { owner, operator, approved } + ); + self.assert_emitted_single(contract, expected); + } - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("ApprovalForAll")); - indexed_keys.append_serde(owner); - indexed_keys.append_serde(operator); - utils::assert_indexed_keys(event, indexed_keys.span()); -} - -pub(crate) fn assert_only_event_approval_for_all( - contract: ContractAddress, owner: ContractAddress, operator: ContractAddress, approved: bool -) { - assert_event_approval_for_all(contract, owner, operator, approved); - utils::assert_no_events_left(contract); -} - -pub(crate) fn assert_event_approval( - contract: ContractAddress, owner: ContractAddress, approved: ContractAddress, token_id: u256 -) { - let event = utils::pop_log::(contract).unwrap(); - let expected = ERC721Component::Event::Approval(Approval { owner, approved, token_id }); - assert!(event == expected); + fn assert_only_event_approval_for_all( + ref self: EventSpy, + contract: ContractAddress, + owner: ContractAddress, + operator: ContractAddress, + approved: bool + ) { + self.assert_event_approval_for_all(contract, owner, operator, approved); + self.assert_no_events_left_from(contract); + } - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("Approval")); - indexed_keys.append_serde(owner); - indexed_keys.append_serde(approved); - indexed_keys.append_serde(token_id); - utils::assert_indexed_keys(event, indexed_keys.span()); -} - -pub(crate) fn assert_only_event_approval( - contract: ContractAddress, owner: ContractAddress, approved: ContractAddress, token_id: u256 -) { - assert_event_approval(contract, owner, approved, token_id); - utils::assert_no_events_left(contract); -} + fn assert_event_approval( + ref self: EventSpy, + contract: ContractAddress, + owner: ContractAddress, + approved: ContractAddress, + token_id: u256 + ) { + let expected = ERC721Component::Event::Approval(Approval { owner, approved, token_id }); + self.assert_emitted_single(contract, expected); + } -pub(crate) fn assert_event_transfer( - contract: ContractAddress, from: ContractAddress, to: ContractAddress, token_id: u256 -) { - let event = utils::pop_log::(contract).unwrap(); - let expected = ERC721Component::Event::Transfer(Transfer { from, to, token_id }); - assert!(event == expected); + fn assert_only_event_approval( + ref self: EventSpy, + contract: ContractAddress, + owner: ContractAddress, + approved: ContractAddress, + token_id: u256 + ) { + self.assert_event_approval(contract, owner, approved, token_id); + self.assert_no_events_left_from(contract); + } - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("Transfer")); - indexed_keys.append_serde(from); - indexed_keys.append_serde(to); - indexed_keys.append_serde(token_id); - utils::assert_indexed_keys(event, indexed_keys.span()); -} + fn assert_event_transfer( + ref self: EventSpy, + contract: ContractAddress, + from: ContractAddress, + to: ContractAddress, + token_id: u256 + ) { + let expected = ERC721Component::Event::Transfer(Transfer { from, to, token_id }); + self.assert_emitted_single(contract, expected); + } -pub(crate) fn assert_only_event_transfer( - contract: ContractAddress, from: ContractAddress, to: ContractAddress, token_id: u256 -) { - assert_event_transfer(contract, from, to, token_id); - utils::assert_no_events_left(contract); + fn assert_only_event_transfer( + ref self: EventSpy, + contract: ContractAddress, + from: ContractAddress, + to: ContractAddress, + token_id: u256 + ) { + self.assert_event_transfer(contract, from, to, token_id); + self.assert_no_events_left_from(contract); + } } diff --git a/src/tests/token/erc721/test_dual721.cairo b/src/tests/token/erc721/test_dual721.cairo index 4cf393bf7..943608c9e 100644 --- a/src/tests/token/erc721/test_dual721.cairo +++ b/src/tests/token/erc721/test_dual721.cairo @@ -1,9 +1,5 @@ -use openzeppelin::tests::mocks::erc721_mocks::{CamelERC721Mock, SnakeERC721Mock}; -use openzeppelin::tests::mocks::erc721_mocks::{CamelERC721PanicMock, SnakeERC721PanicMock}; -use openzeppelin::tests::mocks::erc721_receiver_mocks::DualCaseERC721ReceiverMock; -use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils::constants::{ - DATA, OWNER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, BASE_URI, TOKEN_ID + DATA, OWNER, RECIPIENT, SPENDER, OPERATOR, NAME, SYMBOL, BASE_URI, TOKEN_ID }; use openzeppelin::tests::utils; use openzeppelin::token::erc721::dual721::{DualCaseERC721, DualCaseERC721Trait}; @@ -13,9 +9,8 @@ use openzeppelin::token::erc721::interface::{ }; use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait}; use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::{test_address, start_cheat_caller_address}; use starknet::ContractAddress; -use starknet::testing::set_caller_address; -use starknet::testing::set_contract_address; // // Setup @@ -28,8 +23,9 @@ fn setup_snake() -> (DualCaseERC721, IERC721Dispatcher) { calldata.append_serde(BASE_URI()); calldata.append_serde(OWNER()); calldata.append_serde(TOKEN_ID); - set_contract_address(OWNER()); - let target = utils::deploy(SnakeERC721Mock::TEST_CLASS_HASH, calldata); + + start_cheat_caller_address(test_address(), OWNER()); + let target = utils::declare_and_deploy("SnakeERC721Mock", calldata); (DualCaseERC721 { contract_address: target }, IERC721Dispatcher { contract_address: target }) } @@ -40,8 +36,9 @@ fn setup_camel() -> (DualCaseERC721, IERC721CamelOnlyDispatcher) { calldata.append_serde(BASE_URI()); calldata.append_serde(OWNER()); calldata.append_serde(TOKEN_ID); - set_contract_address(OWNER()); - let target = utils::deploy(CamelERC721Mock::TEST_CLASS_HASH, calldata); + + start_cheat_caller_address(test_address(), OWNER()); + let target = utils::declare_and_deploy("CamelERC721Mock", calldata); ( DualCaseERC721 { contract_address: target }, IERC721CamelOnlyDispatcher { contract_address: target } @@ -50,13 +47,13 @@ fn setup_camel() -> (DualCaseERC721, IERC721CamelOnlyDispatcher) { fn setup_non_erc721() -> DualCaseERC721 { let calldata = array![]; - let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); + let target = utils::declare_and_deploy("NonImplementingMock", calldata); DualCaseERC721 { contract_address: target } } fn setup_erc721_panic() -> (DualCaseERC721, DualCaseERC721) { - let snake_target = utils::deploy(SnakeERC721PanicMock::TEST_CLASS_HASH, array![]); - let camel_target = utils::deploy(CamelERC721PanicMock::TEST_CLASS_HASH, array![]); + let snake_target = utils::declare_and_deploy("SnakeERC721PanicMock", array![]); + let camel_target = utils::declare_and_deploy("CamelERC721PanicMock", array![]); ( DualCaseERC721 { contract_address: snake_target }, DualCaseERC721 { contract_address: camel_target } @@ -64,7 +61,7 @@ fn setup_erc721_panic() -> (DualCaseERC721, DualCaseERC721) { } fn setup_receiver() -> ContractAddress { - utils::deploy(DualCaseERC721ReceiverMock::TEST_CLASS_HASH, array![]) + utils::declare_and_deploy("DualCaseERC721ReceiverMock", array![]) } // @@ -72,6 +69,8 @@ fn setup_receiver() -> ContractAddress { // #[test] +#[ignore] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_name() { let (snake_dispatcher, _) = setup_snake(); let (camel_dispatcher, _) = setup_camel(); @@ -80,6 +79,7 @@ fn test_dual_name() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_name() { let dispatcher = setup_non_erc721(); @@ -87,13 +87,14 @@ fn test_dual_no_name() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_name_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.name(); } #[test] +#[ignore] fn test_dual_symbol() { let (snake_dispatcher, _) = setup_snake(); let (camel_dispatcher, _) = setup_camel(); @@ -102,6 +103,7 @@ fn test_dual_symbol() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_symbol() { let dispatcher = setup_non_erc721(); @@ -109,26 +111,30 @@ fn test_dual_no_symbol() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_symbol_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.symbol(); } #[test] +#[ignore] fn test_dual_approve() { let (snake_dispatcher, snake_target) = setup_snake(); - set_contract_address(OWNER()); + + start_cheat_caller_address(test_address(), OWNER()); snake_dispatcher.approve(SPENDER(), TOKEN_ID); assert_eq!(snake_target.get_approved(TOKEN_ID), SPENDER()); let (camel_dispatcher, camel_target) = setup_camel(); - set_contract_address(OWNER()); + + start_cheat_caller_address(test_address(), OWNER()); camel_dispatcher.approve(SPENDER(), TOKEN_ID); assert_eq!(camel_target.getApproved(TOKEN_ID), SPENDER()); } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_approve() { let dispatcher = setup_non_erc721(); @@ -136,7 +142,7 @@ fn test_dual_no_approve() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_approve_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.approve(SPENDER(), TOKEN_ID); @@ -147,12 +153,14 @@ fn test_dual_approve_exists_and_panics() { // #[test] +#[ignore] fn test_dual_balance_of() { let (dispatcher, _) = setup_snake(); assert_eq!(dispatcher.balance_of(OWNER()), 1); } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_balance_of() { let dispatcher = setup_non_erc721(); @@ -160,19 +168,21 @@ fn test_dual_no_balance_of() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_balance_of_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.balance_of(OWNER()); } #[test] +#[ignore] fn test_dual_owner_of() { let (dispatcher, _) = setup_snake(); assert_eq!(dispatcher.owner_of(TOKEN_ID), OWNER()); } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_owner_of() { let dispatcher = setup_non_erc721(); @@ -180,13 +190,14 @@ fn test_dual_no_owner_of() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_owner_of_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.owner_of(TOKEN_ID); } #[test] +#[ignore] fn test_dual_transfer_from() { let (dispatcher, target) = setup_snake(); dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); @@ -194,6 +205,7 @@ fn test_dual_transfer_from() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_transfer_from() { let dispatcher = setup_non_erc721(); @@ -201,13 +213,14 @@ fn test_dual_no_transfer_from() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_transfer_from_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); } #[test] +#[ignore] fn test_dual_safe_transfer_from() { let (dispatcher, target) = setup_snake(); let receiver = setup_receiver(); @@ -216,6 +229,7 @@ fn test_dual_safe_transfer_from() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_safe_transfer_from() { let dispatcher = setup_non_erc721(); @@ -223,21 +237,24 @@ fn test_dual_no_safe_transfer_from() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_safe_transfer_from_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); } #[test] +#[ignore] fn test_dual_get_approved() { let (dispatcher, target) = setup_snake(); - set_contract_address(OWNER()); + + start_cheat_caller_address(test_address(), OWNER()); target.approve(SPENDER(), TOKEN_ID); assert_eq!(dispatcher.get_approved(TOKEN_ID), SPENDER()); } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_get_approved() { let dispatcher = setup_non_erc721(); @@ -245,16 +262,18 @@ fn test_dual_no_get_approved() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_get_approved_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.get_approved(TOKEN_ID); } #[test] +#[ignore] fn test_dual_set_approval_for_all() { let (dispatcher, target) = setup_snake(); - set_contract_address(OWNER()); + + start_cheat_caller_address(test_address(), OWNER()); dispatcher.set_approval_for_all(OPERATOR(), true); let is_approved_for_all = target.is_approved_for_all(OWNER(), OPERATOR()); @@ -262,6 +281,7 @@ fn test_dual_set_approval_for_all() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_set_approval_for_all() { let dispatcher = setup_non_erc721(); @@ -269,16 +289,18 @@ fn test_dual_no_set_approval_for_all() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_set_approval_for_all_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.set_approval_for_all(OPERATOR(), true); } #[test] +#[ignore] fn test_dual_is_approved_for_all() { let (dispatcher, target) = setup_snake(); - set_contract_address(OWNER()); + + start_cheat_caller_address(test_address(), OWNER()); target.set_approval_for_all(OPERATOR(), true); let is_approved_for_all = dispatcher.is_approved_for_all(OWNER(), OPERATOR()); @@ -286,6 +308,7 @@ fn test_dual_is_approved_for_all() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_is_approved_for_all() { let dispatcher = setup_non_erc721(); @@ -293,13 +316,14 @@ fn test_dual_no_is_approved_for_all() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_is_approved_for_all_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.is_approved_for_all(OWNER(), OPERATOR()); } #[test] +#[ignore] fn test_dual_token_uri() { let (dispatcher, _) = setup_snake(); let uri = dispatcher.token_uri(TOKEN_ID); @@ -308,6 +332,7 @@ fn test_dual_token_uri() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_token_uri() { let dispatcher = setup_non_erc721(); @@ -315,13 +340,14 @@ fn test_dual_no_token_uri() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_token_uri_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.token_uri(TOKEN_ID); } #[test] +#[ignore] fn test_dual_supports_interface() { let (dispatcher, _) = setup_snake(); let supports_ierc721 = dispatcher.supports_interface(IERC721_ID); @@ -329,6 +355,7 @@ fn test_dual_supports_interface() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_supports_interface() { let dispatcher = setup_non_erc721(); @@ -336,7 +363,7 @@ fn test_dual_no_supports_interface() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_supports_interface_exists_and_panics() { let (dispatcher, _) = setup_erc721_panic(); dispatcher.supports_interface(IERC721_ID); @@ -347,19 +374,22 @@ fn test_dual_supports_interface_exists_and_panics() { // #[test] +#[ignore] fn test_dual_balanceOf() { let (dispatcher, _) = setup_camel(); assert_eq!(dispatcher.balance_of(OWNER()), 1); } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_balanceOf_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.balance_of(OWNER()); } #[test] +#[ignore] fn test_dual_ownerOf() { let (dispatcher, _) = setup_camel(); let current_owner = dispatcher.owner_of(TOKEN_ID); @@ -367,16 +397,19 @@ fn test_dual_ownerOf() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_ownerOf_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.owner_of(TOKEN_ID); } #[test] +#[ignore] fn test_dual_transferFrom() { let (dispatcher, target) = setup_camel(); - set_contract_address(OWNER()); + + start_cheat_caller_address(test_address(), OWNER()); dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); let current_owner = target.ownerOf(TOKEN_ID); @@ -384,13 +417,15 @@ fn test_dual_transferFrom() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_transferFrom_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); } #[test] +#[ignore] fn test_dual_safeTransferFrom() { let (dispatcher, target) = setup_camel(); let receiver = setup_receiver(); @@ -401,16 +436,19 @@ fn test_dual_safeTransferFrom() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_safeTransferFrom_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); } #[test] +#[ignore] fn test_dual_getApproved() { let (dispatcher, _) = setup_camel(); - set_contract_address(OWNER()); + + start_cheat_caller_address(test_address(), OWNER()); dispatcher.approve(SPENDER(), TOKEN_ID); let approved = dispatcher.get_approved(TOKEN_ID); @@ -418,16 +456,19 @@ fn test_dual_getApproved() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_getApproved_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.get_approved(TOKEN_ID); } #[test] +#[ignore] fn test_dual_setApprovalForAll() { let (dispatcher, target) = setup_camel(); - set_contract_address(OWNER()); + + start_cheat_caller_address(test_address(), OWNER()); dispatcher.set_approval_for_all(OPERATOR(), true); let is_approved_for_all = target.isApprovedForAll(OWNER(), OPERATOR()); @@ -435,16 +476,19 @@ fn test_dual_setApprovalForAll() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_setApprovalForAll_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.set_approval_for_all(OPERATOR(), true); } #[test] +#[ignore] fn test_dual_isApprovedForAll() { let (dispatcher, target) = setup_camel(); - set_contract_address(OWNER()); + + start_cheat_caller_address(test_address(), OWNER()); target.setApprovalForAll(OPERATOR(), true); let is_approved_for_all = dispatcher.is_approved_for_all(OWNER(), OPERATOR()); @@ -452,13 +496,15 @@ fn test_dual_isApprovedForAll() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_isApprovedForAll_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.is_approved_for_all(OWNER(), OPERATOR()); } #[test] +#[ignore] fn test_dual_tokenURI() { let (dispatcher, _) = setup_camel(); let uri = dispatcher.token_uri(TOKEN_ID); @@ -467,7 +513,8 @@ fn test_dual_tokenURI() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_tokenURI_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); dispatcher.token_uri(TOKEN_ID); diff --git a/src/tests/token/erc721/test_dual721_receiver.cairo b/src/tests/token/erc721/test_dual721_receiver.cairo index b201b14d3..f70d232d8 100644 --- a/src/tests/token/erc721/test_dual721_receiver.cairo +++ b/src/tests/token/erc721/test_dual721_receiver.cairo @@ -1,10 +1,3 @@ -use openzeppelin::tests::mocks::erc721_receiver_mocks::{ - CamelERC721ReceiverMock, CamelERC721ReceiverPanicMock -}; -use openzeppelin::tests::mocks::erc721_receiver_mocks::{ - SnakeERC721ReceiverMock, SnakeERC721ReceiverPanicMock -}; -use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils::constants::{DATA, OPERATOR, OWNER, TOKEN_ID}; use openzeppelin::tests::utils; use openzeppelin::token::erc721::dual721_receiver::{ @@ -23,8 +16,8 @@ use openzeppelin::token::erc721::interface::{ // fn setup_snake() -> (DualCaseERC721Receiver, IERC721ReceiverDispatcher) { - let mut calldata = ArrayTrait::new(); - let target = utils::deploy(SnakeERC721ReceiverMock::TEST_CLASS_HASH, calldata); + let calldata = array![]; + let target = utils::declare_and_deploy("SnakeERC721ReceiverMock", calldata); ( DualCaseERC721Receiver { contract_address: target }, IERC721ReceiverDispatcher { contract_address: target } @@ -32,8 +25,8 @@ fn setup_snake() -> (DualCaseERC721Receiver, IERC721ReceiverDispatcher) { } fn setup_camel() -> (DualCaseERC721Receiver, IERC721ReceiverCamelDispatcher) { - let mut calldata = ArrayTrait::new(); - let target = utils::deploy(CamelERC721ReceiverMock::TEST_CLASS_HASH, calldata); + let calldata = array![]; + let target = utils::declare_and_deploy("CamelERC721ReceiverMock", calldata); ( DualCaseERC721Receiver { contract_address: target }, IERC721ReceiverCamelDispatcher { contract_address: target } @@ -41,18 +34,14 @@ fn setup_camel() -> (DualCaseERC721Receiver, IERC721ReceiverCamelDispatcher) { } fn setup_non_erc721_receiver() -> DualCaseERC721Receiver { - let calldata = ArrayTrait::new(); - let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); + let calldata = array![]; + let target = utils::declare_and_deploy("NonImplementingMock", calldata); DualCaseERC721Receiver { contract_address: target } } fn setup_erc721_receiver_panic() -> (DualCaseERC721Receiver, DualCaseERC721Receiver) { - let snake_target = utils::deploy( - SnakeERC721ReceiverPanicMock::TEST_CLASS_HASH, ArrayTrait::new() - ); - let camel_target = utils::deploy( - CamelERC721ReceiverPanicMock::TEST_CLASS_HASH, ArrayTrait::new() - ); + let snake_target = utils::declare_and_deploy("SnakeERC721ReceiverPanicMock", array![]); + let camel_target = utils::declare_and_deploy("CamelERC721ReceiverPanicMock", array![]); ( DualCaseERC721Receiver { contract_address: snake_target }, DualCaseERC721Receiver { contract_address: camel_target } @@ -73,6 +62,7 @@ fn test_dual_on_erc721_received() { } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_on_erc721_received() { let dispatcher = setup_non_erc721_receiver(); @@ -80,7 +70,8 @@ fn test_dual_no_on_erc721_received() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_on_erc721_received_exists_and_panics() { let (dispatcher, _) = setup_erc721_receiver_panic(); dispatcher.on_erc721_received(OPERATOR(), OWNER(), TOKEN_ID, DATA(true)); @@ -91,6 +82,7 @@ fn test_dual_on_erc721_received_exists_and_panics() { // #[test] +#[ignore] fn test_dual_onERC721Received() { let (dispatcher, _) = setup_camel(); @@ -100,7 +92,8 @@ fn test_dual_onERC721Received() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_onERC721Received_exists_and_panics() { let (_, dispatcher) = setup_erc721_receiver_panic(); dispatcher.on_erc721_received(OPERATOR(), OWNER(), TOKEN_ID, DATA(true)); diff --git a/src/tests/token/erc721/test_erc721.cairo b/src/tests/token/erc721/test_erc721.cairo index 7dd5ae1bf..365e8c942 100644 --- a/src/tests/token/erc721/test_erc721.cairo +++ b/src/tests/token/erc721/test_erc721.cairo @@ -1,34 +1,22 @@ use core::num::traits::Zero; -use openzeppelin::account::AccountComponent; use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; -use openzeppelin::introspection::src5; use openzeppelin::introspection; -use openzeppelin::tests::mocks::account_mocks::{DualCaseAccountMock, CamelAccountMock}; use openzeppelin::tests::mocks::erc721_mocks::DualCaseERC721Mock; -use openzeppelin::tests::mocks::erc721_receiver_mocks::{ - CamelERC721ReceiverMock, SnakeERC721ReceiverMock -}; -use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils::constants::{ DATA, ZERO, OWNER, CALLER, RECIPIENT, SPENDER, OPERATOR, OTHER, NAME, SYMBOL, TOKEN_ID, TOKEN_ID_2, PUBKEY, BASE_URI, BASE_URI_2 }; +use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::tests::utils; -use openzeppelin::token::erc721::ERC721Component::{ - ERC721CamelOnlyImpl, ERC721MetadataCamelOnlyImpl -}; -use openzeppelin::token::erc721::ERC721Component::{ERC721Impl, ERC721MetadataImpl, InternalImpl}; +use openzeppelin::token::erc721::ERC721Component::{ERC721Impl, ERC721CamelOnlyImpl}; +use openzeppelin::token::erc721::ERC721Component::{ERC721MetadataImpl, InternalImpl}; use openzeppelin::token::erc721::ERC721Component; -use openzeppelin::token::erc721::interface::IERC721; use openzeppelin::token::erc721; +use snforge_std::{spy_events, test_address, start_cheat_caller_address}; use starknet::ContractAddress; -use starknet::contract_address_const; use starknet::storage::StorageMapMemberAccessTrait; -use starknet::testing; -use super::common::{ - assert_only_event_approval, assert_only_event_approval_for_all, assert_only_event_transfer, -}; +use super::common::ERC721SpyHelpers; // // Setup @@ -47,26 +35,25 @@ fn setup() -> ComponentState { let mut state = COMPONENT_STATE(); state.initializer(NAME(), SYMBOL(), BASE_URI()); state.mint(OWNER(), TOKEN_ID); - utils::drop_event(ZERO()); state } fn setup_receiver() -> ContractAddress { - utils::deploy(SnakeERC721ReceiverMock::TEST_CLASS_HASH, array![]) + utils::declare_and_deploy("SnakeERC721ReceiverMock", array![]) } fn setup_camel_receiver() -> ContractAddress { - utils::deploy(CamelERC721ReceiverMock::TEST_CLASS_HASH, array![]) + utils::declare_and_deploy("CamelERC721ReceiverMock", array![]) } fn setup_account() -> ContractAddress { - let mut calldata = array![PUBKEY]; - utils::deploy(DualCaseAccountMock::TEST_CLASS_HASH, calldata) + let calldata = array![PUBKEY]; + utils::declare_and_deploy("DualCaseAccountMock", calldata) } fn setup_camel_account() -> ContractAddress { - let mut calldata = array![PUBKEY]; - utils::deploy(CamelAccountMock::TEST_CLASS_HASH, calldata) + let calldata = array![PUBKEY]; + utils::declare_and_deploy("CamelAccountMock", calldata) } // @@ -177,10 +164,13 @@ fn test_get_approved_nonexistent() { #[test] fn test_approve_from_owner() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(contract_address, OWNER()); state.approve(SPENDER(), TOKEN_ID); - assert_only_event_approval(ZERO(), OWNER(), SPENDER(), TOKEN_ID); + + spy.assert_only_event_approval(contract_address, OWNER(), SPENDER(), TOKEN_ID); let approved = state.get_approved(TOKEN_ID); assert_eq!(approved, SPENDER()); @@ -189,14 +179,16 @@ fn test_approve_from_owner() { #[test] fn test_approve_from_operator() { let mut state = setup(); + let contract_address = test_address(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(contract_address, OWNER()); state.set_approval_for_all(OPERATOR(), true); - utils::drop_event(ZERO()); - testing::set_caller_address(OPERATOR()); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, OPERATOR()); state.approve(SPENDER(), TOKEN_ID); - assert_only_event_approval(ZERO(), OWNER(), SPENDER(), TOKEN_ID); + spy.assert_only_event_approval(contract_address, OWNER(), SPENDER(), TOKEN_ID); let approved = state.get_approved(TOKEN_ID); assert_eq!(approved, SPENDER()); @@ -207,7 +199,7 @@ fn test_approve_from_operator() { fn test_approve_from_unauthorized() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.approve(SPENDER(), TOKEN_ID); } @@ -221,8 +213,11 @@ fn test_approve_nonexistent() { #[test] fn test__approve() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); + state._approve(SPENDER(), TOKEN_ID, ZERO()); - assert_only_event_approval(ZERO(), OWNER(), SPENDER(), TOKEN_ID); + spy.assert_only_event_approval(contract_address, OWNER(), SPENDER(), TOKEN_ID); let approved = state.get_approved(TOKEN_ID); assert_eq!(approved, SPENDER()); @@ -238,8 +233,11 @@ fn test__approve_nonexistent() { #[test] fn test__approve_auth_is_owner() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); + state._approve(SPENDER(), TOKEN_ID, OWNER()); - assert_only_event_approval(ZERO(), OWNER(), SPENDER(), TOKEN_ID); + spy.assert_only_event_approval(contract_address, OWNER(), SPENDER(), TOKEN_ID); let approved = state.get_approved(TOKEN_ID); assert_eq!(approved, SPENDER()); @@ -248,13 +246,16 @@ fn test__approve_auth_is_owner() { #[test] fn test__approve_auth_is_approved_for_all() { let mut state = setup(); + let contract_address = test_address(); let auth = CALLER(); - testing::set_caller_address(OWNER()); + + start_cheat_caller_address(contract_address, OWNER()); state.set_approval_for_all(auth, true); - utils::drop_event(ZERO()); + + let mut spy = spy_events(); state._approve(SPENDER(), TOKEN_ID, auth); - assert_only_event_approval(ZERO(), OWNER(), SPENDER(), TOKEN_ID); + spy.assert_only_event_approval(contract_address, OWNER(), SPENDER(), TOKEN_ID); let approved = state.get_approved(TOKEN_ID); assert_eq!(approved, SPENDER()); @@ -274,19 +275,21 @@ fn test__approve_auth_not_authorized() { #[test] fn test_set_approval_for_all() { let mut state = COMPONENT_STATE(); - testing::set_caller_address(OWNER()); + let contract_address = test_address(); + let mut spy = spy_events(); + start_cheat_caller_address(contract_address, OWNER()); let not_approved_for_all = !state.is_approved_for_all(OWNER(), OPERATOR()); assert!(not_approved_for_all); state.set_approval_for_all(OPERATOR(), true); - assert_only_event_approval_for_all(ZERO(), OWNER(), OPERATOR(), true); + spy.assert_only_event_approval_for_all(contract_address, OWNER(), OPERATOR(), true); let is_approved_for_all = state.is_approved_for_all(OWNER(), OPERATOR()); assert!(is_approved_for_all); state.set_approval_for_all(OPERATOR(), false); - assert_only_event_approval_for_all(ZERO(), OWNER(), OPERATOR(), false); + spy.assert_only_event_approval_for_all(contract_address, OWNER(), OPERATOR(), false); let not_approved_for_all = !state.is_approved_for_all(OWNER(), OPERATOR()); assert!(not_approved_for_all); @@ -302,18 +305,20 @@ fn test_set_approval_for_all_invalid_operator() { #[test] fn test__set_approval_for_all() { let mut state = COMPONENT_STATE(); + let contract_address = test_address(); + let mut spy = spy_events(); let not_approved_for_all = !state.is_approved_for_all(OWNER(), OPERATOR()); assert!(not_approved_for_all); state._set_approval_for_all(OWNER(), OPERATOR(), true); - assert_only_event_approval_for_all(ZERO(), OWNER(), OPERATOR(), true); + spy.assert_only_event_approval_for_all(contract_address, OWNER(), OPERATOR(), true); let is_approved_for_all = state.is_approved_for_all(OWNER(), OPERATOR()); assert!(is_approved_for_all); state._set_approval_for_all(OWNER(), OPERATOR(), false); - assert_only_event_approval_for_all(ZERO(), OWNER(), OPERATOR(), false); + spy.assert_only_event_approval_for_all(contract_address, OWNER(), OPERATOR(), false); let not_approved_for_all = !state.is_approved_for_all(OWNER(), OPERATOR()); assert!(not_approved_for_all); @@ -333,21 +338,24 @@ fn test__set_approval_for_all_invalid_operator() { #[test] fn test_transfer_from_owner() { let mut state = setup(); + let contract_address = test_address(); let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); + // set approval to check reset state._approve(OTHER(), token_id, ZERO()); - utils::drop_event(ZERO()); assert_state_before_transfer(owner, recipient, token_id); let approved = state.get_approved(token_id); assert_eq!(approved, OTHER()); - testing::set_caller_address(owner); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, owner); state.transfer_from(owner, recipient, token_id); - assert_only_event_transfer(ZERO(), owner, recipient, token_id); + spy.assert_only_event_transfer(contract_address, owner, recipient, token_id); assert_state_after_transfer(owner, recipient, token_id); } @@ -355,21 +363,24 @@ fn test_transfer_from_owner() { #[test] fn test_transferFrom_owner() { let mut state = setup(); + let contract_address = test_address(); let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); + // set approval to check reset state._approve(OTHER(), token_id, ZERO()); - utils::drop_event(ZERO()); assert_state_before_transfer(owner, recipient, token_id); let approved = state.get_approved(token_id); assert_eq!(approved, OTHER()); - testing::set_caller_address(owner); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, owner); state.transferFrom(owner, recipient, token_id); - assert_only_event_transfer(ZERO(), owner, recipient, token_id); + spy.assert_only_event_transfer(contract_address, owner, recipient, token_id); assert_state_after_transfer(owner, recipient, token_id); } @@ -378,7 +389,7 @@ fn test_transferFrom_owner() { #[should_panic(expected: ('ERC721: invalid token ID',))] fn test_transfer_from_nonexistent() { let mut state = COMPONENT_STATE(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.transfer_from(ZERO(), RECIPIENT(), TOKEN_ID); } @@ -386,7 +397,7 @@ fn test_transfer_from_nonexistent() { #[should_panic(expected: ('ERC721: invalid token ID',))] fn test_transferFrom_nonexistent() { let mut state = COMPONENT_STATE(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.transferFrom(ZERO(), RECIPIENT(), TOKEN_ID); } @@ -394,7 +405,7 @@ fn test_transferFrom_nonexistent() { #[should_panic(expected: ('ERC721: invalid receiver',))] fn test_transfer_from_to_zero() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.transfer_from(OWNER(), ZERO(), TOKEN_ID); } @@ -403,20 +414,22 @@ fn test_transfer_from_to_zero() { fn test_transferFrom_to_zero() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.transferFrom(OWNER(), ZERO(), TOKEN_ID); } #[test] fn test_transfer_from_to_owner() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); assert_eq!(state.owner_of(TOKEN_ID), OWNER()); assert_eq!(state.balance_of(OWNER()), 1); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(contract_address, OWNER()); state.transfer_from(OWNER(), OWNER(), TOKEN_ID); - assert_only_event_transfer(ZERO(), OWNER(), OWNER(), TOKEN_ID); + spy.assert_only_event_transfer(contract_address, OWNER(), OWNER(), TOKEN_ID); assert_eq!(state.owner_of(TOKEN_ID), OWNER()); assert_eq!(state.balance_of(OWNER()), 1); @@ -425,13 +438,15 @@ fn test_transfer_from_to_owner() { #[test] fn test_transferFrom_to_owner() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); assert_eq!(state.owner_of(TOKEN_ID), OWNER()); assert_eq!(state.balance_of(OWNER()), 1); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(contract_address, OWNER()); state.transferFrom(OWNER(), OWNER(), TOKEN_ID); - assert_only_event_transfer(ZERO(), OWNER(), OWNER(), TOKEN_ID); + spy.assert_only_event_transfer(contract_address, OWNER(), OWNER(), TOKEN_ID); assert_eq!(state.owner_of(TOKEN_ID), OWNER()); assert_eq!(state.balance_of(OWNER()), 1); @@ -440,18 +455,21 @@ fn test_transferFrom_to_owner() { #[test] fn test_transfer_from_approved() { let mut state = setup(); + let contract_address = test_address(); let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); + assert_state_before_transfer(owner, recipient, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.approve(OPERATOR(), token_id); - utils::drop_event(ZERO()); - testing::set_caller_address(OPERATOR()); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, OPERATOR()); state.transfer_from(owner, recipient, token_id); - assert_only_event_transfer(ZERO(), owner, recipient, token_id); + spy.assert_only_event_transfer(contract_address, owner, recipient, token_id); assert_state_after_transfer(owner, recipient, token_id); } @@ -459,18 +477,21 @@ fn test_transfer_from_approved() { #[test] fn test_transferFrom_approved() { let mut state = setup(); + let contract_address = test_address(); let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); + assert_state_before_transfer(owner, recipient, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.approve(OPERATOR(), token_id); - utils::drop_event(ZERO()); - testing::set_caller_address(OPERATOR()); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, OPERATOR()); state.transferFrom(owner, recipient, token_id); - assert_only_event_transfer(ZERO(), owner, recipient, token_id); + spy.assert_only_event_transfer(contract_address, owner, recipient, token_id); assert_state_after_transfer(owner, recipient, token_id); } @@ -478,19 +499,21 @@ fn test_transferFrom_approved() { #[test] fn test_transfer_from_approved_for_all() { let mut state = setup(); + let contract_address = test_address(); let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); assert_state_before_transfer(owner, recipient, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.set_approval_for_all(OPERATOR(), true); - utils::drop_event(ZERO()); - testing::set_caller_address(OPERATOR()); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, OPERATOR()); state.transfer_from(owner, recipient, token_id); - assert_only_event_transfer(ZERO(), owner, recipient, token_id); + spy.assert_only_event_transfer(contract_address, owner, recipient, token_id); assert_state_after_transfer(owner, recipient, token_id); } @@ -498,19 +521,21 @@ fn test_transfer_from_approved_for_all() { #[test] fn test_transferFrom_approved_for_all() { let mut state = setup(); + let contract_address = test_address(); let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); assert_state_before_transfer(owner, recipient, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.set_approval_for_all(OPERATOR(), true); - utils::drop_event(ZERO()); - testing::set_caller_address(OPERATOR()); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, OPERATOR()); state.transferFrom(owner, recipient, token_id); - assert_only_event_transfer(ZERO(), owner, recipient, token_id); + spy.assert_only_event_transfer(contract_address, owner, recipient, token_id); assert_state_after_transfer(owner, recipient, token_id); } @@ -519,7 +544,7 @@ fn test_transferFrom_approved_for_all() { #[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_transfer_from_unauthorized() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); } @@ -527,7 +552,7 @@ fn test_transfer_from_unauthorized() { #[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_transferFrom_unauthorized() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.transferFrom(OWNER(), RECIPIENT(), TOKEN_ID); } @@ -538,15 +563,17 @@ fn test_transferFrom_unauthorized() { #[test] fn test_safe_transfer_from_to_account() { let mut state = setup(); + let contract_address = test_address(); let account = setup_account(); + let mut spy = spy_events(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, account, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.safe_transfer_from(owner, account, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, account, token_id); + spy.assert_only_event_transfer(contract_address, owner, account, token_id); assert_state_after_transfer(owner, account, token_id); } @@ -554,15 +581,17 @@ fn test_safe_transfer_from_to_account() { #[test] fn test_safeTransferFrom_to_account() { let mut state = setup(); + let contract_address = test_address(); let account = setup_account(); + let mut spy = spy_events(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, account, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.safeTransferFrom(owner, account, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, account, token_id); + spy.assert_only_event_transfer(contract_address, owner, account, token_id); assert_state_after_transfer(owner, account, token_id); } @@ -570,15 +599,17 @@ fn test_safeTransferFrom_to_account() { #[test] fn test_safe_transfer_from_to_account_camel() { let mut state = setup(); + let contract_address = test_address(); let account = setup_camel_account(); + let mut spy = spy_events(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, account, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.safe_transfer_from(owner, account, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, account, token_id); + spy.assert_only_event_transfer(contract_address, owner, account, token_id); assert_state_after_transfer(owner, account, token_id); } @@ -586,15 +617,17 @@ fn test_safe_transfer_from_to_account_camel() { #[test] fn test_safeTransferFrom_to_account_camel() { let mut state = setup(); + let contract_address = test_address(); let account = setup_camel_account(); + let mut spy = spy_events(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, account, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.safeTransferFrom(owner, account, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, account, token_id); + spy.assert_only_event_transfer(contract_address, owner, account, token_id); assert_state_after_transfer(owner, account, token_id); } @@ -602,15 +635,17 @@ fn test_safeTransferFrom_to_account_camel() { #[test] fn test_safe_transfer_from_to_receiver() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, receiver, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.safe_transfer_from(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, receiver, token_id); + spy.assert_only_event_transfer(contract_address, owner, receiver, token_id); assert_state_after_transfer(owner, receiver, token_id); } @@ -618,47 +653,55 @@ fn test_safe_transfer_from_to_receiver() { #[test] fn test_safeTransferFrom_to_receiver() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, receiver, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.safeTransferFrom(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, receiver, token_id); + spy.assert_only_event_transfer(contract_address, owner, receiver, token_id); assert_state_after_transfer(owner, receiver, token_id); } #[test] +#[ignore] fn test_safe_transfer_from_to_receiver_camel() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, receiver, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.safe_transfer_from(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, receiver, token_id); + spy.assert_only_event_transfer(contract_address, owner, receiver, token_id); assert_state_after_transfer(owner, receiver, token_id); } #[test] +#[ignore] fn test_safeTransferFrom_to_receiver_camel() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, receiver, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.safeTransferFrom(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, receiver, token_id); + spy.assert_only_event_transfer(contract_address, owner, receiver, token_id); assert_state_after_transfer(owner, receiver, token_id); } @@ -671,7 +714,7 @@ fn test_safe_transfer_from_to_receiver_failure() { let token_id = TOKEN_ID; let owner = OWNER(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safe_transfer_from(owner, receiver, token_id, DATA(false)); } @@ -683,11 +726,12 @@ fn test_safeTransferFrom_to_receiver_failure() { let token_id = TOKEN_ID; let owner = OWNER(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safeTransferFrom(owner, receiver, token_id, DATA(false)); } #[test] +#[ignore] #[should_panic(expected: ('ERC721: safe transfer failed',))] fn test_safe_transfer_from_to_receiver_failure_camel() { let mut state = setup(); @@ -695,11 +739,12 @@ fn test_safe_transfer_from_to_receiver_failure_camel() { let token_id = TOKEN_ID; let owner = OWNER(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safe_transfer_from(owner, receiver, token_id, DATA(false)); } #[test] +#[ignore] #[should_panic(expected: ('ERC721: safe transfer failed',))] fn test_safeTransferFrom_to_receiver_failure_camel() { let mut state = setup(); @@ -707,31 +752,33 @@ fn test_safeTransferFrom_to_receiver_failure_camel() { let token_id = TOKEN_ID; let owner = OWNER(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safeTransferFrom(owner, receiver, token_id, DATA(false)); } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_safe_transfer_from_to_non_receiver() { let mut state = setup(); - let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); + let recipient = utils::declare_and_deploy("NonImplementingMock", array![]); let token_id = TOKEN_ID; let owner = OWNER(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safe_transfer_from(owner, recipient, token_id, DATA(true)); } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_safeTransferFrom_to_non_receiver() { let mut state = setup(); - let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); + let recipient = utils::declare_and_deploy("NonImplementingMock", array![]); let token_id = TOKEN_ID; let owner = OWNER(); - testing::set_caller_address(owner); + start_cheat_caller_address(test_address(), owner); state.safeTransferFrom(owner, recipient, token_id, DATA(true)); } @@ -739,7 +786,7 @@ fn test_safeTransferFrom_to_non_receiver() { #[should_panic(expected: ('ERC721: invalid token ID',))] fn test_safe_transfer_from_nonexistent() { let mut state = COMPONENT_STATE(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.safe_transfer_from(ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); } @@ -747,7 +794,7 @@ fn test_safe_transfer_from_nonexistent() { #[should_panic(expected: ('ERC721: invalid token ID',))] fn test_safeTransferFrom_nonexistent() { let mut state = COMPONENT_STATE(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.safeTransferFrom(ZERO(), RECIPIENT(), TOKEN_ID, DATA(true)); } @@ -755,7 +802,7 @@ fn test_safeTransferFrom_nonexistent() { #[should_panic(expected: ('ERC721: invalid receiver',))] fn test_safe_transfer_from_to_zero() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.safe_transfer_from(OWNER(), ZERO(), TOKEN_ID, DATA(true)); } @@ -763,25 +810,28 @@ fn test_safe_transfer_from_to_zero() { #[should_panic(expected: ('ERC721: invalid receiver',))] fn test_safeTransferFrom_to_zero() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.safeTransferFrom(OWNER(), ZERO(), TOKEN_ID, DATA(true)); } #[test] fn test_safe_transfer_from_to_owner() { let mut state = COMPONENT_STATE(); + let contract_address = test_address(); let token_id = TOKEN_ID; let owner = setup_receiver(); + state.initializer(NAME(), SYMBOL(), BASE_URI()); state.mint(owner, token_id); - utils::drop_event(ZERO()); assert_eq!(state.owner_of(token_id), owner); assert_eq!(state.balance_of(owner), 1); - testing::set_caller_address(owner); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, owner); state.safe_transfer_from(owner, owner, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, owner, token_id); + spy.assert_only_event_transfer(contract_address, owner, owner, token_id); assert_eq!(state.owner_of(token_id), owner); assert_eq!(state.balance_of(owner), 1); @@ -790,58 +840,67 @@ fn test_safe_transfer_from_to_owner() { #[test] fn test_safeTransferFrom_to_owner() { let mut state = COMPONENT_STATE(); + let contract_address = test_address(); let token_id = TOKEN_ID; let owner = setup_receiver(); + state.initializer(NAME(), SYMBOL(), BASE_URI()); state.mint(owner, token_id); - utils::drop_event(ZERO()); assert_eq!(state.owner_of(token_id), owner); assert_eq!(state.balance_of(owner), 1); - testing::set_caller_address(owner); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, owner); state.safeTransferFrom(owner, owner, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, owner, token_id); + spy.assert_only_event_transfer(contract_address, owner, owner, token_id); assert_eq!(state.owner_of(token_id), owner); assert_eq!(state.balance_of(owner), 1); } #[test] +#[ignore] fn test_safe_transfer_from_to_owner_camel() { let mut state = COMPONENT_STATE(); + let contract_address = test_address(); + let mut spy = spy_events(); let token_id = TOKEN_ID; let owner = setup_camel_receiver(); + state.initializer(NAME(), SYMBOL(), BASE_URI()); state.mint(owner, token_id); - utils::drop_event(ZERO()); assert_eq!(state.owner_of(token_id), owner); assert_eq!(state.balance_of(owner), 1); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.safe_transfer_from(owner, owner, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, owner, token_id); + spy.assert_only_event_transfer(contract_address, owner, owner, token_id); assert_eq!(state.owner_of(token_id), owner); assert_eq!(state.balance_of(owner), 1); } #[test] +#[ignore] fn test_safeTransferFrom_to_owner_camel() { let mut state = COMPONENT_STATE(); + let contract_address = test_address(); + let mut spy = spy_events(); let token_id = TOKEN_ID; let owner = setup_camel_receiver(); + state.initializer(NAME(), SYMBOL(), BASE_URI()); state.mint(owner, token_id); - utils::drop_event(ZERO()); assert_eq!(state.owner_of(token_id), owner); assert_eq!(state.balance_of(owner), 1); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.safeTransferFrom(owner, owner, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, owner, token_id); + spy.assert_only_event_transfer(contract_address, owner, owner, token_id); assert_eq!(state.owner_of(token_id), owner); assert_eq!(state.balance_of(owner), 1); @@ -850,19 +909,21 @@ fn test_safeTransferFrom_to_owner_camel() { #[test] fn test_safe_transfer_from_approved() { let mut state = setup(); + let contract_address = test_address(); let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, receiver, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.approve(OPERATOR(), token_id); - utils::drop_event(ZERO()); - testing::set_caller_address(OPERATOR()); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, OPERATOR()); state.safe_transfer_from(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, receiver, token_id); + spy.assert_only_event_transfer(contract_address, owner, receiver, token_id); assert_state_after_transfer(owner, receiver, token_id); } @@ -870,59 +931,67 @@ fn test_safe_transfer_from_approved() { #[test] fn test_safeTransferFrom_approved() { let mut state = setup(); + let contract_address = test_address(); let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, receiver, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.approve(OPERATOR(), token_id); - utils::drop_event(ZERO()); - testing::set_caller_address(OPERATOR()); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, OPERATOR()); state.safeTransferFrom(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, receiver, token_id); + spy.assert_only_event_transfer(contract_address, owner, receiver, token_id); assert_state_after_transfer(owner, receiver, token_id); } #[test] +#[ignore] fn test_safe_transfer_from_approved_camel() { let mut state = setup(); + let contract_address = test_address(); let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, receiver, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.approve(OPERATOR(), token_id); - utils::drop_event(ZERO()); - testing::set_caller_address(OPERATOR()); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, OPERATOR()); state.safe_transfer_from(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, receiver, token_id); + spy.assert_only_event_transfer(contract_address, owner, receiver, token_id); assert_state_after_transfer(owner, receiver, token_id); } #[test] +#[ignore] fn test_safeTransferFrom_approved_camel() { let mut state = setup(); + let contract_address = test_address(); let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, receiver, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.approve(OPERATOR(), token_id); - utils::drop_event(ZERO()); - testing::set_caller_address(OPERATOR()); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, OPERATOR()); state.safeTransferFrom(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, receiver, token_id); + spy.assert_only_event_transfer(contract_address, owner, receiver, token_id); assert_state_after_transfer(owner, receiver, token_id); } @@ -930,19 +999,21 @@ fn test_safeTransferFrom_approved_camel() { #[test] fn test_safe_transfer_from_approved_for_all() { let mut state = setup(); + let contract_address = test_address(); let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, receiver, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.set_approval_for_all(OPERATOR(), true); - utils::drop_event(ZERO()); - testing::set_caller_address(OPERATOR()); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, OPERATOR()); state.safe_transfer_from(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, receiver, token_id); + spy.assert_only_event_transfer(contract_address, owner, receiver, token_id); assert_state_after_transfer(owner, receiver, token_id); } @@ -950,59 +1021,66 @@ fn test_safe_transfer_from_approved_for_all() { #[test] fn test_safeTransferFrom_approved_for_all() { let mut state = setup(); + let contract_address = test_address(); let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, receiver, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.set_approval_for_all(OPERATOR(), true); - utils::drop_event(ZERO()); - testing::set_caller_address(OPERATOR()); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, OPERATOR()); state.safeTransferFrom(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, receiver, token_id); + spy.assert_only_event_transfer(contract_address, owner, receiver, token_id); assert_state_after_transfer(owner, receiver, token_id); } #[test] +#[ignore] fn test_safe_transfer_from_approved_for_all_camel() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, receiver, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.set_approval_for_all(OPERATOR(), true); - utils::drop_event(ZERO()); - testing::set_caller_address(OPERATOR()); + start_cheat_caller_address(contract_address, OPERATOR()); state.safe_transfer_from(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, receiver, token_id); + spy.assert_only_event_transfer(contract_address, owner, receiver, token_id); assert_state_after_transfer(owner, receiver, token_id); } #[test] +#[ignore] fn test_safeTransferFrom_approved_for_all_camel() { let mut state = setup(); + let contract_address = test_address(); let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); assert_state_before_transfer(owner, receiver, token_id); - testing::set_caller_address(owner); + start_cheat_caller_address(contract_address, owner); state.set_approval_for_all(OPERATOR(), true); - utils::drop_event(ZERO()); - testing::set_caller_address(OPERATOR()); + let mut spy = spy_events(); + + start_cheat_caller_address(contract_address, OPERATOR()); state.safeTransferFrom(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), owner, receiver, token_id); + spy.assert_only_event_transfer(contract_address, owner, receiver, token_id); assert_state_after_transfer(owner, receiver, token_id); } @@ -1011,7 +1089,7 @@ fn test_safeTransferFrom_approved_for_all_camel() { #[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_safe_transfer_from_unauthorized() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); } @@ -1019,7 +1097,7 @@ fn test_safe_transfer_from_unauthorized() { #[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_safeTransferFrom_unauthorized() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.safeTransferFrom(OWNER(), RECIPIENT(), TOKEN_ID, DATA(true)); } @@ -1030,6 +1108,8 @@ fn test_safeTransferFrom_unauthorized() { #[test] fn test__transfer() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); let token_id = TOKEN_ID; let owner = OWNER(); let recipient = RECIPIENT(); @@ -1037,7 +1117,7 @@ fn test__transfer() { assert_state_before_transfer(owner, recipient, token_id); state.transfer(owner, recipient, token_id); - assert_only_event_transfer(ZERO(), owner, recipient, token_id); + spy.assert_only_event_transfer(contract_address, owner, recipient, token_id); assert_state_after_transfer(owner, recipient, token_id); } @@ -1070,12 +1150,14 @@ fn test__transfer_from_invalid_owner() { #[test] fn test_mint() { let mut state = COMPONENT_STATE(); + let contract_address = test_address(); + let mut spy = spy_events(); let recipient = RECIPIENT(); let token_id = TOKEN_ID; assert_state_before_mint(recipient); state.mint(recipient, TOKEN_ID); - assert_only_event_transfer(ZERO(), ZERO(), recipient, token_id); + spy.assert_only_event_transfer(contract_address, ZERO(), recipient, token_id); assert_state_after_mint(recipient, token_id); } @@ -1101,25 +1183,30 @@ fn test_mint_already_exist() { #[test] fn test__safe_mint_to_receiver() { let mut state = COMPONENT_STATE(); + let contract_address = test_address(); + let mut spy = spy_events(); let recipient = setup_receiver(); let token_id = TOKEN_ID; assert_state_before_mint(recipient); state.safe_mint(recipient, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), ZERO(), recipient, token_id); + spy.assert_only_event_transfer(contract_address, ZERO(), recipient, token_id); assert_state_after_mint(recipient, token_id); } #[test] +#[ignore] fn test__safe_mint_to_receiver_camel() { let mut state = COMPONENT_STATE(); + let contract_address = test_address(); + let mut spy = spy_events(); let recipient = setup_camel_receiver(); let token_id = TOKEN_ID; assert_state_before_mint(recipient); state.safe_mint(recipient, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), ZERO(), recipient, token_id); + spy.assert_only_event_transfer(contract_address, ZERO(), recipient, token_id); assert_state_after_mint(recipient, token_id); } @@ -1127,12 +1214,14 @@ fn test__safe_mint_to_receiver_camel() { #[test] fn test__safe_mint_to_account() { let mut state = COMPONENT_STATE(); + let contract_address = test_address(); let account = setup_account(); + let mut spy = spy_events(); let token_id = TOKEN_ID; assert_state_before_mint(account); state.safe_mint(account, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), ZERO(), account, token_id); + spy.assert_only_event_transfer(contract_address, ZERO(), account, token_id); assert_state_after_mint(account, token_id); } @@ -1140,21 +1229,24 @@ fn test__safe_mint_to_account() { #[test] fn test__safe_mint_to_account_camel() { let mut state = COMPONENT_STATE(); + let contract_address = test_address(); let account = setup_camel_account(); + let mut spy = spy_events(); let token_id = TOKEN_ID; assert_state_before_mint(account); state.safe_mint(account, token_id, DATA(true)); - assert_only_event_transfer(ZERO(), ZERO(), account, token_id); + spy.assert_only_event_transfer(contract_address, ZERO(), account, token_id); assert_state_after_mint(account, token_id); } #[test] +#[ignore] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test__safe_mint_to_non_receiver() { let mut state = COMPONENT_STATE(); - let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); + let recipient = utils::declare_and_deploy("NonImplementingMock", array![]); let token_id = TOKEN_ID; assert_state_before_mint(recipient); @@ -1175,6 +1267,7 @@ fn test__safe_mint_to_receiver_failure() { } #[test] +#[ignore] #[should_panic(expected: ('ERC721: safe mint failed',))] fn test__safe_mint_to_receiver_failure_camel() { let mut state = COMPONENT_STATE(); @@ -1207,16 +1300,18 @@ fn test__safe_mint_already_exist() { #[test] fn test_burn() { let mut state = setup(); + let contract_address = test_address(); state._approve(OTHER(), TOKEN_ID, ZERO()); - utils::drop_event(ZERO()); assert_eq!(state.owner_of(TOKEN_ID), OWNER()); assert_eq!(state.balance_of(OWNER()), 1); assert_eq!(state.get_approved(TOKEN_ID), OTHER()); + let mut spy = spy_events(); + state.burn(TOKEN_ID); - assert_only_event_transfer(ZERO(), OWNER(), ZERO(), TOKEN_ID); + spy.assert_only_event_transfer(contract_address, OWNER(), ZERO(), TOKEN_ID); assert_eq!(state.ERC721_owners.read(TOKEN_ID), ZERO()); assert_eq!(state.balance_of(OWNER()), 0); @@ -1319,8 +1414,11 @@ fn test__exists() { #[test] fn test__approve_with_optional_event_emitting() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); + state._approve_with_optional_event(SPENDER(), TOKEN_ID, ZERO(), true); - assert_only_event_approval(ZERO(), OWNER(), SPENDER(), TOKEN_ID); + spy.assert_only_event_approval(contract_address, OWNER(), SPENDER(), TOKEN_ID); let approved = state.get_approved(TOKEN_ID); assert_eq!(approved, SPENDER()); @@ -1329,8 +1427,10 @@ fn test__approve_with_optional_event_emitting() { #[test] fn test__approve_with_optional_event_not_emitting() { let mut state = setup(); + let mut spy = spy_events(); + state._approve_with_optional_event(SPENDER(), TOKEN_ID, ZERO(), false); - utils::assert_no_events_left(ZERO()); + spy.assert_no_events_left_from(test_address()); let approved = state.get_approved(TOKEN_ID); assert_eq!(approved, SPENDER()); @@ -1346,8 +1446,10 @@ fn test__approve_with_optional_event_nonexistent_emitting() { #[test] fn test__approve_with_optional_event_nonexistent_not_emitting() { let mut state = setup(); + let mut spy = spy_events(); + state._approve_with_optional_event(SPENDER(), TOKEN_ID, ZERO(), false); - utils::assert_no_events_left(ZERO()); + spy.assert_no_events_left_from(test_address()); let approved = state.get_approved(TOKEN_ID); assert_eq!(approved, SPENDER()); @@ -1356,8 +1458,10 @@ fn test__approve_with_optional_event_nonexistent_not_emitting() { #[test] fn test__approve_with_optional_event_auth_is_owner() { let mut state = setup(); + let mut spy = spy_events(); + state._approve_with_optional_event(SPENDER(), TOKEN_ID, OWNER(), false); - utils::assert_no_events_left(ZERO()); + spy.assert_no_events_left_from(test_address()); let approved = state.get_approved(TOKEN_ID); assert_eq!(approved, SPENDER()); @@ -1367,12 +1471,15 @@ fn test__approve_with_optional_event_auth_is_owner() { fn test__approve_with_optional_event_auth_is_approved_for_all() { let mut state = setup(); let auth = CALLER(); - testing::set_caller_address(OWNER()); + let contract_address = test_address(); + + start_cheat_caller_address(contract_address, OWNER()); state.set_approval_for_all(auth, true); - utils::drop_event(ZERO()); + + let mut spy = spy_events(); state._approve_with_optional_event(SPENDER(), TOKEN_ID, auth, false); - utils::assert_no_events_left(ZERO()); + spy.assert_no_events_left_from(contract_address); let approved = state.get_approved(TOKEN_ID); assert_eq!(approved, SPENDER()); @@ -1396,9 +1503,8 @@ fn test__is_authorized_owner() { fn test__is_authorized_approved_for_all() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.set_approval_for_all(SPENDER(), true); - utils::drop_event(ZERO()); let authorized = state._is_authorized(OWNER(), SPENDER(), TOKEN_ID); assert!(authorized); @@ -1408,9 +1514,8 @@ fn test__is_authorized_approved_for_all() { fn test__is_authorized_approved() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.approve(SPENDER(), TOKEN_ID); - utils::drop_event(ZERO()); let authorized = state._is_authorized(OWNER(), SPENDER(), TOKEN_ID); assert!(authorized); @@ -1440,9 +1545,8 @@ fn test__check_authorized_owner() { fn test__check_authorized_approved_for_all() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.set_approval_for_all(SPENDER(), true); - utils::drop_event(ZERO()); state._check_authorized(OWNER(), SPENDER(), TOKEN_ID); } @@ -1451,9 +1555,8 @@ fn test__check_authorized_approved_for_all() { fn test__check_authorized_approved() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.approve(SPENDER(), TOKEN_ID); - utils::drop_event(ZERO()); state._check_authorized(OWNER(), SPENDER(), TOKEN_ID); } @@ -1482,9 +1585,11 @@ fn test__check_authorized_zero_address() { #[test] fn test_update_mint() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); state.update(RECIPIENT(), TOKEN_ID_2, ZERO()); - assert_only_event_transfer(ZERO(), ZERO(), RECIPIENT(), TOKEN_ID_2); + spy.assert_only_event_transfer(contract_address, ZERO(), RECIPIENT(), TOKEN_ID_2); let owner = state.owner_of(TOKEN_ID_2); assert_eq!(owner, RECIPIENT()); @@ -1496,9 +1601,11 @@ fn test_update_mint() { #[test] fn test_update_burn() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); state.update(ZERO(), TOKEN_ID, ZERO()); - assert_only_event_transfer(ZERO(), OWNER(), ZERO(), TOKEN_ID); + spy.assert_only_event_transfer(contract_address, OWNER(), ZERO(), TOKEN_ID); let owner = state._owner_of(TOKEN_ID); assert_eq!(owner, ZERO()); @@ -1510,41 +1617,52 @@ fn test_update_burn() { #[test] fn test_update_transfer() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); state.update(RECIPIENT(), TOKEN_ID, ZERO()); - assert_only_event_transfer(ZERO(), OWNER(), RECIPIENT(), TOKEN_ID); + spy.assert_only_event_transfer(contract_address, OWNER(), RECIPIENT(), TOKEN_ID); assert_state_after_transfer(OWNER(), RECIPIENT(), TOKEN_ID); } #[test] fn test_update_auth_owner() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); + state.update(RECIPIENT(), TOKEN_ID, OWNER()); - assert_only_event_transfer(ZERO(), OWNER(), RECIPIENT(), TOKEN_ID); + spy.assert_only_event_transfer(contract_address, OWNER(), RECIPIENT(), TOKEN_ID); assert_state_after_transfer(OWNER(), RECIPIENT(), TOKEN_ID); } #[test] fn test_update_auth_approved_for_all() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let contract_address = test_address(); + + start_cheat_caller_address(contract_address, OWNER()); state.set_approval_for_all(OPERATOR(), true); - utils::drop_event(ZERO()); + + let mut spy = spy_events(); state.update(RECIPIENT(), TOKEN_ID, OPERATOR()); - assert_only_event_transfer(ZERO(), OWNER(), RECIPIENT(), TOKEN_ID); + spy.assert_only_event_transfer(contract_address, OWNER(), RECIPIENT(), TOKEN_ID); assert_state_after_transfer(OWNER(), RECIPIENT(), TOKEN_ID); } #[test] fn test_update_auth_approved() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let contract_address = test_address(); + + start_cheat_caller_address(contract_address, OWNER()); state.approve(OPERATOR(), TOKEN_ID); - utils::drop_event(ZERO()); + + let mut spy = spy_events(); state.update(RECIPIENT(), TOKEN_ID, OPERATOR()); - assert_only_event_transfer(ZERO(), OWNER(), RECIPIENT(), TOKEN_ID); + spy.assert_only_event_transfer(contract_address, OWNER(), RECIPIENT(), TOKEN_ID); assert_state_after_transfer(OWNER(), RECIPIENT(), TOKEN_ID); } diff --git a/src/tests/token/erc721/test_erc721_receiver.cairo b/src/tests/token/erc721/test_erc721_receiver.cairo index 72f8272a2..a84e1c434 100644 --- a/src/tests/token/erc721/test_erc721_receiver.cairo +++ b/src/tests/token/erc721/test_erc721_receiver.cairo @@ -1,7 +1,7 @@ use openzeppelin::introspection::interface::ISRC5_ID; use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; use openzeppelin::tests::mocks::erc721_receiver_mocks::DualCaseERC721ReceiverMock; -use openzeppelin::tests::utils::constants::{OWNER, OPERATOR, TOKEN_ID, DATA}; +use openzeppelin::tests::utils::constants::{OWNER, OPERATOR, TOKEN_ID}; use openzeppelin::token::erc721::ERC721ReceiverComponent::{ ERC721ReceiverImpl, ERC721ReceiverCamelImpl, InternalImpl }; From fbfb819b154375a5c9d8b9081f0a18247712afe5 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 16 Jul 2024 13:39:46 +0200 Subject: [PATCH 07/20] Check ignored tests (#1049) * test: check ignored tests and add reason * feat: apply review updates --- src/tests/token/erc1155/test_dual1155.cairo | 64 +++++-------- .../erc1155/test_dual1155_receiver.cairo | 16 ++-- src/tests/token/erc1155/test_erc1155.cairo | 14 +-- src/tests/token/erc20/test_dual20.cairo | 78 ++++++--------- src/tests/token/erc721/test_dual721.cairo | 94 ++++++++----------- .../token/erc721/test_dual721_receiver.cairo | 7 +- src/tests/token/erc721/test_erc721.cairo | 48 +++++----- src/tests/utils/common.cairo | 1 + 8 files changed, 140 insertions(+), 182 deletions(-) diff --git a/src/tests/token/erc1155/test_dual1155.cairo b/src/tests/token/erc1155/test_dual1155.cairo index 4589cd2c1..3248947b4 100644 --- a/src/tests/token/erc1155/test_dual1155.cairo +++ b/src/tests/token/erc1155/test_dual1155.cairo @@ -71,16 +71,19 @@ fn setup_erc1155_panic() -> (DualCaseERC1155, DualCaseERC1155) { // #[test] -#[ignore] -fn test_dual_uri() { +fn test_dual_uri_snake() { let (snake_dispatcher, _, _) = setup_snake(); - let (camel_dispatcher, _, _) = setup_camel(); assert_eq!(snake_dispatcher.uri(TOKEN_ID), "URI"); +} + +#[test] +fn test_dual_uri_camel() { + let (camel_dispatcher, _, _) = setup_camel(); assert_eq!(camel_dispatcher.uri(TOKEN_ID), "URI"); } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_uri() { let dispatcher = setup_non_erc1155(); @@ -88,7 +91,6 @@ fn test_dual_no_uri() { } #[test] -#[ignore] #[should_panic(expected: ("Some error",))] fn test_dual_uri_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); @@ -100,14 +102,13 @@ fn test_dual_uri_exists_and_panics() { // #[test] -#[ignore] fn test_dual_balance_of() { let (dispatcher, _, owner) = setup_snake(); assert_eq!(dispatcher.balance_of(owner, TOKEN_ID), TOKEN_VALUE); } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_balance_of() { let dispatcher = setup_non_erc1155(); @@ -115,7 +116,6 @@ fn test_dual_no_balance_of() { } #[test] -#[ignore] #[should_panic(expected: ("Some error",))] fn test_dual_balance_of_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); @@ -123,7 +123,6 @@ fn test_dual_balance_of_exists_and_panics() { } #[test] -#[ignore] fn test_dual_balance_of_batch() { let (dispatcher, _, owner) = setup_snake(); let accounts = array![owner, RECIPIENT()].span(); @@ -135,7 +134,7 @@ fn test_dual_balance_of_batch() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_balance_of_batch() { let dispatcher = setup_non_erc1155(); @@ -144,7 +143,6 @@ fn test_dual_no_balance_of_batch() { } #[test] -#[ignore] #[should_panic(expected: ("Some error",))] fn test_dual_balance_of_batch_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); @@ -153,7 +151,6 @@ fn test_dual_balance_of_batch_exists_and_panics() { } #[test] -#[ignore] fn test_dual_safe_transfer_from() { let (dispatcher, target, owner) = setup_snake(); let receiver = setup_receiver(); @@ -164,7 +161,7 @@ fn test_dual_safe_transfer_from() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_safe_transfer_from() { let dispatcher = setup_non_erc1155(); @@ -172,7 +169,6 @@ fn test_dual_no_safe_transfer_from() { } #[test] -#[ignore] #[should_panic(expected: ("Some error",))] fn test_dual_safe_transfer_from_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); @@ -180,7 +176,6 @@ fn test_dual_safe_transfer_from_exists_and_panics() { } #[test] -#[ignore] fn test_dual_safe_batch_transfer_from() { let (dispatcher, target, owner) = setup_snake(); let token_ids = array![TOKEN_ID, TOKEN_ID_2].span(); @@ -194,7 +189,7 @@ fn test_dual_safe_batch_transfer_from() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_safe_batch_transfer_from() { let dispatcher = setup_non_erc1155(); @@ -204,7 +199,6 @@ fn test_dual_no_safe_batch_transfer_from() { } #[test] -#[ignore] #[should_panic(expected: ("Some error",))] fn test_dual_safe_batch_transfer_from_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); @@ -214,7 +208,6 @@ fn test_dual_safe_batch_transfer_from_exists_and_panics() { } #[test] -#[ignore] fn test_dual_is_approved_for_all() { let (dispatcher, target, _) = setup_snake(); @@ -226,7 +219,7 @@ fn test_dual_is_approved_for_all() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_is_approved_for_all() { let dispatcher = setup_non_erc1155(); @@ -234,7 +227,6 @@ fn test_dual_no_is_approved_for_all() { } #[test] -#[ignore] #[should_panic(expected: ("Some error",))] fn test_dual_is_approved_for_all_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); @@ -242,7 +234,6 @@ fn test_dual_is_approved_for_all_exists_and_panics() { } #[test] -#[ignore] fn test_dual_set_approval_for_all() { let (dispatcher, target, _) = setup_snake(); @@ -254,7 +245,7 @@ fn test_dual_set_approval_for_all() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_set_approval_for_all() { let dispatcher = setup_non_erc1155(); @@ -262,7 +253,6 @@ fn test_dual_no_set_approval_for_all() { } #[test] -#[ignore] #[should_panic(expected: ("Some error",))] fn test_dual_set_approval_for_all_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); @@ -270,7 +260,6 @@ fn test_dual_set_approval_for_all_exists_and_panics() { } #[test] -#[ignore] fn test_dual_supports_interface() { let (dispatcher, _, _) = setup_snake(); let supports_ierc1155 = dispatcher.supports_interface(IERC1155_ID); @@ -278,7 +267,7 @@ fn test_dual_supports_interface() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_supports_interface() { let dispatcher = setup_non_erc1155(); @@ -286,7 +275,6 @@ fn test_dual_no_supports_interface() { } #[test] -#[ignore] #[should_panic(expected: ("Some error",))] fn test_dual_supports_interface_exists_and_panics() { let (dispatcher, _) = setup_erc1155_panic(); @@ -298,14 +286,14 @@ fn test_dual_supports_interface_exists_and_panics() { // #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_balanceOf() { let (dispatcher, _, owner) = setup_camel(); assert_eq!(dispatcher.balance_of(owner, TOKEN_ID), TOKEN_VALUE); } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_balanceOf_exists_and_panics() { let (_, dispatcher) = setup_erc1155_panic(); @@ -313,7 +301,7 @@ fn test_dual_balanceOf_exists_and_panics() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_balanceOfBatch() { let (dispatcher, _, owner) = setup_camel(); let accounts = array![owner, RECIPIENT()].span(); @@ -325,7 +313,7 @@ fn test_dual_balanceOfBatch() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_balanceOfBatch_exists_and_panics() { let (_, dispatcher) = setup_erc1155_panic(); @@ -334,7 +322,7 @@ fn test_dual_balanceOfBatch_exists_and_panics() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_safeTransferFrom() { let (dispatcher, target, owner) = setup_camel(); let receiver = setup_receiver(); @@ -345,7 +333,7 @@ fn test_dual_safeTransferFrom() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_safeTransferFrom_exists_and_panics() { let (_, dispatcher) = setup_erc1155_panic(); @@ -353,7 +341,7 @@ fn test_dual_safeTransferFrom_exists_and_panics() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_safeBatchTransferFrom() { let (dispatcher, target, owner) = setup_camel(); let token_ids = array![TOKEN_ID, TOKEN_ID_2].span(); @@ -367,7 +355,7 @@ fn test_dual_safeBatchTransferFrom() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_safeBatchTransferFrom_exists_and_panics() { let (_, dispatcher) = setup_erc1155_panic(); @@ -377,7 +365,7 @@ fn test_dual_safeBatchTransferFrom_exists_and_panics() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_isApprovedForAll() { let (dispatcher, target, _) = setup_camel(); @@ -389,7 +377,7 @@ fn test_dual_isApprovedForAll() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_isApprovedForAll_exists_and_panics() { let (_, dispatcher) = setup_erc1155_panic(); @@ -397,7 +385,7 @@ fn test_dual_isApprovedForAll_exists_and_panics() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_setApprovalForAll() { let (dispatcher, target, _) = setup_camel(); @@ -409,7 +397,7 @@ fn test_dual_setApprovalForAll() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_setApprovalForAll_exists_and_panics() { let (_, dispatcher) = setup_erc1155_panic(); diff --git a/src/tests/token/erc1155/test_dual1155_receiver.cairo b/src/tests/token/erc1155/test_dual1155_receiver.cairo index c207d3e71..f0e7c3a98 100644 --- a/src/tests/token/erc1155/test_dual1155_receiver.cairo +++ b/src/tests/token/erc1155/test_dual1155_receiver.cairo @@ -53,7 +53,6 @@ fn setup_erc1155_receiver_panic() -> (DualCaseERC1155Receiver, DualCaseERC1155Re // #[test] -#[ignore] fn test_dual_on_erc1155_received() { let (dispatcher, _) = setup_snake(); let result = dispatcher @@ -62,7 +61,7 @@ fn test_dual_on_erc1155_received() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_on_erc1155_received() { let dispatcher = setup_non_erc1155_receiver(); @@ -70,7 +69,6 @@ fn test_dual_no_on_erc1155_received() { } #[test] -#[ignore] #[should_panic(expected: ("Some error",))] fn test_dual_on_erc1155_received_exists_and_panics() { let (dispatcher, _) = setup_erc1155_receiver_panic(); @@ -78,7 +76,6 @@ fn test_dual_on_erc1155_received_exists_and_panics() { } #[test] -#[ignore] fn test_dual_on_erc1155_batch_received() { let (dispatcher, _) = setup_snake(); let (token_ids, values) = get_ids_and_values(); @@ -89,7 +86,7 @@ fn test_dual_on_erc1155_batch_received() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_on_erc1155_batch_received() { let dispatcher = setup_non_erc1155_receiver(); @@ -98,7 +95,6 @@ fn test_dual_no_on_erc1155_batch_received() { } #[test] -#[ignore] #[should_panic(expected: ("Some error",))] fn test_dual_on_erc1155_batch_received_exists_and_panics() { let (dispatcher, _) = setup_erc1155_receiver_panic(); @@ -111,7 +107,7 @@ fn test_dual_on_erc1155_batch_received_exists_and_panics() { // #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_onERC1155Received() { let (dispatcher, _) = setup_camel(); let result = dispatcher @@ -120,7 +116,7 @@ fn test_dual_onERC1155Received() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_onERC1155Received_exists_and_panics() { let (_, dispatcher) = setup_erc1155_receiver_panic(); @@ -128,7 +124,7 @@ fn test_dual_onERC1155Received_exists_and_panics() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_onERC1155BatchReceived() { let (dispatcher, _) = setup_camel(); let (token_ids, values) = get_ids_and_values(); @@ -139,7 +135,7 @@ fn test_dual_onERC1155BatchReceived() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_onERC1155BatchReceived_exists_and_panics() { let (_, dispatcher) = setup_erc1155_receiver_panic(); diff --git a/src/tests/token/erc1155/test_erc1155.cairo b/src/tests/token/erc1155/test_erc1155.cairo index db07832e5..951de7b70 100644 --- a/src/tests/token/erc1155/test_erc1155.cairo +++ b/src/tests/token/erc1155/test_erc1155.cairo @@ -158,7 +158,7 @@ fn test_safe_transfer_from_owner_to_receiver() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safe_transfer_from_owner_to_camel_receiver() { let (mut state, owner) = setup(); let recipient = setup_camel_receiver(); @@ -197,7 +197,7 @@ fn test_safeTransferFrom_owner_to_receiver() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safeTransferFrom_owner_to_camel_receiver() { let (mut state, owner) = setup(); let recipient = setup_camel_receiver(); @@ -423,7 +423,7 @@ fn test_safe_batch_transfer_from_owner_to_receiver() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safe_batch_transfer_from_owner_to_camel_receiver() { let (mut state, owner) = setup(); let recipient = setup_camel_receiver(); @@ -464,7 +464,7 @@ fn test_safeBatchTransferFrom_owner_to_receiver() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safeBatchTransferFrom_owner_to_camel_receiver() { let (mut state, owner) = setup(); let recipient = setup_camel_receiver(); @@ -919,7 +919,7 @@ fn test_update_wac_single_from_non_zero_to_non_zero() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_update_wac_single_from_non_zero_to_non_zero_camel_receiver() { let (mut state, owner) = setup(); let recipient = setup_camel_receiver(); @@ -983,7 +983,7 @@ fn test_update_wac_batch_from_non_zero_to_non_zero() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_update_wac_batch_from_non_zero_to_non_zero_camel_receiver() { let (mut state, owner) = setup(); let recipient = setup_camel_receiver(); @@ -1061,7 +1061,7 @@ fn test_update_wac_from_zero_to_non_zero() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_update_wac_from_zero_to_non_zero_camel_receiver() { let (mut state, owner) = setup(); let recipient = setup_camel_receiver(); diff --git a/src/tests/token/erc20/test_dual20.cairo b/src/tests/token/erc20/test_dual20.cairo index dc37ce836..8a24c3a9a 100644 --- a/src/tests/token/erc20/test_dual20.cairo +++ b/src/tests/token/erc20/test_dual20.cairo @@ -52,7 +52,6 @@ fn setup_erc20_panic() -> (DualCaseERC20, DualCaseERC20) { // #[test] -#[ignore] fn test_dual_name() { let (snake_dispatcher, _) = setup_snake(); assert_eq!(snake_dispatcher.name(), NAME()); @@ -62,7 +61,7 @@ fn test_dual_name() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_name() { let dispatcher = setup_non_erc20(); @@ -70,15 +69,13 @@ fn test_dual_no_name() { } #[test] -#[ignore] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_name_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.name(); } #[test] -#[ignore] fn test_dual_symbol() { let (snake_dispatcher, _) = setup_snake(); let (camel_dispatcher, _) = setup_camel(); @@ -87,7 +84,7 @@ fn test_dual_symbol() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_symbol() { let dispatcher = setup_non_erc20(); @@ -95,15 +92,13 @@ fn test_dual_no_symbol() { } #[test] -#[ignore] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_symbol_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.symbol(); } #[test] -#[ignore] fn test_dual_decimals() { let (snake_dispatcher, _) = setup_snake(); let (camel_dispatcher, _) = setup_camel(); @@ -112,7 +107,7 @@ fn test_dual_decimals() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_decimals() { let dispatcher = setup_non_erc20(); @@ -120,30 +115,28 @@ fn test_dual_no_decimals() { } #[test] -#[ignore] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_decimals_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.decimals(); } #[test] -#[ignore] fn test_dual_transfer() { let (snake_dispatcher, snake_target) = setup_snake(); - start_cheat_caller_address(test_address(), OWNER()); + start_cheat_caller_address(snake_dispatcher.contract_address, OWNER()); assert!(snake_dispatcher.transfer(RECIPIENT(), VALUE)); assert_eq!(snake_target.balance_of(RECIPIENT()), VALUE); let (camel_dispatcher, camel_target) = setup_camel(); - start_cheat_caller_address(test_address(), OWNER()); + start_cheat_caller_address(camel_dispatcher.contract_address, OWNER()); assert!(camel_dispatcher.transfer(RECIPIENT(), VALUE)); assert_eq!(camel_target.balanceOf(RECIPIENT()), VALUE); } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_transfer() { let dispatcher = setup_non_erc20(); @@ -151,25 +144,23 @@ fn test_dual_no_transfer() { } #[test] -#[ignore] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_transfer_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.transfer(RECIPIENT(), VALUE); } #[test] -#[ignore] fn test_dual_approve() { let (snake_dispatcher, snake_target) = setup_snake(); - start_cheat_caller_address(test_address(), OWNER()); + start_cheat_caller_address(snake_dispatcher.contract_address, OWNER()); assert!(snake_dispatcher.approve(SPENDER(), VALUE)); let snake_allowance = snake_target.allowance(OWNER(), SPENDER()); assert_eq!(snake_allowance, VALUE); let (camel_dispatcher, camel_target) = setup_camel(); - start_cheat_caller_address(test_address(), OWNER()); + start_cheat_caller_address(camel_dispatcher.contract_address, OWNER()); assert!(camel_dispatcher.approve(SPENDER(), VALUE)); let camel_allowance = camel_target.allowance(OWNER(), SPENDER()); @@ -177,7 +168,7 @@ fn test_dual_approve() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_approve() { let dispatcher = setup_non_erc20(); @@ -185,8 +176,7 @@ fn test_dual_no_approve() { } #[test] -#[ignore] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_approve_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.approve(SPENDER(), VALUE); @@ -197,14 +187,13 @@ fn test_dual_approve_exists_and_panics() { // #[test] -#[ignore] fn test_dual_total_supply() { let (dispatcher, _) = setup_snake(); assert_eq!(dispatcher.total_supply(), SUPPLY); } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_total_supply() { let dispatcher = setup_non_erc20(); @@ -212,22 +201,20 @@ fn test_dual_no_total_supply() { } #[test] -#[ignore] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_total_supply_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.total_supply(); } #[test] -#[ignore] fn test_dual_balance_of() { let (dispatcher, _) = setup_snake(); assert_eq!(dispatcher.balance_of(OWNER()), SUPPLY); } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_balance_of() { let dispatcher = setup_non_erc20(); @@ -235,27 +222,25 @@ fn test_dual_no_balance_of() { } #[test] -#[ignore] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_balance_of_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.balance_of(OWNER()); } #[test] -#[ignore] fn test_dual_transfer_from() { let (dispatcher, target) = setup_snake(); - start_cheat_caller_address(test_address(), OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); target.approve(OPERATOR(), VALUE); - start_cheat_caller_address(test_address(), OPERATOR()); + start_cheat_caller_address(dispatcher.contract_address, OPERATOR()); dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); assert_eq!(target.balance_of(RECIPIENT()), VALUE); } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_transfer_from() { let dispatcher = setup_non_erc20(); @@ -263,8 +248,7 @@ fn test_dual_no_transfer_from() { } #[test] -#[ignore] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_transfer_from_exists_and_panics() { let (dispatcher, _) = setup_erc20_panic(); dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); @@ -275,37 +259,37 @@ fn test_dual_transfer_from_exists_and_panics() { // #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_totalSupply() { let (dispatcher, _) = setup_camel(); assert_eq!(dispatcher.total_supply(), SUPPLY); } #[test] -#[ignore] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ("Some error",))] fn test_dual_totalSupply_exists_and_panics() { let (_, dispatcher) = setup_erc20_panic(); dispatcher.total_supply(); } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_balanceOf() { let (dispatcher, _) = setup_camel(); assert_eq!(dispatcher.balance_of(OWNER()), SUPPLY); } #[test] -#[ignore] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ("Some error",))] fn test_dual_balanceOf_exists_and_panics() { let (_, dispatcher) = setup_erc20_panic(); dispatcher.balance_of(OWNER()); } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_transferFrom() { let (dispatcher, target) = setup_camel(); start_cheat_caller_address(test_address(), OWNER()); @@ -317,8 +301,8 @@ fn test_dual_transferFrom() { } #[test] -#[ignore] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ("Some error",))] fn test_dual_transferFrom_exists_and_panics() { let (_, dispatcher) = setup_erc20_panic(); dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); diff --git a/src/tests/token/erc721/test_dual721.cairo b/src/tests/token/erc721/test_dual721.cairo index 943608c9e..b4b13cbb6 100644 --- a/src/tests/token/erc721/test_dual721.cairo +++ b/src/tests/token/erc721/test_dual721.cairo @@ -24,8 +24,8 @@ fn setup_snake() -> (DualCaseERC721, IERC721Dispatcher) { calldata.append_serde(OWNER()); calldata.append_serde(TOKEN_ID); - start_cheat_caller_address(test_address(), OWNER()); let target = utils::declare_and_deploy("SnakeERC721Mock", calldata); + start_cheat_caller_address(target, OWNER()); (DualCaseERC721 { contract_address: target }, IERC721Dispatcher { contract_address: target }) } @@ -37,8 +37,8 @@ fn setup_camel() -> (DualCaseERC721, IERC721CamelOnlyDispatcher) { calldata.append_serde(OWNER()); calldata.append_serde(TOKEN_ID); - start_cheat_caller_address(test_address(), OWNER()); let target = utils::declare_and_deploy("CamelERC721Mock", calldata); + start_cheat_caller_address(target, OWNER()); ( DualCaseERC721 { contract_address: target }, IERC721CamelOnlyDispatcher { contract_address: target } @@ -69,17 +69,16 @@ fn setup_receiver() -> ContractAddress { // #[test] -#[ignore] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_name() { let (snake_dispatcher, _) = setup_snake(); - let (camel_dispatcher, _) = setup_camel(); assert_eq!(snake_dispatcher.name(), NAME()); + + let (camel_dispatcher, _) = setup_camel(); assert_eq!(camel_dispatcher.name(), NAME()); } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_name() { let dispatcher = setup_non_erc721(); @@ -94,7 +93,6 @@ fn test_dual_name_exists_and_panics() { } #[test] -#[ignore] fn test_dual_symbol() { let (snake_dispatcher, _) = setup_snake(); let (camel_dispatcher, _) = setup_camel(); @@ -103,7 +101,7 @@ fn test_dual_symbol() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_symbol() { let dispatcher = setup_non_erc721(); @@ -118,23 +116,22 @@ fn test_dual_symbol_exists_and_panics() { } #[test] -#[ignore] fn test_dual_approve() { let (snake_dispatcher, snake_target) = setup_snake(); - start_cheat_caller_address(test_address(), OWNER()); + start_cheat_caller_address(snake_dispatcher.contract_address, OWNER()); snake_dispatcher.approve(SPENDER(), TOKEN_ID); assert_eq!(snake_target.get_approved(TOKEN_ID), SPENDER()); let (camel_dispatcher, camel_target) = setup_camel(); - start_cheat_caller_address(test_address(), OWNER()); + start_cheat_caller_address(camel_dispatcher.contract_address, OWNER()); camel_dispatcher.approve(SPENDER(), TOKEN_ID); assert_eq!(camel_target.getApproved(TOKEN_ID), SPENDER()); } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_approve() { let dispatcher = setup_non_erc721(); @@ -153,14 +150,13 @@ fn test_dual_approve_exists_and_panics() { // #[test] -#[ignore] fn test_dual_balance_of() { let (dispatcher, _) = setup_snake(); assert_eq!(dispatcher.balance_of(OWNER()), 1); } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_balance_of() { let dispatcher = setup_non_erc721(); @@ -175,14 +171,13 @@ fn test_dual_balance_of_exists_and_panics() { } #[test] -#[ignore] fn test_dual_owner_of() { let (dispatcher, _) = setup_snake(); assert_eq!(dispatcher.owner_of(TOKEN_ID), OWNER()); } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_owner_of() { let dispatcher = setup_non_erc721(); @@ -197,7 +192,6 @@ fn test_dual_owner_of_exists_and_panics() { } #[test] -#[ignore] fn test_dual_transfer_from() { let (dispatcher, target) = setup_snake(); dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); @@ -205,7 +199,7 @@ fn test_dual_transfer_from() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_transfer_from() { let dispatcher = setup_non_erc721(); @@ -220,7 +214,6 @@ fn test_dual_transfer_from_exists_and_panics() { } #[test] -#[ignore] fn test_dual_safe_transfer_from() { let (dispatcher, target) = setup_snake(); let receiver = setup_receiver(); @@ -229,7 +222,7 @@ fn test_dual_safe_transfer_from() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_safe_transfer_from() { let dispatcher = setup_non_erc721(); @@ -244,17 +237,16 @@ fn test_dual_safe_transfer_from_exists_and_panics() { } #[test] -#[ignore] fn test_dual_get_approved() { let (dispatcher, target) = setup_snake(); - start_cheat_caller_address(test_address(), OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); target.approve(SPENDER(), TOKEN_ID); assert_eq!(dispatcher.get_approved(TOKEN_ID), SPENDER()); } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_get_approved() { let dispatcher = setup_non_erc721(); @@ -269,11 +261,10 @@ fn test_dual_get_approved_exists_and_panics() { } #[test] -#[ignore] fn test_dual_set_approval_for_all() { let (dispatcher, target) = setup_snake(); - start_cheat_caller_address(test_address(), OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.set_approval_for_all(OPERATOR(), true); let is_approved_for_all = target.is_approved_for_all(OWNER(), OPERATOR()); @@ -281,7 +272,7 @@ fn test_dual_set_approval_for_all() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_set_approval_for_all() { let dispatcher = setup_non_erc721(); @@ -296,11 +287,10 @@ fn test_dual_set_approval_for_all_exists_and_panics() { } #[test] -#[ignore] fn test_dual_is_approved_for_all() { let (dispatcher, target) = setup_snake(); - start_cheat_caller_address(test_address(), OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); target.set_approval_for_all(OPERATOR(), true); let is_approved_for_all = dispatcher.is_approved_for_all(OWNER(), OPERATOR()); @@ -308,7 +298,7 @@ fn test_dual_is_approved_for_all() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_is_approved_for_all() { let dispatcher = setup_non_erc721(); @@ -323,7 +313,6 @@ fn test_dual_is_approved_for_all_exists_and_panics() { } #[test] -#[ignore] fn test_dual_token_uri() { let (dispatcher, _) = setup_snake(); let uri = dispatcher.token_uri(TOKEN_ID); @@ -332,7 +321,7 @@ fn test_dual_token_uri() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_token_uri() { let dispatcher = setup_non_erc721(); @@ -347,7 +336,6 @@ fn test_dual_token_uri_exists_and_panics() { } #[test] -#[ignore] fn test_dual_supports_interface() { let (dispatcher, _) = setup_snake(); let supports_ierc721 = dispatcher.supports_interface(IERC721_ID); @@ -355,7 +343,7 @@ fn test_dual_supports_interface() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_supports_interface() { let dispatcher = setup_non_erc721(); @@ -374,14 +362,14 @@ fn test_dual_supports_interface_exists_and_panics() { // #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_balanceOf() { let (dispatcher, _) = setup_camel(); assert_eq!(dispatcher.balance_of(OWNER()), 1); } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_balanceOf_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); @@ -389,7 +377,7 @@ fn test_dual_balanceOf_exists_and_panics() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_ownerOf() { let (dispatcher, _) = setup_camel(); let current_owner = dispatcher.owner_of(TOKEN_ID); @@ -397,7 +385,7 @@ fn test_dual_ownerOf() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_ownerOf_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); @@ -405,11 +393,11 @@ fn test_dual_ownerOf_exists_and_panics() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_transferFrom() { let (dispatcher, target) = setup_camel(); - start_cheat_caller_address(test_address(), OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_ID); let current_owner = target.ownerOf(TOKEN_ID); @@ -417,7 +405,7 @@ fn test_dual_transferFrom() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_transferFrom_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); @@ -425,7 +413,7 @@ fn test_dual_transferFrom_exists_and_panics() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_safeTransferFrom() { let (dispatcher, target) = setup_camel(); let receiver = setup_receiver(); @@ -436,7 +424,7 @@ fn test_dual_safeTransferFrom() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_safeTransferFrom_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); @@ -444,11 +432,11 @@ fn test_dual_safeTransferFrom_exists_and_panics() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_getApproved() { let (dispatcher, _) = setup_camel(); - start_cheat_caller_address(test_address(), OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.approve(SPENDER(), TOKEN_ID); let approved = dispatcher.get_approved(TOKEN_ID); @@ -456,7 +444,7 @@ fn test_dual_getApproved() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_getApproved_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); @@ -464,11 +452,11 @@ fn test_dual_getApproved_exists_and_panics() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_setApprovalForAll() { let (dispatcher, target) = setup_camel(); - start_cheat_caller_address(test_address(), OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.set_approval_for_all(OPERATOR(), true); let is_approved_for_all = target.isApprovedForAll(OWNER(), OPERATOR()); @@ -476,7 +464,7 @@ fn test_dual_setApprovalForAll() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_setApprovalForAll_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); @@ -484,11 +472,11 @@ fn test_dual_setApprovalForAll_exists_and_panics() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_isApprovedForAll() { let (dispatcher, target) = setup_camel(); - start_cheat_caller_address(test_address(), OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); target.setApprovalForAll(OPERATOR(), true); let is_approved_for_all = dispatcher.is_approved_for_all(OWNER(), OPERATOR()); @@ -496,7 +484,7 @@ fn test_dual_isApprovedForAll() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_isApprovedForAll_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); @@ -504,7 +492,7 @@ fn test_dual_isApprovedForAll_exists_and_panics() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_tokenURI() { let (dispatcher, _) = setup_camel(); let uri = dispatcher.token_uri(TOKEN_ID); @@ -513,7 +501,7 @@ fn test_dual_tokenURI() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_tokenURI_exists_and_panics() { let (_, dispatcher) = setup_erc721_panic(); diff --git a/src/tests/token/erc721/test_dual721_receiver.cairo b/src/tests/token/erc721/test_dual721_receiver.cairo index f70d232d8..1a5ae99e4 100644 --- a/src/tests/token/erc721/test_dual721_receiver.cairo +++ b/src/tests/token/erc721/test_dual721_receiver.cairo @@ -62,7 +62,7 @@ fn test_dual_on_erc721_received() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_on_erc721_received() { let dispatcher = setup_non_erc721_receiver(); @@ -70,7 +70,6 @@ fn test_dual_no_on_erc721_received() { } #[test] -#[ignore] #[should_panic(expected: ("Some error",))] fn test_dual_on_erc721_received_exists_and_panics() { let (dispatcher, _) = setup_erc721_receiver_panic(); @@ -82,7 +81,7 @@ fn test_dual_on_erc721_received_exists_and_panics() { // #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_onERC721Received() { let (dispatcher, _) = setup_camel(); @@ -92,7 +91,7 @@ fn test_dual_onERC721Received() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_onERC721Received_exists_and_panics() { let (_, dispatcher) = setup_erc721_receiver_panic(); diff --git a/src/tests/token/erc721/test_erc721.cairo b/src/tests/token/erc721/test_erc721.cairo index 365e8c942..8055d9c17 100644 --- a/src/tests/token/erc721/test_erc721.cairo +++ b/src/tests/token/erc721/test_erc721.cairo @@ -635,9 +635,9 @@ fn test_safeTransferFrom_to_account_camel() { #[test] fn test_safe_transfer_from_to_receiver() { let mut state = setup(); + let receiver = setup_receiver(); let contract_address = test_address(); let mut spy = spy_events(); - let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -653,9 +653,9 @@ fn test_safe_transfer_from_to_receiver() { #[test] fn test_safeTransferFrom_to_receiver() { let mut state = setup(); + let receiver = setup_receiver(); let contract_address = test_address(); let mut spy = spy_events(); - let receiver = setup_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -669,12 +669,12 @@ fn test_safeTransferFrom_to_receiver() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safe_transfer_from_to_receiver_camel() { let mut state = setup(); + let receiver = setup_camel_receiver(); let contract_address = test_address(); let mut spy = spy_events(); - let receiver = setup_camel_receiver(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -688,12 +688,12 @@ fn test_safe_transfer_from_to_receiver_camel() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safeTransferFrom_to_receiver_camel() { let mut state = setup(); - let contract_address = test_address(); - let mut spy = spy_events(); let receiver = setup_camel_receiver(); + let mut spy = spy_events(); + let contract_address = test_address(); let token_id = TOKEN_ID; let owner = OWNER(); @@ -731,7 +731,7 @@ fn test_safeTransferFrom_to_receiver_failure() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ('ERC721: safe transfer failed',))] fn test_safe_transfer_from_to_receiver_failure_camel() { let mut state = setup(); @@ -744,7 +744,7 @@ fn test_safe_transfer_from_to_receiver_failure_camel() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ('ERC721: safe transfer failed',))] fn test_safeTransferFrom_to_receiver_failure_camel() { let mut state = setup(); @@ -757,7 +757,7 @@ fn test_safeTransferFrom_to_receiver_failure_camel() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_safe_transfer_from_to_non_receiver() { let mut state = setup(); @@ -770,7 +770,7 @@ fn test_safe_transfer_from_to_non_receiver() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_safeTransferFrom_to_non_receiver() { let mut state = setup(); @@ -861,17 +861,18 @@ fn test_safeTransferFrom_to_owner() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safe_transfer_from_to_owner_camel() { let mut state = COMPONENT_STATE(); - let contract_address = test_address(); - let mut spy = spy_events(); let token_id = TOKEN_ID; let owner = setup_camel_receiver(); + let contract_address = test_address(); state.initializer(NAME(), SYMBOL(), BASE_URI()); state.mint(owner, token_id); + let mut spy = spy_events(); + assert_eq!(state.owner_of(token_id), owner); assert_eq!(state.balance_of(owner), 1); @@ -884,11 +885,10 @@ fn test_safe_transfer_from_to_owner_camel() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safeTransferFrom_to_owner_camel() { let mut state = COMPONENT_STATE(); let contract_address = test_address(); - let mut spy = spy_events(); let token_id = TOKEN_ID; let owner = setup_camel_receiver(); @@ -898,6 +898,8 @@ fn test_safeTransferFrom_to_owner_camel() { assert_eq!(state.owner_of(token_id), owner); assert_eq!(state.balance_of(owner), 1); + let mut spy = spy_events(); + start_cheat_caller_address(contract_address, owner); state.safeTransferFrom(owner, owner, token_id, DATA(true)); spy.assert_only_event_transfer(contract_address, owner, owner, token_id); @@ -951,7 +953,7 @@ fn test_safeTransferFrom_approved() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safe_transfer_from_approved_camel() { let mut state = setup(); let contract_address = test_address(); @@ -974,7 +976,7 @@ fn test_safe_transfer_from_approved_camel() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safeTransferFrom_approved_camel() { let mut state = setup(); let contract_address = test_address(); @@ -1041,7 +1043,7 @@ fn test_safeTransferFrom_approved_for_all() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safe_transfer_from_approved_for_all_camel() { let mut state = setup(); let contract_address = test_address(); @@ -1063,7 +1065,7 @@ fn test_safe_transfer_from_approved_for_all_camel() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safeTransferFrom_approved_for_all_camel() { let mut state = setup(); let contract_address = test_address(); @@ -1196,7 +1198,7 @@ fn test__safe_mint_to_receiver() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test__safe_mint_to_receiver_camel() { let mut state = COMPONENT_STATE(); let contract_address = test_address(); @@ -1242,7 +1244,7 @@ fn test__safe_mint_to_account_camel() { } #[test] -#[ignore] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test__safe_mint_to_non_receiver() { let mut state = COMPONENT_STATE(); @@ -1267,7 +1269,7 @@ fn test__safe_mint_to_receiver_failure() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ('ERC721: safe mint failed',))] fn test__safe_mint_to_receiver_failure_camel() { let mut state = COMPONENT_STATE(); diff --git a/src/tests/utils/common.cairo b/src/tests/utils/common.cairo index 88c443cfb..c5920b969 100644 --- a/src/tests/utils/common.cairo +++ b/src/tests/utils/common.cairo @@ -1,5 +1,6 @@ use core::starknet::SyscallResultTrait; use snforge_std::{declare, get_class_hash, ContractClass, ContractClassTrait}; +use snforge_std::{start_cheat_caller_address, stop_cheat_caller_address}; use starknet::ContractAddress; pub fn deploy(contract_class: ContractClass, calldata: Array) -> ContractAddress { From 8fe4f801cd9cb1bbd908b54bf53b66d83477c489 Mon Sep 17 00:00:00 2001 From: immrsd <103599616+immrsd@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:02:44 +0200 Subject: [PATCH 08/20] Migrate Ownable tests (#1033) * Update ERC20Votes tests * Run linter * Add test helpers for Ownable tests * Migrate Ownable tests * Migrate OwnableTwoStep tests * Migrate Ownable Dual Dispatcher tests * Support event changes in snforge 0.26 * Resolve review issues * Address review comments --- src/tests.cairo | 8 +- src/tests/access.cairo | 4 +- src/tests/access/common.cairo | 44 ++++--- src/tests/access/test_dual_ownable.cairo | 54 ++++---- src/tests/access/test_ownable.cairo | 52 ++++---- src/tests/access/test_ownable_twostep.cairo | 133 +++++++++++--------- 6 files changed, 166 insertions(+), 129 deletions(-) diff --git a/src/tests.cairo b/src/tests.cairo index eceac55c6..fcb9fb1dc 100644 --- a/src/tests.cairo +++ b/src/tests.cairo @@ -1,11 +1,11 @@ -// #[cfg(test)] -// mod access; +#[cfg(test)] +mod access; // #[cfg(test)] // mod account; // #[cfg(test)] // mod cryptography; -// #[cfg(test)] -// mod introspection; +#[cfg(test)] +mod introspection; mod mocks; // #[cfg(test)] // mod presets; diff --git a/src/tests/access.cairo b/src/tests/access.cairo index cfa981899..c59f6dbc4 100644 --- a/src/tests/access.cairo +++ b/src/tests/access.cairo @@ -1,7 +1,7 @@ pub(crate) mod common; -mod test_accesscontrol; -mod test_dual_accesscontrol; +// mod test_accesscontrol; +// mod test_dual_accesscontrol; mod test_dual_ownable; mod test_ownable; mod test_ownable_twostep; diff --git a/src/tests/access/common.cairo b/src/tests/access/common.cairo index 752b08584..6836a3437 100644 --- a/src/tests/access/common.cairo +++ b/src/tests/access/common.cairo @@ -1,28 +1,32 @@ use openzeppelin::access::ownable::OwnableComponent::OwnershipTransferred; use openzeppelin::access::ownable::OwnableComponent; +use openzeppelin::tests::utils::EventSpyExt; use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::EventSpy; use starknet::ContractAddress; -pub(crate) fn assert_only_event_ownership_transferred( - contract: ContractAddress, previous_owner: ContractAddress, new_owner: ContractAddress -) { - assert_event_ownership_transferred(contract, previous_owner, new_owner); - utils::assert_no_events_left(contract); -} - -pub(crate) fn assert_event_ownership_transferred( - contract: ContractAddress, previous_owner: ContractAddress, new_owner: ContractAddress -) { - let event = utils::pop_log::(contract).unwrap(); - let expected = OwnableComponent::Event::OwnershipTransferred( - OwnershipTransferred { previous_owner, new_owner } - ); - assert!(event == expected); +#[generate_trait] +pub(crate) impl OwnableSpyHelpersImpl of OwnableSpyHelpers { + fn assert_only_event_ownership_transferred( + ref self: EventSpy, + contract: ContractAddress, + previous_owner: ContractAddress, + new_owner: ContractAddress + ) { + self.assert_event_ownership_transferred(contract, previous_owner, new_owner); + self.assert_no_events_left_from(contract); + } - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("OwnershipTransferred")); - indexed_keys.append_serde(previous_owner); - indexed_keys.append_serde(new_owner); - utils::assert_indexed_keys(event, indexed_keys.span()); + fn assert_event_ownership_transferred( + ref self: EventSpy, + contract: ContractAddress, + previous_owner: ContractAddress, + new_owner: ContractAddress + ) { + let expected = OwnableComponent::Event::OwnershipTransferred( + OwnershipTransferred { previous_owner, new_owner } + ); + self.assert_emitted_single(contract, expected); + } } diff --git a/src/tests/access/test_dual_ownable.cairo b/src/tests/access/test_dual_ownable.cairo index c29d09fef..8d24aaae7 100644 --- a/src/tests/access/test_dual_ownable.cairo +++ b/src/tests/access/test_dual_ownable.cairo @@ -1,9 +1,8 @@ use core::num::traits::Zero; -use openzeppelin::access::ownable::dual_ownable::DualCaseOwnable; -use openzeppelin::access::ownable::dual_ownable::DualCaseOwnableTrait; -use openzeppelin::access::ownable::interface::IOwnableCamelOnlyDispatcher; -use openzeppelin::access::ownable::interface::IOwnableDispatcher; -use openzeppelin::access::ownable::interface::IOwnableDispatcherTrait; +use openzeppelin::access::ownable::dual_ownable::{DualCaseOwnable, DualCaseOwnableTrait}; +use openzeppelin::access::ownable::interface::{ + IOwnableDispatcher, IOwnableCamelOnlyDispatcher, IOwnableDispatcherTrait +}; use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::mocks::ownable_mocks::{ CamelOwnableMock, CamelOwnablePanicMock, SnakeOwnableMock, SnakeOwnablePanicMock @@ -11,7 +10,7 @@ use openzeppelin::tests::mocks::ownable_mocks::{ use openzeppelin::tests::utils::constants::{OWNER, NEW_OWNER}; use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; -use starknet::testing::set_contract_address; +use snforge_std::start_cheat_caller_address; // // Setup @@ -20,14 +19,14 @@ use starknet::testing::set_contract_address; fn setup_snake() -> (DualCaseOwnable, IOwnableDispatcher) { let mut calldata = array![]; calldata.append_serde(OWNER()); - let target = utils::deploy(SnakeOwnableMock::TEST_CLASS_HASH, calldata); + let target = utils::declare_and_deploy("SnakeOwnableMock", calldata); (DualCaseOwnable { contract_address: target }, IOwnableDispatcher { contract_address: target }) } fn setup_camel() -> (DualCaseOwnable, IOwnableCamelOnlyDispatcher) { let mut calldata = array![]; calldata.append_serde(OWNER()); - let target = utils::deploy(CamelOwnableMock::TEST_CLASS_HASH, calldata); + let target = utils::declare_and_deploy("CamelOwnableMock", calldata); ( DualCaseOwnable { contract_address: target }, IOwnableCamelOnlyDispatcher { contract_address: target } @@ -36,13 +35,13 @@ fn setup_camel() -> (DualCaseOwnable, IOwnableCamelOnlyDispatcher) { fn setup_non_ownable() -> DualCaseOwnable { let calldata = array![]; - let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); + let target = utils::declare_and_deploy("NonImplementingMock", calldata); DualCaseOwnable { contract_address: target } } fn setup_ownable_panic() -> (DualCaseOwnable, DualCaseOwnable) { - let snake_target = utils::deploy(SnakeOwnablePanicMock::TEST_CLASS_HASH, array![]); - let camel_target = utils::deploy(CamelOwnablePanicMock::TEST_CLASS_HASH, array![]); + let snake_target = utils::declare_and_deploy("SnakeOwnablePanicMock", array![]); + let camel_target = utils::declare_and_deploy("CamelOwnablePanicMock", array![]); ( DualCaseOwnable { contract_address: snake_target }, DualCaseOwnable { contract_address: camel_target } @@ -66,6 +65,7 @@ fn test_dual_owner() { } #[test] +#[ignore] // REASON: inconsistent ENTRYPOINT_NOT_FOUND panic message #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_owner() { let dispatcher = setup_non_ownable(); @@ -73,7 +73,7 @@ fn test_dual_no_owner() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_owner_exists_and_panics() { let (dispatcher, _) = setup_ownable_panic(); dispatcher.owner(); @@ -86,7 +86,7 @@ fn test_dual_owner_exists_and_panics() { #[test] fn test_dual_transfer_ownership() { let (dispatcher, target) = setup_snake(); - set_contract_address(OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.transfer_ownership(NEW_OWNER()); let current_owner = target.owner(); @@ -94,6 +94,7 @@ fn test_dual_transfer_ownership() { } #[test] +#[ignore] // REASON: inconsistent ENTRYPOINT_NOT_FOUND panic message #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_transfer_ownership() { let dispatcher = setup_non_ownable(); @@ -101,7 +102,7 @@ fn test_dual_no_transfer_ownership() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_transfer_ownership_exists_and_panics() { let (dispatcher, _) = setup_ownable_panic(); dispatcher.transfer_ownership(NEW_OWNER()); @@ -110,7 +111,7 @@ fn test_dual_transfer_ownership_exists_and_panics() { #[test] fn test_dual_renounce_ownership() { let (dispatcher, target) = setup_snake(); - set_contract_address(OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.renounce_ownership(); let current_owner = target.owner(); @@ -118,6 +119,7 @@ fn test_dual_renounce_ownership() { } #[test] +#[ignore] // REASON: inconsistent ENTRYPOINT_NOT_FOUND panic message #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_renounce_ownership() { let dispatcher = setup_non_ownable(); @@ -125,7 +127,7 @@ fn test_dual_no_renounce_ownership() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_renounce_ownership_exists_and_panics() { let (dispatcher, _) = setup_ownable_panic(); dispatcher.renounce_ownership(); @@ -136,9 +138,10 @@ fn test_dual_renounce_ownership_exists_and_panics() { // #[test] +#[ignore] fn test_dual_transferOwnership() { let (dispatcher, _) = setup_camel(); - set_contract_address(OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.transfer_ownership(NEW_OWNER()); let current_owner = dispatcher.owner(); @@ -146,16 +149,18 @@ fn test_dual_transferOwnership() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_transferOwnership_exists_and_panics() { - let (_, dispatcher) = setup_ownable_panic(); - dispatcher.transfer_ownership(NEW_OWNER()); + let (_, camel_dispatcher) = setup_ownable_panic(); + camel_dispatcher.transfer_ownership(NEW_OWNER()); } #[test] +#[ignore] fn test_dual_renounceOwnership() { let (dispatcher, _) = setup_camel(); - set_contract_address(OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.renounce_ownership(); let current_owner = dispatcher.owner(); @@ -163,9 +168,10 @@ fn test_dual_renounceOwnership() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] +#[should_panic(expected: ("Some error",))] fn test_dual_renounceOwnership_exists_and_panics() { - let (_, dispatcher) = setup_ownable_panic(); - dispatcher.renounce_ownership(); + let (_, camel_dispatcher) = setup_ownable_panic(); + camel_dispatcher.renounce_ownership(); } diff --git a/src/tests/access/test_ownable.cairo b/src/tests/access/test_ownable.cairo index ea4b97c74..463406916 100644 --- a/src/tests/access/test_ownable.cairo +++ b/src/tests/access/test_ownable.cairo @@ -4,11 +4,10 @@ use openzeppelin::access::ownable::OwnableComponent; use openzeppelin::access::ownable::interface::{IOwnable, IOwnableCamelOnly}; use openzeppelin::tests::mocks::ownable_mocks::DualCaseOwnableMock; use openzeppelin::tests::utils::constants::{ZERO, OTHER, OWNER}; -use openzeppelin::tests::utils; +use snforge_std::{spy_events, test_address, start_cheat_caller_address}; use starknet::storage::StorageMemberAccessTrait; -use starknet::testing; -use super::common::assert_only_event_ownership_transferred; +use super::common::OwnableSpyHelpers; // // Setup @@ -23,7 +22,6 @@ fn COMPONENT_STATE() -> ComponentState { fn setup() -> ComponentState { let mut state = COMPONENT_STATE(); state.initializer(OWNER()); - utils::drop_event(ZERO()); state } @@ -34,13 +32,14 @@ fn setup() -> ComponentState { #[test] fn test_initializer_owner() { let mut state = COMPONENT_STATE(); + let mut spy = spy_events(); let current_owner = state.Ownable_owner.read(); assert!(current_owner.is_zero()); state.initializer(OWNER()); - assert_only_event_ownership_transferred(ZERO(), ZERO(), OWNER()); + spy.assert_only_event_ownership_transferred(test_address(), ZERO(), OWNER()); let new_owner = state.Ownable_owner.read(); assert_eq!(new_owner, OWNER()); @@ -53,7 +52,7 @@ fn test_initializer_owner() { #[test] fn test_assert_only_owner() { let state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.assert_only_owner(); } @@ -61,7 +60,7 @@ fn test_assert_only_owner() { #[should_panic(expected: ('Caller is not the owner',))] fn test_assert_only_owner_when_not_owner() { let state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.assert_only_owner(); } @@ -79,9 +78,10 @@ fn test_assert_only_owner_when_caller_zero() { #[test] fn test__transfer_ownership() { let mut state = setup(); + let mut spy = spy_events(); state._transfer_ownership(OTHER()); - assert_only_event_ownership_transferred(ZERO(), OWNER(), OTHER()); + spy.assert_only_event_ownership_transferred(test_address(), OWNER(), OTHER()); let current_owner = state.Ownable_owner.read(); assert_eq!(current_owner, OTHER()); @@ -94,10 +94,12 @@ fn test__transfer_ownership() { #[test] fn test_transfer_ownership() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, OWNER()); state.transfer_ownership(OTHER()); - assert_only_event_ownership_transferred(ZERO(), OWNER(), OTHER()); + spy.assert_only_event_ownership_transferred(contract_address, OWNER(), OTHER()); assert_eq!(state.owner(), OTHER()); } @@ -105,7 +107,7 @@ fn test_transfer_ownership() { #[should_panic(expected: ('New owner is the zero address',))] fn test_transfer_ownership_to_zero() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.transfer_ownership(ZERO()); } @@ -120,17 +122,19 @@ fn test_transfer_ownership_from_zero() { #[should_panic(expected: ('Caller is not the owner',))] fn test_transfer_ownership_from_nonowner() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.transfer_ownership(OTHER()); } #[test] fn test_transferOwnership() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, OWNER()); state.transferOwnership(OTHER()); - assert_only_event_ownership_transferred(ZERO(), OWNER(), OTHER()); + spy.assert_only_event_ownership_transferred(contract_address, OWNER(), OTHER()); assert_eq!(state.owner(), OTHER()); } @@ -138,7 +142,7 @@ fn test_transferOwnership() { #[should_panic(expected: ('New owner is the zero address',))] fn test_transferOwnership_to_zero() { let mut state = setup(); - testing::set_caller_address(OWNER()); + start_cheat_caller_address(test_address(), OWNER()); state.transferOwnership(ZERO()); } @@ -153,7 +157,7 @@ fn test_transferOwnership_from_zero() { #[should_panic(expected: ('Caller is not the owner',))] fn test_transferOwnership_from_nonowner() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.transferOwnership(OTHER()); } @@ -164,10 +168,12 @@ fn test_transferOwnership_from_nonowner() { #[test] fn test_renounce_ownership() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, OWNER()); state.renounce_ownership(); - assert_only_event_ownership_transferred(ZERO(), OWNER(), ZERO()); + spy.assert_only_event_ownership_transferred(contract_address, OWNER(), ZERO()); assert!(state.owner().is_zero()); } @@ -182,17 +188,19 @@ fn test_renounce_ownership_from_zero_address() { #[should_panic(expected: ('Caller is not the owner',))] fn test_renounce_ownership_from_nonowner() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.renounce_ownership(); } #[test] fn test_renounceOwnership() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, OWNER()); state.renounceOwnership(); - assert_only_event_ownership_transferred(ZERO(), OWNER(), ZERO()); + spy.assert_only_event_ownership_transferred(contract_address, OWNER(), ZERO()); assert!(state.owner().is_zero()); } @@ -207,6 +215,6 @@ fn test_renounceOwnership_from_zero_address() { #[should_panic(expected: ('Caller is not the owner',))] fn test_renounceOwnership_from_nonowner() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.renounceOwnership(); } diff --git a/src/tests/access/test_ownable_twostep.cairo b/src/tests/access/test_ownable_twostep.cairo index 0b300ddc4..55b58b09a 100644 --- a/src/tests/access/test_ownable_twostep.cairo +++ b/src/tests/access/test_ownable_twostep.cairo @@ -1,17 +1,16 @@ use core::num::traits::Zero; -use openzeppelin::access::ownable::OwnableComponent::InternalTrait; -use openzeppelin::access::ownable::OwnableComponent::OwnershipTransferStarted; +use openzeppelin::access::ownable::OwnableComponent::{InternalTrait, OwnershipTransferStarted}; use openzeppelin::access::ownable::OwnableComponent; use openzeppelin::access::ownable::interface::{IOwnableTwoStep, IOwnableTwoStepCamelOnly}; use openzeppelin::tests::mocks::ownable_mocks::DualCaseTwoStepOwnableMock; use openzeppelin::tests::utils::constants::{ZERO, OWNER, OTHER, NEW_OWNER}; -use openzeppelin::tests::utils; +use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::{EventSpy, spy_events, start_cheat_caller_address, test_address}; use starknet::ContractAddress; use starknet::storage::StorageMemberAccessTrait; -use starknet::testing; -use super::common::assert_only_event_ownership_transferred; +use super::common::OwnableSpyHelpers; // // Setup @@ -27,7 +26,6 @@ fn COMPONENT_STATE() -> ComponentState { fn setup() -> ComponentState { let mut state = COMPONENT_STATE(); state.initializer(OWNER()); - utils::drop_event(ZERO()); state } @@ -38,11 +36,12 @@ fn setup() -> ComponentState { #[test] fn test_initializer_owner_pending_owner() { let mut state = COMPONENT_STATE(); + let mut spy = spy_events(); assert!(state.Ownable_owner.read().is_zero()); assert!(state.Ownable_pending_owner.read().is_zero()); state.initializer(OWNER()); - assert_only_event_ownership_transferred(ZERO(), ZERO(), OWNER()); + spy.assert_only_event_ownership_transferred(test_address(), ZERO(), OWNER()); assert_eq!(state.Ownable_owner.read(), OWNER()); assert!(state.Ownable_pending_owner.read().is_zero()); @@ -55,11 +54,12 @@ fn test_initializer_owner_pending_owner() { #[test] fn test__accept_ownership() { let mut state = setup(); + let mut spy = spy_events(); state.Ownable_pending_owner.write(OTHER()); state._accept_ownership(); - assert_only_event_ownership_transferred(ZERO(), OWNER(), OTHER()); + spy.assert_only_event_ownership_transferred(test_address(), OWNER(), OTHER()); assert_eq!(state.owner(), OTHER()); assert!(state.pending_owner().is_zero()); } @@ -71,10 +71,11 @@ fn test__accept_ownership() { #[test] fn test__propose_owner() { let mut state = setup(); + let mut spy = spy_events(); state._propose_owner(OTHER()); - assert_event_ownership_transfer_started(OWNER(), OTHER()); + spy.assert_event_ownership_transfer_started(test_address(), OWNER(), OTHER()); assert_eq!(state.owner(), OWNER()); assert_eq!(state.pending_owner(), OTHER()); } @@ -84,17 +85,19 @@ fn test__propose_owner() { #[test] fn test_transfer_ownership() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, OWNER()); state.transfer_ownership(OTHER()); - assert_event_ownership_transfer_started(OWNER(), OTHER()); + spy.assert_event_ownership_transfer_started(contract_address, OWNER(), OTHER()); assert_eq!(state.owner(), OWNER()); assert_eq!(state.pending_owner(), OTHER()); // Transferring to yet another owner while pending is set should work state.transfer_ownership(NEW_OWNER()); - assert_event_ownership_transfer_started(OWNER(), NEW_OWNER()); + spy.assert_event_ownership_transfer_started(contract_address, OWNER(), NEW_OWNER()); assert_eq!(state.owner(), OWNER()); assert_eq!(state.pending_owner(), NEW_OWNER()); } @@ -102,10 +105,12 @@ fn test_transfer_ownership() { #[test] fn test_transfer_ownership_to_zero() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, OWNER()); state.transfer_ownership(ZERO()); - assert_event_ownership_transfer_started(OWNER(), ZERO()); + spy.assert_event_ownership_transfer_started(contract_address, OWNER(), ZERO()); assert_eq!(state.owner(), OWNER()); assert_eq!(state.pending_owner(), ZERO()); } @@ -121,24 +126,26 @@ fn test_transfer_ownership_from_zero() { #[should_panic(expected: ('Caller is not the owner',))] fn test_transfer_ownership_from_nonowner() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.transfer_ownership(OTHER()); } #[test] fn test_transferOwnership() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, OWNER()); state.transferOwnership(OTHER()); - assert_event_ownership_transfer_started(OWNER(), OTHER()); + spy.assert_event_ownership_transfer_started(contract_address, OWNER(), OTHER()); assert_eq!(state.owner(), OWNER()); assert_eq!(state.pendingOwner(), OTHER()); // Transferring to yet another owner while pending is set should work state.transferOwnership(NEW_OWNER()); - assert_event_ownership_transfer_started(OWNER(), NEW_OWNER()); + spy.assert_event_ownership_transfer_started(contract_address, OWNER(), NEW_OWNER()); assert_eq!(state.owner(), OWNER()); assert_eq!(state.pendingOwner(), NEW_OWNER()); } @@ -146,10 +153,12 @@ fn test_transferOwnership() { #[test] fn test_transferOwnership_to_zero() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, OWNER()); state.transferOwnership(ZERO()); - assert_event_ownership_transfer_started(OWNER(), ZERO()); + spy.assert_event_ownership_transfer_started(contract_address, OWNER(), ZERO()); assert_eq!(state.owner(), OWNER()); assert!(state.pendingOwner().is_zero()); } @@ -165,7 +174,7 @@ fn test_transferOwnership_from_zero() { #[should_panic(expected: ('Caller is not the owner',))] fn test_transferOwnership_from_nonowner() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.transferOwnership(OTHER()); } @@ -176,12 +185,14 @@ fn test_transferOwnership_from_nonowner() { #[test] fn test_accept_ownership() { let mut state = setup(); + let mut spy = spy_events(); + let contract_address = test_address(); state.Ownable_pending_owner.write(OTHER()); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(contract_address, OTHER()); state.accept_ownership(); - assert_only_event_ownership_transferred(ZERO(), OWNER(), OTHER()); + spy.assert_only_event_ownership_transferred(contract_address, OWNER(), OTHER()); assert_eq!(state.owner(), OTHER()); assert!(state.pending_owner().is_zero()); } @@ -191,19 +202,21 @@ fn test_accept_ownership() { fn test_accept_ownership_from_nonpending() { let mut state = setup(); state.Ownable_pending_owner.write(NEW_OWNER()); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.accept_ownership(); } #[test] fn test_acceptOwnership() { let mut state = setup(); + let mut spy = spy_events(); + let contract_address = test_address(); state.Ownable_pending_owner.write(OTHER()); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(contract_address, OTHER()); state.acceptOwnership(); - assert_only_event_ownership_transferred(ZERO(), OWNER(), OTHER()); + spy.assert_only_event_ownership_transferred(contract_address, OWNER(), OTHER()); assert_eq!(state.owner(), OTHER()); assert!(state.pendingOwner().is_zero()); } @@ -213,7 +226,7 @@ fn test_acceptOwnership() { fn test_acceptOwnership_from_nonpending() { let mut state = setup(); state.Ownable_pending_owner.write(NEW_OWNER()); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.acceptOwnership(); } @@ -224,10 +237,12 @@ fn test_acceptOwnership_from_nonpending() { #[test] fn test_renounce_ownership() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, OWNER()); state.renounce_ownership(); - assert_only_event_ownership_transferred(ZERO(), OWNER(), ZERO()); + spy.assert_only_event_ownership_transferred(contract_address, OWNER(), ZERO()); assert!(state.owner().is_zero()); } @@ -243,17 +258,19 @@ fn test_renounce_ownership_from_zero_address() { #[should_panic(expected: ('Caller is not the owner',))] fn test_renounce_ownership_from_nonowner() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.renounce_ownership(); } #[test] fn test_renounceOwnership() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, OWNER()); state.renounceOwnership(); - assert_only_event_ownership_transferred(ZERO(), OWNER(), ZERO()); + spy.assert_only_event_ownership_transferred(contract_address, OWNER(), ZERO()); assert!(state.owner().is_zero()); } @@ -269,24 +286,26 @@ fn test_renounceOwnership_from_zero_address() { #[should_panic(expected: ('Caller is not the owner',))] fn test_renounceOwnership_from_nonowner() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.renounceOwnership(); } #[test] fn test_full_two_step_transfer() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, OWNER()); state.transfer_ownership(OTHER()); - assert_event_ownership_transfer_started(OWNER(), OTHER()); + spy.assert_event_ownership_transfer_started(contract_address, OWNER(), OTHER()); assert_eq!(state.owner(), OWNER()); assert_eq!(state.pending_owner(), OTHER()); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(contract_address, OTHER()); state.accept_ownership(); - assert_only_event_ownership_transferred(ZERO(), OWNER(), OTHER()); + spy.assert_only_event_ownership_transferred(contract_address, OWNER(), OTHER()); assert_eq!(state.owner(), OTHER()); assert!(state.pending_owner().is_zero()); } @@ -294,22 +313,24 @@ fn test_full_two_step_transfer() { #[test] fn test_pending_accept_after_owner_renounce() { let mut state = setup(); - testing::set_caller_address(OWNER()); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, OWNER()); state.transfer_ownership(OTHER()); - assert_event_ownership_transfer_started(OWNER(), OTHER()); + spy.assert_event_ownership_transfer_started(contract_address, OWNER(), OTHER()); assert_eq!(state.owner(), OWNER()); assert_eq!(state.pending_owner(), OTHER()); state.renounce_ownership(); - assert_only_event_ownership_transferred(ZERO(), OWNER(), ZERO()); + spy.assert_only_event_ownership_transferred(contract_address, OWNER(), ZERO()); assert!(state.owner().is_zero()); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(contract_address, OTHER()); state.accept_ownership(); - assert_only_event_ownership_transferred(ZERO(), ZERO(), OTHER()); + spy.assert_only_event_ownership_transferred(contract_address, ZERO(), OTHER()); assert_eq!(state.owner(), OTHER()); assert!(state.pending_owner().is_zero()); } @@ -318,19 +339,17 @@ fn test_pending_accept_after_owner_renounce() { // Helpers // -fn assert_event_ownership_transfer_started( - previous_owner: ContractAddress, new_owner: ContractAddress -) { - let event = utils::pop_log::(ZERO()).unwrap(); - let expected = OwnableComponent::Event::OwnershipTransferStarted( - OwnershipTransferStarted { previous_owner, new_owner } - ); - assert!(event == expected); - utils::assert_no_events_left(ZERO()); - - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("OwnershipTransferStarted")); - indexed_keys.append_serde(previous_owner); - indexed_keys.append_serde(new_owner); - utils::assert_indexed_keys(event, indexed_keys.span()); +#[generate_trait] +impl TwoStepSpyHelpersImpl of TwoStepSpyHelpers { + fn assert_event_ownership_transfer_started( + ref self: EventSpy, + from_address: ContractAddress, + previous_owner: ContractAddress, + new_owner: ContractAddress + ) { + let expected = OwnableComponent::Event::OwnershipTransferStarted( + OwnershipTransferStarted { previous_owner, new_owner } + ); + self.assert_emitted_single(from_address, expected); + } } From 6f7130f629bc75cd52c4333bb654d1785dbeefe0 Mon Sep 17 00:00:00 2001 From: immrsd <103599616+immrsd@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:10:01 +0200 Subject: [PATCH 09/20] Migrate Upgrades tests (#1051) * Migrate Upgrades tests to Foundry * Update src/tests/upgrades/test_upgradeable.cairo Co-authored-by: Andrew Fleming * Update src/tests/upgrades/test_upgradeable.cairo Co-authored-by: Andrew Fleming --------- Co-authored-by: Eric Nordelo Co-authored-by: Andrew Fleming --- src/tests.cairo | 4 +- src/tests/upgrades/common.cairo | 24 +++++---- src/tests/upgrades/test_upgradeable.cairo | 60 ++++++++++++----------- src/tests/utils/constants.cairo | 1 + 4 files changed, 48 insertions(+), 41 deletions(-) diff --git a/src/tests.cairo b/src/tests.cairo index fcb9fb1dc..bd3cd67b5 100644 --- a/src/tests.cairo +++ b/src/tests.cairo @@ -13,7 +13,7 @@ mod mocks; mod security; #[cfg(test)] mod token; -// #[cfg(test)] -// mod upgrades; +#[cfg(test)] +mod upgrades; pub mod utils; diff --git a/src/tests/upgrades/common.cairo b/src/tests/upgrades/common.cairo index 0308fd45b..10876b756 100644 --- a/src/tests/upgrades/common.cairo +++ b/src/tests/upgrades/common.cairo @@ -1,16 +1,20 @@ -use openzeppelin::tests::utils::constants::ZERO; -use openzeppelin::tests::utils; +use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::upgrades::UpgradeableComponent::Upgraded; use openzeppelin::upgrades::UpgradeableComponent; +use snforge_std::{EventSpy, EventSpyAssertionsTrait}; use starknet::{ContractAddress, ClassHash}; -pub(crate) fn assert_event_upgraded(contract: ContractAddress, class_hash: ClassHash) { - let event = utils::pop_log::(contract).unwrap(); - let expected = UpgradeableComponent::Event::Upgraded(Upgraded { class_hash }); - assert!(event == expected); -} +#[generate_trait] +pub(crate) impl UpgradableSpyHelpersImpl of UpgradableSpyHelpers { + fn assert_event_upgraded(ref self: EventSpy, contract: ContractAddress, class_hash: ClassHash) { + let expected = UpgradeableComponent::Event::Upgraded(Upgraded { class_hash }); + self.assert_emitted_single(contract, expected); + } -pub(crate) fn assert_only_event_upgraded(contract: ContractAddress, class_hash: ClassHash) { - assert_event_upgraded(contract, class_hash); - utils::assert_no_events_left(ZERO()); + fn assert_only_event_upgraded( + ref self: EventSpy, contract: ContractAddress, class_hash: ClassHash + ) { + self.assert_event_upgraded(contract, class_hash); + self.assert_no_events_left_from(contract); + } } diff --git a/src/tests/upgrades/test_upgradeable.cairo b/src/tests/upgrades/test_upgradeable.cairo index c022e9c85..183f4f575 100644 --- a/src/tests/upgrades/test_upgradeable.cairo +++ b/src/tests/upgrades/test_upgradeable.cairo @@ -1,30 +1,26 @@ use openzeppelin::tests::mocks::upgrades_mocks::{ IUpgradesV1Dispatcher, IUpgradesV1DispatcherTrait, UpgradesV1 }; -use openzeppelin::tests::mocks::upgrades_mocks::{ - IUpgradesV2Dispatcher, IUpgradesV2DispatcherTrait, UpgradesV2 -}; -use openzeppelin::tests::utils::constants::{CLASS_HASH_ZERO, ZERO}; -use openzeppelin::tests::utils; +use openzeppelin::tests::mocks::upgrades_mocks::{IUpgradesV2Dispatcher, IUpgradesV2DispatcherTrait}; +use openzeppelin::tests::utils::common::{declare_class, deploy}; +use openzeppelin::tests::utils::constants::{CLASS_HASH_ZERO, ZERO, FELT_VALUE as VALUE}; +use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::upgrades::UpgradeableComponent; -use starknet::ClassHash; - -use super::common::assert_only_event_upgraded; +use snforge_std::ContractClass; +use snforge_std::{spy_events, EventSpy}; -const VALUE: felt252 = 123; - -fn V2_CLASS_HASH() -> ClassHash { - UpgradesV2::TEST_CLASS_HASH.try_into().unwrap() -} +use super::common::UpgradableSpyHelpers; // // Setup // -fn deploy_v1() -> IUpgradesV1Dispatcher { - let calldata = array![]; - let address = utils::deploy(UpgradesV1::TEST_CLASS_HASH, calldata); - IUpgradesV1Dispatcher { contract_address: address } +fn setup_test() -> (IUpgradesV1Dispatcher, ContractClass, ContractClass) { + let v1_class = declare_class("UpgradesV1"); + let v2_class = declare_class("UpgradesV2"); + let v1_contract_address = deploy(v1_class, array![]); + let v1 = IUpgradesV1Dispatcher { contract_address: v1_contract_address }; + (v1, v1_class, v2_class) } // @@ -32,25 +28,27 @@ fn deploy_v1() -> IUpgradesV1Dispatcher { // #[test] -#[should_panic(expected: ('Class hash cannot be zero', 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ('Class hash cannot be zero',))] fn test_upgrade_with_class_hash_zero() { - let v1 = deploy_v1(); + let (v1, _v1_class, _v2_class) = setup_test(); v1.upgrade(CLASS_HASH_ZERO()); } #[test] fn test_upgraded_event() { - let v1 = deploy_v1(); - v1.upgrade(V2_CLASS_HASH()); + let (v1, _v1_class, v2_class) = setup_test(); + let mut spy = spy_events(); - assert_only_event_upgraded(v1.contract_address, V2_CLASS_HASH()); + v1.upgrade(v2_class.class_hash); + + spy.assert_only_event_upgraded(v1.contract_address, v2_class.class_hash); } #[test] fn test_new_selector_after_upgrade() { - let v1 = deploy_v1(); + let (v1, _v1_class, v2_class) = setup_test(); - v1.upgrade(V2_CLASS_HASH()); + v1.upgrade(v2_class.class_hash); let v2 = IUpgradesV2Dispatcher { contract_address: v1.contract_address }; v2.set_value2(VALUE); @@ -59,10 +57,11 @@ fn test_new_selector_after_upgrade() { #[test] fn test_state_persists_after_upgrade() { - let v1 = deploy_v1(); + let (v1, _v1_class, v2_class) = setup_test(); + v1.set_value(VALUE); - v1.upgrade(V2_CLASS_HASH()); + v1.upgrade(v2_class.class_hash); let v2 = IUpgradesV2Dispatcher { contract_address: v1.contract_address }; assert_eq!(v2.get_value(), VALUE); @@ -70,15 +69,18 @@ fn test_state_persists_after_upgrade() { #[test] fn test_remove_selector_passes_in_v1() { - let v1 = deploy_v1(); + let (v1, _v1_class, _v2_class) = setup_test(); + v1.remove_selector(); } #[test] +#[ignore] // REASON: inconsistent ENTRYPOINT_NOT_FOUND panic message #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_remove_selector_fails_in_v2() { - let v1 = deploy_v1(); - v1.upgrade(V2_CLASS_HASH()); + let (v1, _v1_class, v2_class) = setup_test(); + + v1.upgrade(v2_class.class_hash); // We use the v1 dispatcher because remove_selector is not in v2 interface v1.remove_selector(); } diff --git a/src/tests/utils/constants.cairo b/src/tests/utils/constants.cairo index 8f29ca4f9..c84190c20 100644 --- a/src/tests/utils/constants.cairo +++ b/src/tests/utils/constants.cairo @@ -9,6 +9,7 @@ use starknet::secp256_trait::Secp256Trait; pub(crate) const DECIMALS: u8 = 18_u8; pub(crate) const SUPPLY: u256 = 2000; pub(crate) const VALUE: u256 = 300; +pub(crate) const FELT_VALUE: felt252 = 'FELT_VALUE'; pub(crate) const ROLE: felt252 = 'ROLE'; pub(crate) const OTHER_ROLE: felt252 = 'OTHER_ROLE'; pub(crate) const TOKEN_ID: u256 = 21; From 485024536e35a18980a95787648f503d92109a3b Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 18 Jul 2024 10:50:21 +0200 Subject: [PATCH 10/20] Migrate erc20 preset tests (#1055) * feat: include erc20 preset tests * feat: tests updated * feat: remove unused imports * feat: add deployments * Update src/tests/utils/common.cairo Co-authored-by: Andrew Fleming * feat: apply review updates --------- Co-authored-by: Andrew Fleming --- src/tests.cairo | 4 +- src/tests/access/test_dual_ownable.cairo | 6 +- src/tests/presets.cairo | 12 +- src/tests/presets/test_erc20.cairo | 288 ++++++++++++---------- src/tests/upgrades/test_upgradeable.cairo | 27 +- src/tests/utils.cairo | 5 +- src/tests/utils/common.cairo | 63 ++--- src/tests/utils/deployment.cairo | 46 ++++ 8 files changed, 253 insertions(+), 198 deletions(-) create mode 100644 src/tests/utils/deployment.cairo diff --git a/src/tests.cairo b/src/tests.cairo index bd3cd67b5..a3fa799ac 100644 --- a/src/tests.cairo +++ b/src/tests.cairo @@ -7,8 +7,8 @@ mod access; #[cfg(test)] mod introspection; mod mocks; -// #[cfg(test)] -// mod presets; +#[cfg(test)] +mod presets; #[cfg(test)] mod security; #[cfg(test)] diff --git a/src/tests/access/test_dual_ownable.cairo b/src/tests/access/test_dual_ownable.cairo index 8d24aaae7..16c22514f 100644 --- a/src/tests/access/test_dual_ownable.cairo +++ b/src/tests/access/test_dual_ownable.cairo @@ -65,7 +65,7 @@ fn test_dual_owner() { } #[test] -#[ignore] // REASON: inconsistent ENTRYPOINT_NOT_FOUND panic message +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_owner() { let dispatcher = setup_non_ownable(); @@ -94,7 +94,7 @@ fn test_dual_transfer_ownership() { } #[test] -#[ignore] // REASON: inconsistent ENTRYPOINT_NOT_FOUND panic message +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_transfer_ownership() { let dispatcher = setup_non_ownable(); @@ -119,7 +119,7 @@ fn test_dual_renounce_ownership() { } #[test] -#[ignore] // REASON: inconsistent ENTRYPOINT_NOT_FOUND panic message +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_renounce_ownership() { let dispatcher = setup_non_ownable(); diff --git a/src/tests/presets.cairo b/src/tests/presets.cairo index 87e7cb2f4..638696372 100644 --- a/src/tests/presets.cairo +++ b/src/tests/presets.cairo @@ -1,6 +1,8 @@ -mod test_account; -mod test_erc1155; +// mod test_account; +// mod test_erc1155; mod test_erc20; -mod test_erc721; -mod test_eth_account; -mod test_universal_deployer; +// mod test_erc721; +// mod test_eth_account; +// mod test_universal_deployer; + + diff --git a/src/tests/presets/test_erc20.cairo b/src/tests/presets/test_erc20.cairo index b9d7f8e00..a7ead420d 100644 --- a/src/tests/presets/test_erc20.cairo +++ b/src/tests/presets/test_erc20.cairo @@ -1,35 +1,35 @@ use core::integer::BoundedInt; use core::num::traits::Zero; -use openzeppelin::access::ownable::OwnableComponent::OwnershipTransferred; -use openzeppelin::presets::ERC20Upgradeable; +use openzeppelin::presets::interfaces::erc20::{ + ERC20UpgradeableABISafeDispatcher, ERC20UpgradeableABISafeDispatcherTrait +}; use openzeppelin::presets::interfaces::{ ERC20UpgradeableABIDispatcher, ERC20UpgradeableABIDispatcherTrait }; -use openzeppelin::tests::access::common::assert_event_ownership_transferred; -use openzeppelin::tests::mocks::erc20_mocks::SnakeERC20Mock; -use openzeppelin::tests::token::erc20::common::{ - assert_event_approval, assert_only_event_approval, assert_only_event_transfer -}; -use openzeppelin::tests::upgrades::common::assert_only_event_upgraded; +use openzeppelin::tests::access::common::OwnableSpyHelpers; +use openzeppelin::tests::token::erc20::common::ERC20SpyHelpers; +use openzeppelin::tests::upgrades::common::UpgradableSpyHelpers; +use openzeppelin::tests::utils::common::IntoBase16String; use openzeppelin::tests::utils::constants::{ ZERO, OWNER, SPENDER, RECIPIENT, OTHER, NAME, SYMBOL, DECIMALS, SUPPLY, VALUE, CLASS_HASH_ZERO }; +use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::tests::utils; use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::{spy_events, EventSpy, start_cheat_caller_address}; use starknet::ClassHash; -use starknet::testing; fn V2_CLASS_HASH() -> ClassHash { - SnakeERC20Mock::TEST_CLASS_HASH.try_into().unwrap() + utils::declare_class("SnakeERC20Mock").class_hash } // // Setup // -fn setup_dispatcher_with_event() -> ERC20UpgradeableABIDispatcher { +fn setup_dispatcher_with_event() -> (EventSpy, ERC20UpgradeableABIDispatcher) { let mut calldata = array![]; calldata.append_serde(NAME()); @@ -38,15 +38,15 @@ fn setup_dispatcher_with_event() -> ERC20UpgradeableABIDispatcher { calldata.append_serde(OWNER()); calldata.append_serde(OWNER()); - let address = utils::deploy(ERC20Upgradeable::TEST_CLASS_HASH, calldata); - ERC20UpgradeableABIDispatcher { contract_address: address } + let spy = spy_events(); + let address = utils::declare_and_deploy("ERC20Upgradeable", calldata); + (spy, ERC20UpgradeableABIDispatcher { contract_address: address }) } -fn setup_dispatcher() -> ERC20UpgradeableABIDispatcher { - let dispatcher = setup_dispatcher_with_event(); - utils::drop_event(dispatcher.contract_address); // Ownable `OwnershipTransferred` - utils::drop_event(dispatcher.contract_address); // ERC20 `Transfer` - dispatcher +fn setup_dispatcher() -> (EventSpy, ERC20UpgradeableABIDispatcher) { + let (mut spy, dispatcher) = setup_dispatcher_with_event(); + spy.drop_all_events(); + (spy, dispatcher) } // @@ -55,17 +55,17 @@ fn setup_dispatcher() -> ERC20UpgradeableABIDispatcher { #[test] fn test_constructor() { - let mut dispatcher = setup_dispatcher_with_event(); + let (mut spy, dispatcher) = setup_dispatcher_with_event(); assert_eq!(dispatcher.owner(), OWNER()); - assert_event_ownership_transferred(dispatcher.contract_address, ZERO(), OWNER()); + spy.assert_event_ownership_transferred(dispatcher.contract_address, ZERO(), OWNER()); assert_eq!(dispatcher.name(), NAME()); assert_eq!(dispatcher.symbol(), SYMBOL()); assert_eq!(dispatcher.decimals(), DECIMALS); assert_eq!(dispatcher.total_supply(), SUPPLY); assert_eq!(dispatcher.balance_of(OWNER()), SUPPLY); - assert_only_event_transfer(dispatcher.contract_address, ZERO(), OWNER(), SUPPLY); + spy.assert_only_event_transfer(dispatcher.contract_address, ZERO(), OWNER(), SUPPLY); } // @@ -74,7 +74,7 @@ fn test_constructor() { #[test] fn test_total_supply() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); assert_eq!(dispatcher.total_supply(), SUPPLY); assert_eq!(dispatcher.totalSupply(), SUPPLY); @@ -82,7 +82,7 @@ fn test_total_supply() { #[test] fn test_balance_of() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); assert_eq!(dispatcher.balance_of(OWNER()), SUPPLY); assert_eq!(dispatcher.balanceOf(OWNER()), SUPPLY); @@ -90,9 +90,9 @@ fn test_balance_of() { #[test] fn test_allowance() { - let mut dispatcher = setup_dispatcher(); + let (_, mut dispatcher) = setup_dispatcher(); - testing::set_contract_address(OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.approve(SPENDER(), VALUE); let allowance = dispatcher.allowance(OWNER(), SPENDER()); @@ -105,31 +105,32 @@ fn test_allowance() { #[test] fn test_approve() { - let mut dispatcher = setup_dispatcher(); + let (mut spy, mut dispatcher) = setup_dispatcher(); let allowance = dispatcher.allowance(OWNER(), SPENDER()); assert!(allowance.is_zero()); - testing::set_contract_address(OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); assert!(dispatcher.approve(SPENDER(), VALUE)); let allowance = dispatcher.allowance(OWNER(), SPENDER()); assert_eq!(allowance, VALUE); - assert_only_event_approval(dispatcher.contract_address, OWNER(), SPENDER(), VALUE); + spy.assert_only_event_approval(dispatcher.contract_address, OWNER(), SPENDER(), VALUE); } #[test] -#[should_panic(expected: ('ERC20: approve from 0', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC20: approve from 0',))] fn test_approve_from_zero() { - let mut dispatcher = setup_dispatcher(); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, ZERO()); dispatcher.approve(SPENDER(), VALUE); } #[test] -#[should_panic(expected: ('ERC20: approve to 0', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC20: approve to 0',))] fn test_approve_to_zero() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.approve(Zero::zero(), VALUE); } @@ -139,40 +140,41 @@ fn test_approve_to_zero() { #[test] fn test_transfer() { - let mut dispatcher = setup_dispatcher(); + let (mut spy, mut dispatcher) = setup_dispatcher(); - testing::set_contract_address(OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); assert!(dispatcher.transfer(RECIPIENT(), VALUE)); assert_eq!(dispatcher.balance_of(OWNER()), SUPPLY - VALUE); assert_eq!(dispatcher.balance_of(RECIPIENT()), VALUE); assert_eq!(dispatcher.total_supply(), SUPPLY); - assert_only_event_transfer(dispatcher.contract_address, OWNER(), RECIPIENT(), VALUE); + spy.assert_only_event_transfer(dispatcher.contract_address, OWNER(), RECIPIENT(), VALUE); } #[test] -#[should_panic(expected: ('ERC20: insufficient balance', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC20: insufficient balance',))] fn test_transfer_not_enough_balance() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); let balance_plus_one = SUPPLY + 1; dispatcher.transfer(RECIPIENT(), balance_plus_one); } #[test] -#[should_panic(expected: ('ERC20: transfer from 0', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC20: transfer from 0',))] fn test_transfer_from_zero() { - let mut dispatcher = setup_dispatcher(); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, ZERO()); dispatcher.transfer(RECIPIENT(), VALUE); } #[test] -#[should_panic(expected: ('ERC20: transfer to 0', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC20: transfer to 0',))] fn test_transfer_to_zero() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.transfer(ZERO(), VALUE); } @@ -182,17 +184,17 @@ fn test_transfer_to_zero() { #[test] fn test_transfer_from() { - let mut dispatcher = setup_dispatcher(); + let (mut spy, mut dispatcher) = setup_dispatcher(); - testing::set_contract_address(OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.approve(SPENDER(), VALUE); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(SPENDER()); + start_cheat_caller_address(dispatcher.contract_address, SPENDER()); assert!(dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE)); - assert_event_approval(dispatcher.contract_address, OWNER(), SPENDER(), 0); - assert_only_event_transfer(dispatcher.contract_address, OWNER(), RECIPIENT(), VALUE); + spy.assert_event_approval(dispatcher.contract_address, OWNER(), SPENDER(), 0); + spy.assert_only_event_transfer(dispatcher.contract_address, OWNER(), RECIPIENT(), VALUE); assert_eq!(dispatcher.balance_of(RECIPIENT()), VALUE); assert_eq!(dispatcher.balance_of(OWNER()), SUPPLY - VALUE); @@ -202,12 +204,12 @@ fn test_transfer_from() { #[test] fn test_transfer_from_doesnt_consume_infinite_allowance() { - let mut dispatcher = setup_dispatcher(); + let (_, mut dispatcher) = setup_dispatcher(); - testing::set_contract_address(OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.approve(SPENDER(), BoundedInt::max()); - testing::set_contract_address(SPENDER()); + start_cheat_caller_address(dispatcher.contract_address, SPENDER()); dispatcher.transfer_from(OWNER(), RECIPIENT(), VALUE); let allowance = dispatcher.allowance(OWNER(), SPENDER()); @@ -215,48 +217,48 @@ fn test_transfer_from_doesnt_consume_infinite_allowance() { } #[test] -#[should_panic(expected: ('ERC20: insufficient allowance', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC20: insufficient allowance',))] fn test_transfer_from_greater_than_allowance() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.approve(SPENDER(), VALUE); - testing::set_contract_address(SPENDER()); + start_cheat_caller_address(dispatcher.contract_address, SPENDER()); let allowance_plus_one = VALUE + 1; dispatcher.transfer_from(OWNER(), RECIPIENT(), allowance_plus_one); } #[test] -#[should_panic(expected: ('ERC20: transfer to 0', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC20: transfer to 0',))] fn test_transfer_from_to_zero_address() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.approve(SPENDER(), VALUE); - testing::set_contract_address(SPENDER()); + start_cheat_caller_address(dispatcher.contract_address, SPENDER()); dispatcher.transfer_from(OWNER(), Zero::zero(), VALUE); } #[test] -#[should_panic(expected: ('ERC20: insufficient allowance', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC20: insufficient allowance',))] fn test_transfer_from_from_zero_address() { - let mut dispatcher = setup_dispatcher(); + let (_, mut dispatcher) = setup_dispatcher(); dispatcher.transfer_from(Zero::zero(), RECIPIENT(), VALUE); } #[test] fn test_transferFrom() { - let mut dispatcher = setup_dispatcher(); + let (mut spy, mut dispatcher) = setup_dispatcher(); - testing::set_contract_address(OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.approve(SPENDER(), VALUE); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(SPENDER()); + start_cheat_caller_address(dispatcher.contract_address, SPENDER()); assert!(dispatcher.transferFrom(OWNER(), RECIPIENT(), VALUE)); - assert_event_approval(dispatcher.contract_address, OWNER(), SPENDER(), 0); - assert_only_event_transfer(dispatcher.contract_address, OWNER(), RECIPIENT(), VALUE); + spy.assert_event_approval(dispatcher.contract_address, OWNER(), SPENDER(), 0); + spy.assert_only_event_transfer(dispatcher.contract_address, OWNER(), RECIPIENT(), VALUE); assert_eq!(dispatcher.balance_of(RECIPIENT()), VALUE); assert_eq!(dispatcher.balance_of(OWNER()), SUPPLY - VALUE); @@ -266,11 +268,11 @@ fn test_transferFrom() { #[test] fn test_transferFrom_doesnt_consume_infinite_allowance() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.approve(SPENDER(), BoundedInt::max()); - testing::set_contract_address(SPENDER()); + start_cheat_caller_address(dispatcher.contract_address, SPENDER()); dispatcher.transferFrom(OWNER(), RECIPIENT(), VALUE); let allowance = dispatcher.allowance(OWNER(), SPENDER()); @@ -278,32 +280,32 @@ fn test_transferFrom_doesnt_consume_infinite_allowance() { } #[test] -#[should_panic(expected: ('ERC20: insufficient allowance', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC20: insufficient allowance',))] fn test_transferFrom_greater_than_allowance() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.approve(SPENDER(), VALUE); - testing::set_contract_address(SPENDER()); + start_cheat_caller_address(dispatcher.contract_address, SPENDER()); let allowance_plus_one = VALUE + 1; dispatcher.transferFrom(OWNER(), RECIPIENT(), allowance_plus_one); } #[test] -#[should_panic(expected: ('ERC20: transfer to 0', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC20: transfer to 0',))] fn test_transferFrom_to_zero_address() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.approve(SPENDER(), VALUE); - testing::set_contract_address(SPENDER()); + start_cheat_caller_address(dispatcher.contract_address, SPENDER()); dispatcher.transferFrom(OWNER(), Zero::zero(), VALUE); } #[test] -#[should_panic(expected: ('ERC20: insufficient allowance', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC20: insufficient allowance',))] fn test_transferFrom_from_zero_address() { - let mut dispatcher = setup_dispatcher(); + let (_, mut dispatcher) = setup_dispatcher(); dispatcher.transferFrom(Zero::zero(), RECIPIENT(), VALUE); } @@ -313,67 +315,69 @@ fn test_transferFrom_from_zero_address() { #[test] fn test_transfer_ownership() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (mut spy, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.transfer_ownership(OTHER()); - assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), OTHER()); + spy.assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), OTHER()); assert_eq!(dispatcher.owner(), OTHER()); } #[test] -#[should_panic(expected: ('New owner is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('New owner is the zero address',))] fn test_transfer_ownership_to_zero() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.transfer_ownership(ZERO()); } #[test] -#[should_panic(expected: ('Caller is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_transfer_ownership_from_zero() { - let mut dispatcher = setup_dispatcher(); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, ZERO()); dispatcher.transfer_ownership(OTHER()); } #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_transfer_ownership_from_nonowner() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.transfer_ownership(OTHER()); } #[test] fn test_transferOwnership() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (mut spy, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.transferOwnership(OTHER()); - assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), OTHER()); + spy.assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), OTHER()); assert_eq!(dispatcher.owner(), OTHER()); } #[test] -#[should_panic(expected: ('New owner is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('New owner is the zero address',))] fn test_transferOwnership_to_zero() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.transferOwnership(ZERO()); } #[test] -#[should_panic(expected: ('Caller is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_transferOwnership_from_zero() { - let mut dispatcher = setup_dispatcher(); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, ZERO()); dispatcher.transferOwnership(OTHER()); } #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_transferOwnership_from_nonowner() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.transferOwnership(OTHER()); } @@ -383,51 +387,53 @@ fn test_transferOwnership_from_nonowner() { #[test] fn test_renounce_ownership() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (mut spy, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.renounce_ownership(); - assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), ZERO()); + spy.assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), ZERO()); assert!(dispatcher.owner().is_zero()); } #[test] -#[should_panic(expected: ('Caller is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_renounce_ownership_from_zero_address() { - let mut dispatcher = setup_dispatcher(); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, ZERO()); dispatcher.renounce_ownership(); } #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_renounce_ownership_from_nonowner() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.renounce_ownership(); } #[test] fn test_renounceOwnership() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (mut spy, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.renounceOwnership(); - assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), ZERO()); + spy.assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), ZERO()); assert!(dispatcher.owner().is_zero()); } #[test] -#[should_panic(expected: ('Caller is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_renounceOwnership_from_zero_address() { - let mut dispatcher = setup_dispatcher(); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, ZERO()); dispatcher.renounceOwnership(); } #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_renounceOwnership_from_nonowner() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.renounceOwnership(); } @@ -436,52 +442,62 @@ fn test_renounceOwnership_from_nonowner() { // #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_upgrade_unauthorized() { - let v1 = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, mut v1) = setup_dispatcher(); + start_cheat_caller_address(v1.contract_address, OTHER()); v1.upgrade(CLASS_HASH_ZERO()); } #[test] -#[should_panic(expected: ('Class hash cannot be zero', 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ('Class hash cannot be zero',))] fn test_upgrade_with_class_hash_zero() { - let v1 = setup_dispatcher(); + let (_, mut v1) = setup_dispatcher(); - testing::set_contract_address(OWNER()); + start_cheat_caller_address(v1.contract_address, OWNER()); v1.upgrade(CLASS_HASH_ZERO()); } #[test] fn test_upgraded_event() { - let v1 = setup_dispatcher(); + let (mut spy, mut v1) = setup_dispatcher(); let v2_class_hash = V2_CLASS_HASH(); - testing::set_contract_address(OWNER()); + start_cheat_caller_address(v1.contract_address, OWNER()); v1.upgrade(v2_class_hash); - assert_only_event_upgraded(v1.contract_address, v2_class_hash); + spy.assert_only_event_upgraded(v1.contract_address, v2_class_hash); } #[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +#[feature("safe_dispatcher")] fn test_v2_missing_camel_selector() { - let v1 = setup_dispatcher(); + let (_, mut v1) = setup_dispatcher(); let v2_class_hash = V2_CLASS_HASH(); - testing::set_contract_address(OWNER()); + start_cheat_caller_address(v1.contract_address, OWNER()); v1.upgrade(v2_class_hash); - let dispatcher = IERC20CamelDispatcher { contract_address: v1.contract_address }; - dispatcher.totalSupply(); + let safe_dispatcher = ERC20UpgradeableABISafeDispatcher { + contract_address: v1.contract_address + }; + let mut panic_data = safe_dispatcher.totalSupply().unwrap_err(); + + let selector = selector!("totalSupply"); + let expected_panic_message = format!( + "Entry point selector {} not found in contract {}", + selector.into_base_16_string(), + v1.contract_address.into_base_16_string() + ); + assert_eq!(utils::panic_data_to_byte_array(panic_data), expected_panic_message); } #[test] fn test_state_persists_after_upgrade() { - let v1 = setup_dispatcher(); + let (_, mut v1) = setup_dispatcher(); let v2_class_hash = V2_CLASS_HASH(); - testing::set_contract_address(OWNER()); + start_cheat_caller_address(v1.contract_address, OWNER()); v1.transfer(RECIPIENT(), VALUE); // Check RECIPIENT balance v1 diff --git a/src/tests/upgrades/test_upgradeable.cairo b/src/tests/upgrades/test_upgradeable.cairo index 183f4f575..867aad502 100644 --- a/src/tests/upgrades/test_upgradeable.cairo +++ b/src/tests/upgrades/test_upgradeable.cairo @@ -1,13 +1,10 @@ -use openzeppelin::tests::mocks::upgrades_mocks::{ - IUpgradesV1Dispatcher, IUpgradesV1DispatcherTrait, UpgradesV1 -}; +use openzeppelin::tests::mocks::upgrades_mocks::{IUpgradesV1Dispatcher, IUpgradesV1DispatcherTrait}; use openzeppelin::tests::mocks::upgrades_mocks::{IUpgradesV2Dispatcher, IUpgradesV2DispatcherTrait}; -use openzeppelin::tests::utils::common::{declare_class, deploy}; use openzeppelin::tests::utils::constants::{CLASS_HASH_ZERO, ZERO, FELT_VALUE as VALUE}; use openzeppelin::tests::utils::events::EventSpyExt; +use openzeppelin::tests::utils::{declare_class, deploy}; use openzeppelin::upgrades::UpgradeableComponent; -use snforge_std::ContractClass; -use snforge_std::{spy_events, EventSpy}; +use snforge_std::{spy_events, ContractClass}; use super::common::UpgradableSpyHelpers; @@ -15,12 +12,12 @@ use super::common::UpgradableSpyHelpers; // Setup // -fn setup_test() -> (IUpgradesV1Dispatcher, ContractClass, ContractClass) { +fn setup_test() -> (IUpgradesV1Dispatcher, ContractClass) { let v1_class = declare_class("UpgradesV1"); let v2_class = declare_class("UpgradesV2"); let v1_contract_address = deploy(v1_class, array![]); let v1 = IUpgradesV1Dispatcher { contract_address: v1_contract_address }; - (v1, v1_class, v2_class) + (v1, v2_class) } // @@ -30,13 +27,13 @@ fn setup_test() -> (IUpgradesV1Dispatcher, ContractClass, ContractClass) { #[test] #[should_panic(expected: ('Class hash cannot be zero',))] fn test_upgrade_with_class_hash_zero() { - let (v1, _v1_class, _v2_class) = setup_test(); + let (v1, _) = setup_test(); v1.upgrade(CLASS_HASH_ZERO()); } #[test] fn test_upgraded_event() { - let (v1, _v1_class, v2_class) = setup_test(); + let (v1, v2_class) = setup_test(); let mut spy = spy_events(); v1.upgrade(v2_class.class_hash); @@ -46,7 +43,7 @@ fn test_upgraded_event() { #[test] fn test_new_selector_after_upgrade() { - let (v1, _v1_class, v2_class) = setup_test(); + let (v1, v2_class) = setup_test(); v1.upgrade(v2_class.class_hash); let v2 = IUpgradesV2Dispatcher { contract_address: v1.contract_address }; @@ -57,7 +54,7 @@ fn test_new_selector_after_upgrade() { #[test] fn test_state_persists_after_upgrade() { - let (v1, _v1_class, v2_class) = setup_test(); + let (v1, v2_class) = setup_test(); v1.set_value(VALUE); @@ -69,16 +66,16 @@ fn test_state_persists_after_upgrade() { #[test] fn test_remove_selector_passes_in_v1() { - let (v1, _v1_class, _v2_class) = setup_test(); + let (v1, _) = setup_test(); v1.remove_selector(); } #[test] -#[ignore] // REASON: inconsistent ENTRYPOINT_NOT_FOUND panic message +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_remove_selector_fails_in_v2() { - let (v1, _v1_class, v2_class) = setup_test(); + let (v1, v2_class) = setup_test(); v1.upgrade(v2_class.class_hash); // We use the v1 dispatcher because remove_selector is not in v2 interface diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index 64abc3023..1c31f8a61 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -1,8 +1,11 @@ pub(crate) mod common; pub(crate) mod constants; +pub(crate) mod deployment; pub(crate) mod events; pub(crate) mod signing; -pub use common::{ + +pub use common::{panic_data_to_byte_array, to_base_16_string, IntoBase16StringTrait}; +pub use deployment::{ declare_class, declare_and_deploy, declare_and_deploy_at, deploy, deploy_at, deploy_another_at }; diff --git a/src/tests/utils/common.cairo b/src/tests/utils/common.cairo index c5920b969..c48a85344 100644 --- a/src/tests/utils/common.cairo +++ b/src/tests/utils/common.cairo @@ -1,45 +1,36 @@ -use core::starknet::SyscallResultTrait; -use snforge_std::{declare, get_class_hash, ContractClass, ContractClassTrait}; -use snforge_std::{start_cheat_caller_address, stop_cheat_caller_address}; -use starknet::ContractAddress; +use core::to_byte_array::FormatAsByteArray; -pub fn deploy(contract_class: ContractClass, calldata: Array) -> ContractAddress { - match contract_class.deploy(@calldata) { - Result::Ok((contract_address, _)) => contract_address, - Result::Err(panic_data) => panic!("Failed to deploy, error: ${:?}", panic_data) - } -} +/// Converts panic data into a string (ByteArray). +/// +/// panic_data is expected to be a valid serialized byte array with an extra +/// felt252 at the beginning, which is the BYTE_ARRAY_MAGIC. +pub fn panic_data_to_byte_array(panic_data: Array) -> ByteArray { + let mut panic_data = panic_data.span(); -pub fn deploy_at( - contract_class: ContractClass, contract_address: ContractAddress, calldata: Array -) { - match contract_class.deploy_at(@calldata, contract_address) { - Result::Ok(_) => (), - Result::Err(panic_data) => panic!("Failed to deploy, error: ${:?}", panic_data) - }; -} + // Remove BYTE_ARRAY_MAGIC from the panic data. + panic_data.pop_front().unwrap(); -/// Deploys a contract from the class hash of another contract which is already deployed. -pub fn deploy_another_at( - existing: ContractAddress, target_address: ContractAddress, calldata: Array -) { - let class_hash = get_class_hash(existing); - let contract_class = ContractClassTrait::new(class_hash); - deploy_at(contract_class, target_address, calldata) + match Serde::::deserialize(ref panic_data) { + Option::Some(string) => string, + Option::None => panic!("Failed to deserialize panic data."), + } } -pub fn declare_class(contract_name: ByteArray) -> ContractClass { - declare(contract_name).unwrap_syscall() -} +/// Converts a felt252 to a base 16 string padded to 66 characters including the `0x` prefix. +pub fn to_base_16_string(value: felt252) -> ByteArray { + let mut string = value.format_as_byte_array(16); + let mut padding = 64 - string.len(); -pub fn declare_and_deploy(contract_name: ByteArray, calldata: Array) -> ContractAddress { - let contract_class = declare(contract_name).unwrap_syscall(); - deploy(contract_class, calldata) + while padding > 0 { + string = "0" + string; + padding -= 1; + }; + format!("0x{}", string) } -pub fn declare_and_deploy_at( - contract_name: ByteArray, target_address: ContractAddress, calldata: Array -) { - let contract_class = declare(contract_name).unwrap_syscall(); - deploy_at(contract_class, target_address, calldata) +#[generate_trait] +pub impl IntoBase16String> of IntoBase16StringTrait { + fn into_base_16_string(self: T) -> ByteArray { + to_base_16_string(self.into()) + } } diff --git a/src/tests/utils/deployment.cairo b/src/tests/utils/deployment.cairo new file mode 100644 index 000000000..dae9a2071 --- /dev/null +++ b/src/tests/utils/deployment.cairo @@ -0,0 +1,46 @@ +use core::starknet::SyscallResultTrait; +use openzeppelin::tests::utils::panic_data_to_byte_array; +use snforge_std::{declare, get_class_hash, ContractClass, ContractClassTrait}; +use snforge_std::{start_cheat_caller_address, stop_cheat_caller_address}; +use starknet::ContractAddress; + +pub fn deploy(contract_class: ContractClass, calldata: Array) -> ContractAddress { + match contract_class.deploy(@calldata) { + Result::Ok((contract_address, _)) => contract_address, + Result::Err(panic_data) => panic!("{}", panic_data_to_byte_array(panic_data)) + } +} + +pub fn deploy_at( + contract_class: ContractClass, contract_address: ContractAddress, calldata: Array +) { + match contract_class.deploy_at(@calldata, contract_address) { + Result::Ok(_) => (), + Result::Err(panic_data) => panic!("{}", panic_data_to_byte_array(panic_data)) + }; +} + +/// Deploys a contract from the class hash of another contract which is already deployed. +pub fn deploy_another_at( + existing: ContractAddress, target_address: ContractAddress, calldata: Array +) { + let class_hash = get_class_hash(existing); + let contract_class = ContractClassTrait::new(class_hash); + deploy_at(contract_class, target_address, calldata) +} + +pub fn declare_class(contract_name: ByteArray) -> ContractClass { + declare(contract_name).unwrap_syscall() +} + +pub fn declare_and_deploy(contract_name: ByteArray, calldata: Array) -> ContractAddress { + let contract_class = declare(contract_name).unwrap_syscall(); + deploy(contract_class, calldata) +} + +pub fn declare_and_deploy_at( + contract_name: ByteArray, target_address: ContractAddress, calldata: Array +) { + let contract_class = declare(contract_name).unwrap_syscall(); + deploy_at(contract_class, target_address, calldata) +} From 78aa934cb4235d02e2f30eb49e48d04a48b890d8 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 19 Jul 2024 12:33:50 +0200 Subject: [PATCH 11/20] feat: update ignored tests messages (#1056) --- src/tests/presets.cairo | 2 +- src/tests/presets/test_erc20.cairo | 3 +- src/tests/presets/test_erc721.cairo | 518 +++++++++++----------- src/tests/upgrades/common.cairo | 2 +- src/tests/upgrades/test_upgradeable.cairo | 2 +- 5 files changed, 270 insertions(+), 257 deletions(-) diff --git a/src/tests/presets.cairo b/src/tests/presets.cairo index 638696372..feb573ea8 100644 --- a/src/tests/presets.cairo +++ b/src/tests/presets.cairo @@ -1,7 +1,7 @@ // mod test_account; // mod test_erc1155; mod test_erc20; -// mod test_erc721; +mod test_erc721; // mod test_eth_account; // mod test_universal_deployer; diff --git a/src/tests/presets/test_erc20.cairo b/src/tests/presets/test_erc20.cairo index a7ead420d..2625ac249 100644 --- a/src/tests/presets/test_erc20.cairo +++ b/src/tests/presets/test_erc20.cairo @@ -8,14 +8,13 @@ use openzeppelin::presets::interfaces::{ }; use openzeppelin::tests::access::common::OwnableSpyHelpers; use openzeppelin::tests::token::erc20::common::ERC20SpyHelpers; -use openzeppelin::tests::upgrades::common::UpgradableSpyHelpers; +use openzeppelin::tests::upgrades::common::UpgradeableSpyHelpers; use openzeppelin::tests::utils::common::IntoBase16String; use openzeppelin::tests::utils::constants::{ ZERO, OWNER, SPENDER, RECIPIENT, OTHER, NAME, SYMBOL, DECIMALS, SUPPLY, VALUE, CLASS_HASH_ZERO }; use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::tests::utils; -use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use openzeppelin::utils::serde::SerializedAppend; use snforge_std::{spy_events, EventSpy, start_cheat_caller_address}; diff --git a/src/tests/presets/test_erc721.cairo b/src/tests/presets/test_erc721.cairo index bf1d27d14..a84bb7cce 100644 --- a/src/tests/presets/test_erc721.cairo +++ b/src/tests/presets/test_erc721.cairo @@ -5,32 +5,24 @@ use openzeppelin::presets::ERC721Upgradeable; use openzeppelin::presets::interfaces::{ ERC721UpgradeableABIDispatcher, ERC721UpgradeableABIDispatcherTrait }; -use openzeppelin::tests::access::common::assert_event_ownership_transferred; -use openzeppelin::tests::mocks::account_mocks::{DualCaseAccountMock, CamelAccountMock}; -use openzeppelin::tests::mocks::erc721_mocks::SnakeERC721Mock; -use openzeppelin::tests::mocks::erc721_receiver_mocks::{ - CamelERC721ReceiverMock, SnakeERC721ReceiverMock -}; -use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; -use openzeppelin::tests::token::erc721::common::{ - assert_event_transfer, assert_only_event_transfer, assert_event_approval, - assert_event_approval_for_all -}; -use openzeppelin::tests::upgrades::common::assert_only_event_upgraded; +use openzeppelin::tests::access::common::OwnableSpyHelpers; +use openzeppelin::tests::token::erc721::common::ERC721SpyHelpers; +use openzeppelin::tests::upgrades::common::UpgradeableSpyHelpers; +use openzeppelin::tests::utils::common::IntoBase16String; use openzeppelin::tests::utils::constants::{ ZERO, DATA, OWNER, SPENDER, RECIPIENT, OTHER, OPERATOR, CLASS_HASH_ZERO, PUBKEY, NAME, SYMBOL, BASE_URI }; +use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::tests::utils; use openzeppelin::token::erc721::ERC721Component::ERC721Impl; -use openzeppelin::token::erc721::ERC721Component; use openzeppelin::token::erc721::interface::{ - IERC721CamelOnlyDispatcher, IERC721CamelOnlyDispatcherTrait + IERC721CamelOnlySafeDispatcher, IERC721CamelOnlySafeDispatcherTrait }; use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait}; use openzeppelin::token::erc721::interface::{IERC721_ID, IERC721_METADATA_ID}; use openzeppelin::utils::serde::SerializedAppend; -use starknet::testing; +use snforge_std::{spy_events, EventSpy, start_cheat_caller_address}; use starknet::{ContractAddress, ClassHash}; @@ -43,20 +35,17 @@ const NONEXISTENT: u256 = 9898; const TOKENS_LEN: u256 = 3; fn V2_CLASS_HASH() -> ClassHash { - SnakeERC721Mock::TEST_CLASS_HASH.try_into().unwrap() + utils::declare_class("SnakeERC721Mock").class_hash } // // Setup // -fn setup_dispatcher_with_event() -> ERC721UpgradeableABIDispatcher { +fn setup_dispatcher_with_event() -> (EventSpy, ERC721UpgradeableABIDispatcher) { let mut calldata = array![]; let mut token_ids = array![TOKEN_1, TOKEN_2, TOKEN_3]; - // Set caller as `OWNER` - testing::set_contract_address(OWNER()); - calldata.append_serde(NAME()); calldata.append_serde(SYMBOL()); calldata.append_serde(BASE_URI()); @@ -64,33 +53,34 @@ fn setup_dispatcher_with_event() -> ERC721UpgradeableABIDispatcher { calldata.append_serde(token_ids); calldata.append_serde(OWNER()); - let address = utils::deploy(ERC721Upgradeable::TEST_CLASS_HASH, calldata); - ERC721UpgradeableABIDispatcher { contract_address: address } + let spy = spy_events(); + let address = utils::declare_and_deploy("ERC721Upgradeable", calldata); + start_cheat_caller_address(address, OWNER()); + (spy, ERC721UpgradeableABIDispatcher { contract_address: address }) } -fn setup_dispatcher() -> ERC721UpgradeableABIDispatcher { - let dispatcher = setup_dispatcher_with_event(); - // `OwnershipTransferred` + `Transfer`s - utils::drop_events(dispatcher.contract_address, TOKENS_LEN.try_into().unwrap() + 1); - dispatcher +fn setup_dispatcher() -> (EventSpy, ERC721UpgradeableABIDispatcher) { + let (mut spy, dispatcher) = setup_dispatcher_with_event(); + spy.drop_all_events(); + (spy, dispatcher) } fn setup_receiver() -> ContractAddress { - utils::deploy(SnakeERC721ReceiverMock::TEST_CLASS_HASH, array![]) + utils::declare_and_deploy("SnakeERC721ReceiverMock", array![]) } fn setup_camel_receiver() -> ContractAddress { - utils::deploy(CamelERC721ReceiverMock::TEST_CLASS_HASH, array![]) + utils::declare_and_deploy("CamelERC721ReceiverMock", array![]) } fn setup_account() -> ContractAddress { let mut calldata = array![PUBKEY]; - utils::deploy(DualCaseAccountMock::TEST_CLASS_HASH, calldata) + utils::declare_and_deploy("DualCaseAccountMock", calldata) } fn setup_camel_account() -> ContractAddress { let mut calldata = array![PUBKEY]; - utils::deploy(CamelAccountMock::TEST_CLASS_HASH, calldata) + utils::declare_and_deploy("CamelAccountMock", calldata) } // @@ -120,7 +110,7 @@ fn test_mint_assets() { #[test] fn test_constructor() { - let dispatcher = setup_dispatcher_with_event(); + let (_, dispatcher) = setup_dispatcher_with_event(); // Check interface registration let mut interface_ids = array![ISRC5_ID, IERC721_ID, IERC721_METADATA_ID]; @@ -149,18 +139,18 @@ fn test_constructor() { #[test] fn test_constructor_events() { - let dispatcher = setup_dispatcher_with_event(); + let (mut spy, dispatcher) = setup_dispatcher_with_event(); let mut tokens = array![TOKEN_1, TOKEN_2, TOKEN_3]; - assert_event_ownership_transferred(dispatcher.contract_address, ZERO(), OWNER()); + spy.assert_event_ownership_transferred(dispatcher.contract_address, ZERO(), OWNER()); loop { let token = tokens.pop_front().unwrap(); if tokens.len() == 0 { // Includes event queue check - assert_only_event_transfer(dispatcher.contract_address, ZERO(), OWNER(), token); + spy.assert_only_event_transfer(dispatcher.contract_address, ZERO(), OWNER(), token); break; } - assert_event_transfer(dispatcher.contract_address, ZERO(), OWNER(), token); + spy.assert_event_transfer(dispatcher.contract_address, ZERO(), OWNER(), token); }; } @@ -170,40 +160,40 @@ fn test_constructor_events() { #[test] fn test_balance_of() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); assert_eq!(dispatcher.balance_of(OWNER()), TOKENS_LEN); } #[test] -#[should_panic(expected: ('ERC721: invalid account', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: invalid account',))] fn test_balance_of_zero() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); dispatcher.balance_of(ZERO()); } #[test] fn test_owner_of() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); assert_eq!(dispatcher.owner_of(TOKEN_1), OWNER()); } #[test] -#[should_panic(expected: ('ERC721: invalid token ID', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_owner_of_non_minted() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); dispatcher.owner_of(7); } #[test] -#[should_panic(expected: ('ERC721: invalid token ID', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_token_uri_non_minted() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); dispatcher.token_uri(7); } #[test] fn test_token_uri() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); let uri = dispatcher.token_uri(TOKEN_1); let expected = format!("{}{}", BASE_URI(), TOKEN_1); @@ -212,7 +202,7 @@ fn test_token_uri() { #[test] fn test_get_approved() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); let spender = SPENDER(); let token_id = TOKEN_1; @@ -225,9 +215,9 @@ fn test_get_approved() { } #[test] -#[should_panic(expected: ('ERC721: invalid token ID', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_get_approved_nonexistent() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); dispatcher.get_approved(NONEXISTENT); } @@ -237,10 +227,10 @@ fn test_get_approved_nonexistent() { #[test] fn test_approve_from_owner() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); dispatcher.approve(SPENDER(), TOKEN_1); - assert_event_approval(dispatcher.contract_address, OWNER(), SPENDER(), TOKEN_1); + spy.assert_event_approval(dispatcher.contract_address, OWNER(), SPENDER(), TOKEN_1); let approved = dispatcher.get_approved(TOKEN_1); assert_eq!(approved, SPENDER()); @@ -248,32 +238,32 @@ fn test_approve_from_owner() { #[test] fn test_approve_from_operator() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); dispatcher.set_approval_for_all(OPERATOR(), true); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(OPERATOR()); + start_cheat_caller_address(dispatcher.contract_address, OPERATOR()); dispatcher.approve(SPENDER(), TOKEN_1); - assert_event_approval(dispatcher.contract_address, OWNER(), SPENDER(), TOKEN_1); + spy.assert_event_approval(dispatcher.contract_address, OWNER(), SPENDER(), TOKEN_1); let approved = dispatcher.get_approved(TOKEN_1); assert_eq!(approved, SPENDER()); } #[test] -#[should_panic(expected: ('ERC721: unauthorized caller', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_approve_from_unauthorized() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); - testing::set_contract_address(OTHER()); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.approve(SPENDER(), TOKEN_1); } #[test] -#[should_panic(expected: ('ERC721: invalid token ID', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_approve_nonexistent() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); dispatcher.approve(SPENDER(), NONEXISTENT); } @@ -283,19 +273,19 @@ fn test_approve_nonexistent() { #[test] fn test_set_approval_for_all() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let is_not_approved_for_all = !dispatcher.is_approved_for_all(OWNER(), OPERATOR()); assert!(is_not_approved_for_all); dispatcher.set_approval_for_all(OPERATOR(), true); - assert_event_approval_for_all(dispatcher.contract_address, OWNER(), OPERATOR(), true); + spy.assert_event_approval_for_all(dispatcher.contract_address, OWNER(), OPERATOR(), true); let is_approved_for_all = dispatcher.is_approved_for_all(OWNER(), OPERATOR()); assert!(is_approved_for_all); dispatcher.set_approval_for_all(OPERATOR(), false); - assert_event_approval_for_all(dispatcher.contract_address, OWNER(), OPERATOR(), false); + spy.assert_event_approval_for_all(dispatcher.contract_address, OWNER(), OPERATOR(), false); let is_not_approved_for_all = !dispatcher.is_approved_for_all(OWNER(), OPERATOR()); assert!(is_not_approved_for_all); @@ -307,14 +297,14 @@ fn test_set_approval_for_all() { #[test] fn test_transfer_from_owner() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let token_id = TOKEN_1; let owner = OWNER(); let recipient = RECIPIENT(); // set approval to check reset dispatcher.approve(OTHER(), token_id); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); assert_state_before_transfer(dispatcher, owner, recipient, token_id); @@ -322,21 +312,21 @@ fn test_transfer_from_owner() { assert_eq!(approved, OTHER()); dispatcher.transfer_from(owner, recipient, token_id); - assert_only_event_transfer(dispatcher.contract_address, owner, recipient, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, recipient, token_id); assert_state_after_transfer(dispatcher, owner, recipient, token_id); } #[test] fn test_transferFrom_owner() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let token_id = TOKEN_1; let owner = OWNER(); let recipient = RECIPIENT(); // set approval to check reset dispatcher.approve(OTHER(), token_id); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); assert_state_before_transfer(dispatcher, owner, recipient, token_id); @@ -344,100 +334,100 @@ fn test_transferFrom_owner() { assert_eq!(approved, OTHER()); dispatcher.transferFrom(owner, recipient, token_id); - assert_only_event_transfer(dispatcher.contract_address, owner, recipient, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, recipient, token_id); assert_state_after_transfer(dispatcher, owner, recipient, token_id); } #[test] -#[should_panic(expected: ('ERC721: invalid token ID', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_transfer_from_nonexistent() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); dispatcher.transfer_from(OWNER(), RECIPIENT(), NONEXISTENT); } #[test] -#[should_panic(expected: ('ERC721: invalid token ID', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_transferFrom_nonexistent() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); dispatcher.transferFrom(OWNER(), RECIPIENT(), NONEXISTENT); } #[test] -#[should_panic(expected: ('ERC721: invalid receiver', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: invalid receiver',))] fn test_transfer_from_to_zero() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); dispatcher.transfer_from(OWNER(), ZERO(), TOKEN_1); } #[test] -#[should_panic(expected: ('ERC721: invalid receiver', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: invalid receiver',))] fn test_transferFrom_to_zero() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); dispatcher.transferFrom(OWNER(), ZERO(), TOKEN_1); } #[test] fn test_transfer_from_to_owner() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); assert_state_transfer_to_self(dispatcher, OWNER(), TOKEN_1, TOKENS_LEN); dispatcher.transfer_from(OWNER(), OWNER(), TOKEN_1); - assert_only_event_transfer(dispatcher.contract_address, OWNER(), OWNER(), TOKEN_1); + spy.assert_only_event_transfer(dispatcher.contract_address, OWNER(), OWNER(), TOKEN_1); assert_state_transfer_to_self(dispatcher, OWNER(), TOKEN_1, TOKENS_LEN); } #[test] fn test_transferFrom_to_owner() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); assert_state_transfer_to_self(dispatcher, OWNER(), TOKEN_1, TOKENS_LEN); dispatcher.transferFrom(OWNER(), OWNER(), TOKEN_1); - assert_only_event_transfer(dispatcher.contract_address, OWNER(), OWNER(), TOKEN_1); + spy.assert_only_event_transfer(dispatcher.contract_address, OWNER(), OWNER(), TOKEN_1); assert_state_transfer_to_self(dispatcher, OWNER(), TOKEN_1, TOKENS_LEN); } #[test] fn test_transfer_from_approved() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let token_id = TOKEN_1; let owner = OWNER(); let recipient = RECIPIENT(); assert_state_before_transfer(dispatcher, owner, recipient, token_id); dispatcher.approve(OPERATOR(), token_id); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(OPERATOR()); + start_cheat_caller_address(dispatcher.contract_address, OPERATOR()); dispatcher.transfer_from(owner, recipient, token_id); - assert_only_event_transfer(dispatcher.contract_address, owner, recipient, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, recipient, token_id); assert_state_after_transfer(dispatcher, owner, recipient, token_id); } #[test] fn test_transferFrom_approved() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let token_id = TOKEN_1; let owner = OWNER(); let recipient = RECIPIENT(); assert_state_before_transfer(dispatcher, owner, recipient, token_id); dispatcher.approve(OPERATOR(), token_id); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(OPERATOR()); + start_cheat_caller_address(dispatcher.contract_address, OPERATOR()); dispatcher.transferFrom(owner, recipient, token_id); - assert_only_event_transfer(dispatcher.contract_address, owner, recipient, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, recipient, token_id); assert_state_after_transfer(dispatcher, owner, recipient, token_id); } #[test] fn test_transfer_from_approved_for_all() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let token_id = TOKEN_1; let owner = OWNER(); let recipient = RECIPIENT(); @@ -445,18 +435,18 @@ fn test_transfer_from_approved_for_all() { assert_state_before_transfer(dispatcher, owner, recipient, token_id); dispatcher.set_approval_for_all(OPERATOR(), true); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(OPERATOR()); + start_cheat_caller_address(dispatcher.contract_address, OPERATOR()); dispatcher.transfer_from(owner, recipient, token_id); - assert_only_event_transfer(dispatcher.contract_address, owner, recipient, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, recipient, token_id); assert_state_after_transfer(dispatcher, owner, recipient, token_id); } #[test] fn test_transferFrom_approved_for_all() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let token_id = TOKEN_1; let owner = OWNER(); let recipient = RECIPIENT(); @@ -464,28 +454,28 @@ fn test_transferFrom_approved_for_all() { assert_state_before_transfer(dispatcher, owner, recipient, token_id); dispatcher.set_approval_for_all(OPERATOR(), true); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(OPERATOR()); + start_cheat_caller_address(dispatcher.contract_address, OPERATOR()); dispatcher.transferFrom(owner, recipient, token_id); - assert_only_event_transfer(dispatcher.contract_address, owner, recipient, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, recipient, token_id); assert_state_after_transfer(dispatcher, owner, recipient, token_id); } #[test] -#[should_panic(expected: ('ERC721: unauthorized caller', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_transfer_from_unauthorized() { - let dispatcher = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.transfer_from(OWNER(), RECIPIENT(), TOKEN_1); } #[test] -#[should_panic(expected: ('ERC721: unauthorized caller', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_transferFrom_unauthorized() { - let dispatcher = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.transferFrom(OWNER(), RECIPIENT(), TOKEN_1); } @@ -495,67 +485,71 @@ fn test_transferFrom_unauthorized() { #[test] fn test_safe_transfer_from_to_account() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let account = setup_account(); let token_id = TOKEN_1; let owner = OWNER(); + spy.drop_all_events(); assert_state_before_transfer(dispatcher, owner, account, token_id); dispatcher.safe_transfer_from(owner, account, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, account, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, account, token_id); assert_state_after_transfer(dispatcher, owner, account, token_id); } #[test] fn test_safeTransferFrom_to_account() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let account = setup_account(); let token_id = TOKEN_1; let owner = OWNER(); + spy.drop_all_events(); assert_state_before_transfer(dispatcher, owner, account, token_id); dispatcher.safeTransferFrom(owner, account, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, account, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, account, token_id); assert_state_after_transfer(dispatcher, owner, account, token_id); } #[test] fn test_safe_transfer_from_to_account_camel() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let account = setup_camel_account(); let token_id = TOKEN_1; let owner = OWNER(); + spy.drop_all_events(); assert_state_before_transfer(dispatcher, owner, account, token_id); dispatcher.safe_transfer_from(owner, account, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, account, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, account, token_id); assert_state_after_transfer(dispatcher, owner, account, token_id); } #[test] fn test_safeTransferFrom_to_account_camel() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let account = setup_camel_account(); let token_id = TOKEN_1; let owner = OWNER(); + spy.drop_all_events(); assert_state_before_transfer(dispatcher, owner, account, token_id); dispatcher.safeTransferFrom(owner, account, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, account, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, account, token_id); assert_state_after_transfer(dispatcher, owner, account, token_id); } #[test] fn test_safe_transfer_from_to_receiver() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let receiver = setup_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -563,14 +557,14 @@ fn test_safe_transfer_from_to_receiver() { assert_state_before_transfer(dispatcher, owner, receiver, token_id); dispatcher.safe_transfer_from(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); assert_state_after_transfer(dispatcher, owner, receiver, token_id); } #[test] fn test_safeTransferFrom_to_receiver() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let receiver = setup_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -578,14 +572,15 @@ fn test_safeTransferFrom_to_receiver() { assert_state_before_transfer(dispatcher, owner, receiver, token_id); dispatcher.safeTransferFrom(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); assert_state_after_transfer(dispatcher, owner, receiver, token_id); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safe_transfer_from_to_receiver_camel() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let receiver = setup_camel_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -593,14 +588,15 @@ fn test_safe_transfer_from_to_receiver_camel() { assert_state_before_transfer(dispatcher, owner, receiver, token_id); dispatcher.safe_transfer_from(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); assert_state_after_transfer(dispatcher, owner, receiver, token_id); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safeTransferFrom_to_receiver_camel() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let receiver = setup_camel_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -608,15 +604,15 @@ fn test_safeTransferFrom_to_receiver_camel() { assert_state_before_transfer(dispatcher, owner, receiver, token_id); dispatcher.safeTransferFrom(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); assert_state_after_transfer(dispatcher, owner, receiver, token_id); } #[test] -#[should_panic(expected: ('ERC721: safe transfer failed', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: safe transfer failed',))] fn test_safe_transfer_from_to_receiver_failure() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); let receiver = setup_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -625,9 +621,9 @@ fn test_safe_transfer_from_to_receiver_failure() { } #[test] -#[should_panic(expected: ('ERC721: safe transfer failed', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: safe transfer failed',))] fn test_safeTransferFrom_to_receiver_failure() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); let receiver = setup_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -636,9 +632,10 @@ fn test_safeTransferFrom_to_receiver_failure() { } #[test] -#[should_panic(expected: ('ERC721: safe transfer failed', 'ENTRYPOINT_FAILED'))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ('ERC721: safe transfer failed',))] fn test_safe_transfer_from_to_receiver_failure_camel() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); let receiver = setup_camel_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -647,9 +644,10 @@ fn test_safe_transfer_from_to_receiver_failure_camel() { } #[test] -#[should_panic(expected: ('ERC721: safe transfer failed', 'ENTRYPOINT_FAILED'))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ('ERC721: safe transfer failed',))] fn test_safeTransferFrom_to_receiver_failure_camel() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); let receiver = setup_camel_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -658,10 +656,11 @@ fn test_safeTransferFrom_to_receiver_failure_camel() { } #[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', 'ENTRYPOINT_FAILED'))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_safe_transfer_from_to_non_receiver() { - let dispatcher = setup_dispatcher(); - let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); + let (_, dispatcher) = setup_dispatcher(); + let recipient = utils::declare_and_deploy("NonImplementingMock", array![]); let token_id = TOKEN_1; let owner = OWNER(); @@ -669,10 +668,11 @@ fn test_safe_transfer_from_to_non_receiver() { } #[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', 'ENTRYPOINT_FAILED'))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_safeTransferFrom_to_non_receiver() { - let dispatcher = setup_dispatcher(); - let recipient = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); + let (_, dispatcher) = setup_dispatcher(); + let recipient = utils::declare_and_deploy("NonImplementingMock", array![]); let token_id = TOKEN_1; let owner = OWNER(); @@ -680,108 +680,110 @@ fn test_safeTransferFrom_to_non_receiver() { } #[test] -#[should_panic(expected: ('ERC721: invalid token ID', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_safe_transfer_from_nonexistent() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), NONEXISTENT, DATA(true)); } #[test] -#[should_panic(expected: ('ERC721: invalid token ID', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: invalid token ID',))] fn test_safeTransferFrom_nonexistent() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); dispatcher.safeTransferFrom(OWNER(), RECIPIENT(), NONEXISTENT, DATA(true)); } #[test] -#[should_panic(expected: ('ERC721: invalid receiver', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: invalid receiver',))] fn test_safe_transfer_from_to_zero() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); dispatcher.safe_transfer_from(OWNER(), ZERO(), TOKEN_1, DATA(true)); } #[test] -#[should_panic(expected: ('ERC721: invalid receiver', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: invalid receiver',))] fn test_safeTransferFrom_to_zero() { - let dispatcher = setup_dispatcher(); + let (_, dispatcher) = setup_dispatcher(); dispatcher.safeTransferFrom(OWNER(), ZERO(), TOKEN_1, DATA(true)); } #[test] fn test_safe_transfer_from_to_owner() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let token_id = TOKEN_1; let receiver = setup_receiver(); dispatcher.transfer_from(OWNER(), receiver, token_id); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); assert_state_transfer_to_self(dispatcher, receiver, token_id, 1); - testing::set_contract_address(receiver); + start_cheat_caller_address(dispatcher.contract_address, receiver); dispatcher.safe_transfer_from(receiver, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, receiver, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, receiver, receiver, token_id); assert_state_transfer_to_self(dispatcher, receiver, token_id, 1); } #[test] fn test_safeTransferFrom_to_owner() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let token_id = TOKEN_1; let receiver = setup_receiver(); dispatcher.transfer_from(OWNER(), receiver, token_id); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); assert_state_transfer_to_self(dispatcher, receiver, token_id, 1); - testing::set_contract_address(receiver); + start_cheat_caller_address(dispatcher.contract_address, receiver); dispatcher.safeTransferFrom(receiver, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, receiver, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, receiver, receiver, token_id); assert_state_transfer_to_self(dispatcher, receiver, token_id, 1); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safe_transfer_from_to_owner_camel() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let token_id = TOKEN_1; let receiver = setup_camel_receiver(); dispatcher.transfer_from(OWNER(), receiver, token_id); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); assert_state_transfer_to_self(dispatcher, receiver, token_id, 1); - testing::set_contract_address(receiver); + start_cheat_caller_address(dispatcher.contract_address, receiver); dispatcher.safe_transfer_from(receiver, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, receiver, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, receiver, receiver, token_id); assert_state_transfer_to_self(dispatcher, receiver, token_id, 1); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safeTransferFrom_to_owner_camel() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let token_id = TOKEN_1; let receiver = setup_camel_receiver(); dispatcher.transfer_from(OWNER(), receiver, token_id); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); assert_state_transfer_to_self(dispatcher, receiver, token_id, 1); - testing::set_contract_address(receiver); + start_cheat_caller_address(dispatcher.contract_address, receiver); dispatcher.safeTransferFrom(receiver, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, receiver, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, receiver, receiver, token_id); assert_state_transfer_to_self(dispatcher, receiver, token_id, 1); } #[test] fn test_safe_transfer_from_approved() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let receiver = setup_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -789,18 +791,18 @@ fn test_safe_transfer_from_approved() { assert_state_before_transfer(dispatcher, owner, receiver, token_id); dispatcher.approve(OPERATOR(), token_id); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(OPERATOR()); + start_cheat_caller_address(dispatcher.contract_address, OPERATOR()); dispatcher.safe_transfer_from(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); assert_state_after_transfer(dispatcher, owner, receiver, token_id); } #[test] fn test_safeTransferFrom_approved() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let receiver = setup_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -808,18 +810,19 @@ fn test_safeTransferFrom_approved() { assert_state_before_transfer(dispatcher, owner, receiver, token_id); dispatcher.approve(OPERATOR(), token_id); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(OPERATOR()); + start_cheat_caller_address(dispatcher.contract_address, OPERATOR()); dispatcher.safeTransferFrom(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); assert_state_after_transfer(dispatcher, owner, receiver, token_id); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safe_transfer_from_approved_camel() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let receiver = setup_camel_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -827,18 +830,19 @@ fn test_safe_transfer_from_approved_camel() { assert_state_before_transfer(dispatcher, owner, receiver, token_id); dispatcher.approve(OPERATOR(), token_id); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(OPERATOR()); + start_cheat_caller_address(dispatcher.contract_address, OPERATOR()); dispatcher.safe_transfer_from(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); assert_state_after_transfer(dispatcher, owner, receiver, token_id); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safeTransferFrom_approved_camel() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let receiver = setup_camel_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -846,18 +850,18 @@ fn test_safeTransferFrom_approved_camel() { assert_state_before_transfer(dispatcher, owner, receiver, token_id); dispatcher.approve(OPERATOR(), token_id); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(OPERATOR()); + start_cheat_caller_address(dispatcher.contract_address, OPERATOR()); dispatcher.safeTransferFrom(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); assert_state_after_transfer(dispatcher, owner, receiver, token_id); } #[test] fn test_safe_transfer_from_approved_for_all() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let receiver = setup_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -865,18 +869,18 @@ fn test_safe_transfer_from_approved_for_all() { assert_state_before_transfer(dispatcher, owner, receiver, token_id); dispatcher.set_approval_for_all(OPERATOR(), true); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(OPERATOR()); + start_cheat_caller_address(dispatcher.contract_address, OPERATOR()); dispatcher.safe_transfer_from(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); assert_state_after_transfer(dispatcher, owner, receiver, token_id); } #[test] fn test_safeTransferFrom_approved_for_all() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let receiver = setup_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -884,18 +888,19 @@ fn test_safeTransferFrom_approved_for_all() { assert_state_before_transfer(dispatcher, owner, receiver, token_id); dispatcher.set_approval_for_all(OPERATOR(), true); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(OPERATOR()); + start_cheat_caller_address(dispatcher.contract_address, OPERATOR()); dispatcher.safeTransferFrom(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); assert_state_after_transfer(dispatcher, owner, receiver, token_id); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safe_transfer_from_approved_for_all_camel() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let receiver = setup_camel_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -903,18 +908,19 @@ fn test_safe_transfer_from_approved_for_all_camel() { assert_state_before_transfer(dispatcher, owner, receiver, token_id); dispatcher.set_approval_for_all(OPERATOR(), true); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(OPERATOR()); + start_cheat_caller_address(dispatcher.contract_address, OPERATOR()); dispatcher.safe_transfer_from(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); assert_state_after_transfer(dispatcher, owner, receiver, token_id); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safeTransferFrom_approved_for_all_camel() { - let dispatcher = setup_dispatcher(); + let (mut spy, dispatcher) = setup_dispatcher(); let receiver = setup_camel_receiver(); let token_id = TOKEN_1; let owner = OWNER(); @@ -922,28 +928,28 @@ fn test_safeTransferFrom_approved_for_all_camel() { assert_state_before_transfer(dispatcher, owner, receiver, token_id); dispatcher.set_approval_for_all(OPERATOR(), true); - utils::drop_event(dispatcher.contract_address); + spy.drop_event(); - testing::set_contract_address(OPERATOR()); + start_cheat_caller_address(dispatcher.contract_address, OPERATOR()); dispatcher.safeTransferFrom(owner, receiver, token_id, DATA(true)); - assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); + spy.assert_only_event_transfer(dispatcher.contract_address, owner, receiver, token_id); assert_state_after_transfer(dispatcher, owner, receiver, token_id); } #[test] -#[should_panic(expected: ('ERC721: unauthorized caller', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_safe_transfer_from_unauthorized() { - let dispatcher = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.safe_transfer_from(OWNER(), RECIPIENT(), TOKEN_1, DATA(true)); } #[test] -#[should_panic(expected: ('ERC721: unauthorized caller', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_safeTransferFrom_unauthorized() { - let dispatcher = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.safeTransferFrom(OWNER(), RECIPIENT(), TOKEN_1, DATA(true)); } @@ -953,69 +959,69 @@ fn test_safeTransferFrom_unauthorized() { #[test] fn test_transfer_ownership() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (mut spy, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.transfer_ownership(OTHER()); - assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), OTHER()); + spy.assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), OTHER()); assert_eq!(dispatcher.owner(), OTHER()); } #[test] -#[should_panic(expected: ('New owner is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('New owner is the zero address',))] fn test_transfer_ownership_to_zero() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.transfer_ownership(ZERO()); } #[test] -#[should_panic(expected: ('Caller is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_transfer_ownership_from_zero() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(ZERO()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, ZERO()); dispatcher.transfer_ownership(OTHER()); } #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_transfer_ownership_from_nonowner() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.transfer_ownership(OTHER()); } #[test] fn test_transferOwnership() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (mut spy, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.transferOwnership(OTHER()); - assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), OTHER()); + spy.assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), OTHER()); assert_eq!(dispatcher.owner(), OTHER()); } #[test] -#[should_panic(expected: ('New owner is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('New owner is the zero address',))] fn test_transferOwnership_to_zero() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.transferOwnership(ZERO()); } #[test] -#[should_panic(expected: ('Caller is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_transferOwnership_from_zero() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(ZERO()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, ZERO()); dispatcher.transferOwnership(OTHER()); } #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_transferOwnership_from_nonowner() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.transferOwnership(OTHER()); } @@ -1025,53 +1031,53 @@ fn test_transferOwnership_from_nonowner() { #[test] fn test_renounce_ownership() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (mut spy, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.renounce_ownership(); - assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), ZERO()); + spy.assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), ZERO()); assert!(dispatcher.owner().is_zero()); } #[test] -#[should_panic(expected: ('Caller is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_renounce_ownership_from_zero_address() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(ZERO()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, ZERO()); dispatcher.renounce_ownership(); } #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_renounce_ownership_from_nonowner() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.renounce_ownership(); } #[test] fn test_renounceOwnership() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (mut spy, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.renounceOwnership(); - assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), ZERO()); + spy.assert_event_ownership_transferred(dispatcher.contract_address, OWNER(), ZERO()); assert!(dispatcher.owner().is_zero()); } #[test] -#[should_panic(expected: ('Caller is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_renounceOwnership_from_zero_address() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(ZERO()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, ZERO()); dispatcher.renounceOwnership(); } #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_renounceOwnership_from_nonowner() { - let mut dispatcher = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, mut dispatcher) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.renounceOwnership(); } @@ -1080,52 +1086,60 @@ fn test_renounceOwnership_from_nonowner() { // #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_upgrade_unauthorized() { - let v1 = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, mut v1) = setup_dispatcher(); + start_cheat_caller_address(v1.contract_address, OTHER()); v1.upgrade(CLASS_HASH_ZERO()); } #[test] -#[should_panic(expected: ('Class hash cannot be zero', 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ('Class hash cannot be zero',))] fn test_upgrade_with_class_hash_zero() { - let v1 = setup_dispatcher(); + let (_, mut v1) = setup_dispatcher(); - testing::set_contract_address(OWNER()); + start_cheat_caller_address(v1.contract_address, OWNER()); v1.upgrade(CLASS_HASH_ZERO()); } #[test] fn test_upgraded_event() { - let v1 = setup_dispatcher(); + let (mut spy, mut v1) = setup_dispatcher(); let v2_class_hash = V2_CLASS_HASH(); - testing::set_contract_address(OWNER()); + start_cheat_caller_address(v1.contract_address, OWNER()); v1.upgrade(v2_class_hash); - assert_only_event_upgraded(v1.contract_address, v2_class_hash); + spy.assert_only_event_upgraded(v1.contract_address, v2_class_hash); } #[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +#[feature("safe_dispatcher")] fn test_v2_missing_camel_selector() { - let v1 = setup_dispatcher(); + let (_, mut v1) = setup_dispatcher(); let v2_class_hash = V2_CLASS_HASH(); - testing::set_contract_address(OWNER()); + start_cheat_caller_address(v1.contract_address, OWNER()); v1.upgrade(v2_class_hash); - let dispatcher = IERC721CamelOnlyDispatcher { contract_address: v1.contract_address }; - dispatcher.ownerOf(TOKEN_1); + let safe_dispatcher = IERC721CamelOnlySafeDispatcher { contract_address: v1.contract_address }; + let mut panic_data = safe_dispatcher.ownerOf(TOKEN_1).unwrap_err(); + + let selector = selector!("ownerOf"); + let expected_panic_message = format!( + "Entry point selector {} not found in contract {}", + selector.into_base_16_string(), + v1.contract_address.into_base_16_string() + ); + assert_eq!(utils::panic_data_to_byte_array(panic_data), expected_panic_message); } #[test] fn test_state_persists_after_upgrade() { - let v1 = setup_dispatcher(); + let (_, mut v1) = setup_dispatcher(); let v2_class_hash = V2_CLASS_HASH(); - testing::set_contract_address(OWNER()); + start_cheat_caller_address(v1.contract_address, OWNER()); v1.transferFrom(OWNER(), RECIPIENT(), TOKEN_1); // Check RECIPIENT balance v1 diff --git a/src/tests/upgrades/common.cairo b/src/tests/upgrades/common.cairo index 10876b756..973f47c8b 100644 --- a/src/tests/upgrades/common.cairo +++ b/src/tests/upgrades/common.cairo @@ -5,7 +5,7 @@ use snforge_std::{EventSpy, EventSpyAssertionsTrait}; use starknet::{ContractAddress, ClassHash}; #[generate_trait] -pub(crate) impl UpgradableSpyHelpersImpl of UpgradableSpyHelpers { +pub(crate) impl UpgradeableSpyHelpersImpl of UpgradeableSpyHelpers { fn assert_event_upgraded(ref self: EventSpy, contract: ContractAddress, class_hash: ClassHash) { let expected = UpgradeableComponent::Event::Upgraded(Upgraded { class_hash }); self.assert_emitted_single(contract, expected); diff --git a/src/tests/upgrades/test_upgradeable.cairo b/src/tests/upgrades/test_upgradeable.cairo index 867aad502..d55c41f74 100644 --- a/src/tests/upgrades/test_upgradeable.cairo +++ b/src/tests/upgrades/test_upgradeable.cairo @@ -6,7 +6,7 @@ use openzeppelin::tests::utils::{declare_class, deploy}; use openzeppelin::upgrades::UpgradeableComponent; use snforge_std::{spy_events, ContractClass}; -use super::common::UpgradableSpyHelpers; +use super::common::UpgradeableSpyHelpers; // // Setup From daff8e54c34a6a8ee95c6cb1ab9bbefde46e295a Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 19 Jul 2024 21:29:14 +0200 Subject: [PATCH 12/20] Migrate erc1155 preset tests (#1057) * feat: update tests * refactor: remove unnecessary import --- src/tests/presets.cairo | 2 +- src/tests/presets/test_erc1155.cairo | 422 ++++++++++++++------------- src/tests/presets/test_erc20.cairo | 10 +- src/tests/utils.cairo | 5 +- src/tests/utils/common.cairo | 14 + 5 files changed, 246 insertions(+), 207 deletions(-) diff --git a/src/tests/presets.cairo b/src/tests/presets.cairo index feb573ea8..d8b4fdd0e 100644 --- a/src/tests/presets.cairo +++ b/src/tests/presets.cairo @@ -1,5 +1,5 @@ // mod test_account; -// mod test_erc1155; +mod test_erc1155; mod test_erc20; mod test_erc721; // mod test_eth_account; diff --git a/src/tests/presets/test_erc1155.cairo b/src/tests/presets/test_erc1155.cairo index ba1b71101..454e97ae9 100644 --- a/src/tests/presets/test_erc1155.cairo +++ b/src/tests/presets/test_erc1155.cairo @@ -1,50 +1,45 @@ use core::num::traits::Zero; use openzeppelin::introspection; -use openzeppelin::presets::ERC1155Upgradeable; use openzeppelin::presets::interfaces::{ ERC1155UpgradeableABIDispatcher, ERC1155UpgradeableABIDispatcherTrait }; -use openzeppelin::tests::access::common::assert_event_ownership_transferred; -use openzeppelin::tests::mocks::erc1155_mocks::SnakeERC1155Mock; +use openzeppelin::tests::access::common::OwnableSpyHelpers; +use openzeppelin::tests::token::erc1155::common::ERC1155SpyHelpers; use openzeppelin::tests::token::erc1155::common::{ - assert_only_event_transfer_single, assert_only_event_transfer_batch, - assert_only_event_approval_for_all -}; -use openzeppelin::tests::token::erc1155::common::{ - setup_account, setup_receiver, setup_camel_receiver, setup_account_with_salt, setup_src5 + setup_account, setup_receiver, setup_camel_receiver, deploy_another_account_at, setup_src5 }; use openzeppelin::tests::token::erc1155::common::{get_ids_and_values, get_ids_and_split_values}; -use openzeppelin::tests::upgrades::common::assert_only_event_upgraded; +use openzeppelin::tests::upgrades::common::UpgradableSpyHelpers; use openzeppelin::tests::utils::constants::{ EMPTY_DATA, ZERO, OWNER, RECIPIENT, CLASS_HASH_ZERO, OPERATOR, OTHER, TOKEN_ID, TOKEN_ID_2, TOKEN_VALUE, TOKEN_VALUE_2 }; +use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::tests::utils; use openzeppelin::token::erc1155::interface::{ - IERC1155CamelDispatcher, IERC1155CamelDispatcherTrait + IERC1155CamelSafeDispatcher, IERC1155CamelSafeDispatcherTrait }; use openzeppelin::token::erc1155::interface::{IERC1155Dispatcher, IERC1155DispatcherTrait}; use openzeppelin::token::erc1155; use openzeppelin::utils::serde::SerializedAppend; -use starknet::testing; +use snforge_std::{spy_events, EventSpy, start_cheat_caller_address}; use starknet::{ContractAddress, ClassHash}; fn V2_CLASS_HASH() -> ClassHash { - SnakeERC1155Mock::TEST_CLASS_HASH.try_into().unwrap() + utils::declare_class("SnakeERC1155Mock").class_hash } // // Setup // -fn setup_dispatcher_with_event() -> (ERC1155UpgradeableABIDispatcher, ContractAddress) { +fn setup_dispatcher_with_event() -> (EventSpy, ERC1155UpgradeableABIDispatcher, ContractAddress) { let uri: ByteArray = "URI"; let mut calldata = array![]; let mut token_ids = array![TOKEN_ID, TOKEN_ID_2]; let mut values = array![TOKEN_VALUE, TOKEN_VALUE_2]; let owner = setup_account(); - testing::set_contract_address(owner); calldata.append_serde(uri); calldata.append_serde(owner); @@ -52,15 +47,16 @@ fn setup_dispatcher_with_event() -> (ERC1155UpgradeableABIDispatcher, ContractAd calldata.append_serde(values); calldata.append_serde(owner); - let address = utils::deploy(ERC1155Upgradeable::TEST_CLASS_HASH, calldata); - (ERC1155UpgradeableABIDispatcher { contract_address: address }, owner) + let spy = spy_events(); + let address = utils::declare_and_deploy("ERC1155Upgradeable", calldata); + start_cheat_caller_address(address, owner); + (spy, ERC1155UpgradeableABIDispatcher { contract_address: address }, owner) } -fn setup_dispatcher() -> (ERC1155UpgradeableABIDispatcher, ContractAddress) { - let (dispatcher, owner) = setup_dispatcher_with_event(); - utils::drop_event(dispatcher.contract_address); // `TransferOwnership` - utils::drop_event(dispatcher.contract_address); // `Transfer` - (dispatcher, owner) +fn setup_dispatcher() -> (EventSpy, ERC1155UpgradeableABIDispatcher, ContractAddress) { + let (mut spy, dispatcher, owner) = setup_dispatcher_with_event(); + spy.drop_all_events(); + (spy, dispatcher, owner) } // @@ -69,7 +65,7 @@ fn setup_dispatcher() -> (ERC1155UpgradeableABIDispatcher, ContractAddress) { #[test] fn test_constructor() { - let (dispatcher, owner) = setup_dispatcher_with_event(); + let (_, dispatcher, owner) = setup_dispatcher_with_event(); assert_eq!(dispatcher.uri(TOKEN_ID), "URI"); assert_eq!(dispatcher.balance_of(owner, TOKEN_ID), TOKEN_VALUE); @@ -92,7 +88,7 @@ fn test_constructor() { #[test] fn test_balance_of() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let balance = dispatcher.balance_of(owner, TOKEN_ID); assert_eq!(balance, TOKEN_VALUE); @@ -100,7 +96,7 @@ fn test_balance_of() { #[test] fn test_balanceOf() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let balance = dispatcher.balanceOf(owner, TOKEN_ID); assert_eq!(balance, TOKEN_VALUE); @@ -112,7 +108,7 @@ fn test_balanceOf() { #[test] fn test_balance_of_batch() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let accounts = array![owner, OTHER()].span(); let token_ids = array![TOKEN_ID, TOKEN_ID].span(); @@ -124,7 +120,7 @@ fn test_balance_of_batch() { #[test] fn test_balanceOfBatch() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let accounts = array![owner, OTHER()].span(); let token_ids = array![TOKEN_ID, TOKEN_ID].span(); @@ -135,9 +131,9 @@ fn test_balanceOfBatch() { } #[test] -#[should_panic(expected: ('ERC1155: no equal array length', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: no equal array length',))] fn test_balance_of_batch_invalid_inputs() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let accounts = array![owner, OTHER()].span(); let token_ids = array![TOKEN_ID].span(); @@ -146,9 +142,9 @@ fn test_balance_of_batch_invalid_inputs() { } #[test] -#[should_panic(expected: ('ERC1155: no equal array length', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: no equal array length',))] fn test_balanceOfBatch_invalid_inputs() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let accounts = array![owner, OTHER()].span(); let token_ids = array![TOKEN_ID].span(); @@ -162,203 +158,219 @@ fn test_balanceOfBatch_invalid_inputs() { #[test] fn test_safe_transfer_from_to_receiver() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; let recipient = setup_receiver(); assert_state_before_transfer_single(dispatcher, owner, recipient, TOKEN_ID); dispatcher.safe_transfer_from(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(contract, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy.assert_only_event_transfer_single(contract, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); assert_state_after_transfer_single(dispatcher, owner, recipient, TOKEN_ID); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safe_transfer_from_to_camel_receiver() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; let recipient = setup_camel_receiver(); assert_state_before_transfer_single(dispatcher, owner, recipient, TOKEN_ID); dispatcher.safe_transfer_from(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(contract, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy.assert_only_event_transfer_single(contract, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); assert_state_after_transfer_single(dispatcher, owner, recipient, TOKEN_ID); } #[test] fn test_safeTransferFrom_to_receiver() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; let recipient = setup_receiver(); assert_state_before_transfer_single(dispatcher, owner, recipient, TOKEN_ID); dispatcher.safeTransferFrom(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(contract, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy.assert_only_event_transfer_single(contract, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); assert_state_after_transfer_single(dispatcher, owner, recipient, TOKEN_ID); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safeTransferFrom_to_camel_receiver() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; let recipient = setup_camel_receiver(); assert_state_before_transfer_single(dispatcher, owner, recipient, TOKEN_ID); dispatcher.safeTransferFrom(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(contract, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy.assert_only_event_transfer_single(contract, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); assert_state_after_transfer_single(dispatcher, owner, recipient, TOKEN_ID); } #[test] fn test_safe_transfer_from_to_account() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; - let recipient = setup_account_with_salt(1); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); + spy.drop_all_events(); assert_state_before_transfer_single(dispatcher, owner, recipient, TOKEN_ID); dispatcher.safe_transfer_from(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(contract, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy.assert_only_event_transfer_single(contract, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); assert_state_after_transfer_single(dispatcher, owner, recipient, TOKEN_ID); } #[test] fn test_safeTransferFrom_to_account() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; - let recipient = setup_account_with_salt(1); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); + spy.drop_all_events(); assert_state_before_transfer_single(dispatcher, owner, recipient, TOKEN_ID); dispatcher.safeTransferFrom(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(contract, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy.assert_only_event_transfer_single(contract, owner, owner, recipient, TOKEN_ID, TOKEN_VALUE); assert_state_after_transfer_single(dispatcher, owner, recipient, TOKEN_ID); } #[test] fn test_safe_transfer_from_approved_operator() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; - let recipient = setup_account_with_salt(1); let operator = OPERATOR(); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); + spy.drop_all_events(); dispatcher.set_approval_for_all(operator, true); - assert_only_event_approval_for_all(contract, owner, operator, true); + spy.assert_only_event_approval_for_all(contract, owner, operator, true); assert_state_before_transfer_single(dispatcher, owner, recipient, TOKEN_ID); - testing::set_contract_address(operator); + start_cheat_caller_address(dispatcher.contract_address, operator); dispatcher.safe_transfer_from(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(contract, operator, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract, operator, owner, recipient, TOKEN_ID, TOKEN_VALUE + ); assert_state_after_transfer_single(dispatcher, owner, recipient, TOKEN_ID); } #[test] fn test_safeTransferFrom_approved_operator() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; - let recipient = setup_account_with_salt(1); let operator = OPERATOR(); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); + spy.drop_all_events(); dispatcher.set_approval_for_all(operator, true); - assert_only_event_approval_for_all(contract, owner, operator, true); + spy.assert_only_event_approval_for_all(contract, owner, operator, true); assert_state_before_transfer_single(dispatcher, owner, recipient, TOKEN_ID); - testing::set_contract_address(operator); + start_cheat_caller_address(dispatcher.contract_address, operator); dispatcher.safeTransferFrom(owner, recipient, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); - assert_only_event_transfer_single(contract, operator, owner, recipient, TOKEN_ID, TOKEN_VALUE); + spy + .assert_only_event_transfer_single( + contract, operator, owner, recipient, TOKEN_ID, TOKEN_VALUE + ); assert_state_after_transfer_single(dispatcher, owner, recipient, TOKEN_ID); } #[test] -#[should_panic(expected: ('ERC1155: invalid sender', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: invalid sender',))] fn test_safe_transfer_from_from_zero() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); dispatcher.safe_transfer_from(ZERO(), owner, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: invalid sender', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: invalid sender',))] fn test_safeTransferFrom_from_zero() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); dispatcher.safeTransferFrom(ZERO(), owner, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: invalid receiver', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: invalid receiver',))] fn test_safe_transfer_from_to_zero() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); dispatcher.safe_transfer_from(owner, ZERO(), TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: invalid receiver', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: invalid receiver',))] fn test_safeTransferFrom_to_zero() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); dispatcher.safeTransferFrom(owner, ZERO(), TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: unauthorized operator', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: unauthorized operator',))] fn test_safe_transfer_from_unauthorized() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); dispatcher.safe_transfer_from(OTHER(), owner, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: unauthorized operator', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: unauthorized operator',))] fn test_safeTransferFrom_unauthorized() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); dispatcher.safeTransferFrom(OTHER(), owner, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: insufficient balance', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: insufficient balance',))] fn test_safe_transfer_from_insufficient_balance() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); dispatcher.safe_transfer_from(owner, OTHER(), TOKEN_ID, TOKEN_VALUE + 1, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: insufficient balance', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: insufficient balance',))] fn test_safeTransferFrom_insufficient_balance() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); dispatcher.safeTransferFrom(owner, OTHER(), TOKEN_ID, TOKEN_VALUE + 1, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: safe transfer failed', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: safe transfer failed',))] fn test_safe_transfer_from_non_account_non_receiver() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let non_receiver = setup_src5(); dispatcher.safe_transfer_from(owner, non_receiver, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: safe transfer failed', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: safe transfer failed',))] fn test_safeTransferFrom_non_account_non_receiver() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let non_receiver = setup_src5(); dispatcher.safeTransferFrom(owner, non_receiver, TOKEN_ID, TOKEN_VALUE, EMPTY_DATA()); @@ -370,7 +382,7 @@ fn test_safeTransferFrom_non_account_non_receiver() { #[test] fn test_safe_batch_transfer_from_to_receiver() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; let recipient = setup_receiver(); let (token_ids, values) = get_ids_and_values(); @@ -378,14 +390,15 @@ fn test_safe_batch_transfer_from_to_receiver() { assert_state_before_transfer_batch(dispatcher, owner, recipient, token_ids, values); dispatcher.safe_batch_transfer_from(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(contract, owner, owner, recipient, token_ids, values); + spy.assert_only_event_transfer_batch(contract, owner, owner, recipient, token_ids, values); assert_state_after_transfer_batch(dispatcher, owner, recipient, token_ids, values); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safe_batch_transfer_from_to_camel_receiver() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; let recipient = setup_camel_receiver(); let (token_ids, values) = get_ids_and_values(); @@ -393,14 +406,14 @@ fn test_safe_batch_transfer_from_to_camel_receiver() { assert_state_before_transfer_batch(dispatcher, owner, recipient, token_ids, values); dispatcher.safe_batch_transfer_from(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(contract, owner, owner, recipient, token_ids, values); + spy.assert_only_event_transfer_batch(contract, owner, owner, recipient, token_ids, values); assert_state_after_transfer_batch(dispatcher, owner, recipient, token_ids, values); } #[test] fn test_safeBatchTransferFrom_to_receiver() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; let recipient = setup_receiver(); let (token_ids, values) = get_ids_and_values(); @@ -408,14 +421,15 @@ fn test_safeBatchTransferFrom_to_receiver() { assert_state_before_transfer_batch(dispatcher, owner, recipient, token_ids, values); dispatcher.safeBatchTransferFrom(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(contract, owner, owner, recipient, token_ids, values); + spy.assert_only_event_transfer_batch(contract, owner, owner, recipient, token_ids, values); assert_state_after_transfer_batch(dispatcher, owner, recipient, token_ids, values); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_safeBatchTransferFrom_to_camel_receiver() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; let recipient = setup_camel_receiver(); let (token_ids, values) = get_ids_and_values(); @@ -423,37 +437,41 @@ fn test_safeBatchTransferFrom_to_camel_receiver() { assert_state_before_transfer_batch(dispatcher, owner, recipient, token_ids, values); dispatcher.safeBatchTransferFrom(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(contract, owner, owner, recipient, token_ids, values); + spy.assert_only_event_transfer_batch(contract, owner, owner, recipient, token_ids, values); assert_state_after_transfer_batch(dispatcher, owner, recipient, token_ids, values); } #[test] fn test_safe_batch_transfer_from_to_account() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; - let recipient = setup_account_with_salt(1); let (token_ids, values) = get_ids_and_values(); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); + spy.drop_all_events(); assert_state_before_transfer_batch(dispatcher, owner, recipient, token_ids, values); dispatcher.safe_batch_transfer_from(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(contract, owner, owner, recipient, token_ids, values); + spy.assert_only_event_transfer_batch(contract, owner, owner, recipient, token_ids, values); assert_state_after_transfer_batch(dispatcher, owner, recipient, token_ids, values); } #[test] fn test_safeBatchTransferFrom_to_account() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; - let recipient = setup_account_with_salt(1); let (token_ids, values) = get_ids_and_values(); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); + spy.drop_all_events(); assert_state_before_transfer_batch(dispatcher, owner, recipient, token_ids, values); dispatcher.safeBatchTransferFrom(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(contract, owner, owner, recipient, token_ids, values); + spy.assert_only_event_transfer_batch(contract, owner, owner, recipient, token_ids, values); assert_state_after_transfer_batch(dispatcher, owner, recipient, token_ids, values); } @@ -461,101 +479,105 @@ fn test_safeBatchTransferFrom_to_account() { #[test] fn test_safe_batch_transfer_from_approved_operator() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; - let recipient = setup_account_with_salt(1); - let operator = OPERATOR(); let (token_ids, values) = get_ids_and_values(); + let operator = OPERATOR(); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); + spy.drop_all_events(); dispatcher.set_approval_for_all(operator, true); - assert_only_event_approval_for_all(contract, owner, operator, true); + spy.assert_only_event_approval_for_all(contract, owner, operator, true); assert_state_before_transfer_batch(dispatcher, owner, recipient, token_ids, values); - testing::set_contract_address(operator); + start_cheat_caller_address(dispatcher.contract_address, operator); dispatcher.safe_batch_transfer_from(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(contract, operator, owner, recipient, token_ids, values); + spy.assert_only_event_transfer_batch(contract, operator, owner, recipient, token_ids, values); assert_state_after_transfer_batch(dispatcher, owner, recipient, token_ids, values); } #[test] fn test_safeBatchTransferFrom_approved_operator() { - let (dispatcher, owner) = setup_dispatcher(); + let (mut spy, dispatcher, owner) = setup_dispatcher(); let contract = dispatcher.contract_address; - let recipient = setup_account_with_salt(1); - let operator = OPERATOR(); let (token_ids, values) = get_ids_and_values(); + let operator = OPERATOR(); + let recipient = RECIPIENT(); + deploy_another_account_at(owner, recipient); + spy.drop_all_events(); dispatcher.set_approval_for_all(operator, true); - assert_only_event_approval_for_all(contract, owner, operator, true); + spy.assert_only_event_approval_for_all(contract, owner, operator, true); assert_state_before_transfer_batch(dispatcher, owner, recipient, token_ids, values); - testing::set_contract_address(operator); + start_cheat_caller_address(dispatcher.contract_address, operator); dispatcher.safeBatchTransferFrom(owner, recipient, token_ids, values, EMPTY_DATA()); - assert_only_event_transfer_batch(contract, operator, owner, recipient, token_ids, values); + spy.assert_only_event_transfer_batch(contract, operator, owner, recipient, token_ids, values); assert_state_after_transfer_batch(dispatcher, owner, recipient, token_ids, values); } #[test] -#[should_panic(expected: ('ERC1155: invalid sender', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: invalid sender',))] fn test_safe_batch_transfer_from_from_zero() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let (token_ids, values) = get_ids_and_values(); dispatcher.safe_batch_transfer_from(ZERO(), owner, token_ids, values, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: invalid sender', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: invalid sender',))] fn test_safeBatchTransferFrom_from_zero() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let (token_ids, values) = get_ids_and_values(); dispatcher.safeBatchTransferFrom(ZERO(), owner, token_ids, values, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: invalid receiver', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: invalid receiver',))] fn test_safe_batch_transfer_from_to_zero() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let (token_ids, values) = get_ids_and_values(); dispatcher.safe_batch_transfer_from(owner, ZERO(), token_ids, values, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: invalid receiver', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: invalid receiver',))] fn test_safeBatchTransferFrom_to_zero() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let (token_ids, values) = get_ids_and_values(); dispatcher.safeBatchTransferFrom(owner, ZERO(), token_ids, values, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: unauthorized operator', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: unauthorized operator',))] fn test_safe_batch_transfer_from_unauthorized() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let (token_ids, values) = get_ids_and_values(); dispatcher.safe_batch_transfer_from(OTHER(), owner, token_ids, values, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: unauthorized operator', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: unauthorized operator',))] fn test_safeBatchTransferFrom_unauthorized() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let (token_ids, values) = get_ids_and_values(); dispatcher.safeBatchTransferFrom(OTHER(), owner, token_ids, values, EMPTY_DATA()); } #[test] -#[should_panic(expected: ('ERC1155: insufficient balance', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: insufficient balance',))] fn test_safe_batch_transfer_from_insufficient_balance() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let token_ids = array![TOKEN_ID, TOKEN_ID_2].span(); let values = array![TOKEN_VALUE + 1, TOKEN_VALUE_2].span(); @@ -563,9 +585,9 @@ fn test_safe_batch_transfer_from_insufficient_balance() { } #[test] -#[should_panic(expected: ('ERC1155: insufficient balance', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: insufficient balance',))] fn test_safeBatchTransferFrom_insufficient_balance() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let token_ids = array![TOKEN_ID, TOKEN_ID_2].span(); let values = array![TOKEN_VALUE + 1, TOKEN_VALUE_2].span(); @@ -573,9 +595,9 @@ fn test_safeBatchTransferFrom_insufficient_balance() { } #[test] -#[should_panic(expected: ('ERC1155: safe transfer failed', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: safe transfer failed',))] fn test_safe_batch_transfer_from_non_account_non_receiver() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let (token_ids, values) = get_ids_and_split_values(5); let non_receiver = setup_src5(); @@ -583,9 +605,9 @@ fn test_safe_batch_transfer_from_non_account_non_receiver() { } #[test] -#[should_panic(expected: ('ERC1155: safe transfer failed', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: safe transfer failed',))] fn test_safeBatchTransferFrom_non_account_non_receiver() { - let (dispatcher, owner) = setup_dispatcher(); + let (_, dispatcher, owner) = setup_dispatcher(); let (token_ids, values) = get_ids_and_split_values(5); let non_receiver = setup_src5(); @@ -598,39 +620,39 @@ fn test_safeBatchTransferFrom_non_account_non_receiver() { #[test] fn test_set_approval_for_all_and_is_approved_for_all() { - let (dispatcher, _) = setup_dispatcher(); + let (mut spy, dispatcher, _) = setup_dispatcher(); let contract = dispatcher.contract_address; - testing::set_contract_address(OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); let not_approved_for_all = !dispatcher.is_approved_for_all(OWNER(), OPERATOR()); assert!(not_approved_for_all); dispatcher.set_approval_for_all(OPERATOR(), true); - assert_only_event_approval_for_all(contract, OWNER(), OPERATOR(), true); + spy.assert_only_event_approval_for_all(contract, OWNER(), OPERATOR(), true); let is_approved_for_all = dispatcher.is_approved_for_all(OWNER(), OPERATOR()); assert!(is_approved_for_all); dispatcher.set_approval_for_all(OPERATOR(), false); - assert_only_event_approval_for_all(contract, OWNER(), OPERATOR(), false); + spy.assert_only_event_approval_for_all(contract, OWNER(), OPERATOR(), false); let not_approved_for_all = !dispatcher.is_approved_for_all(OWNER(), OPERATOR()); assert!(not_approved_for_all); } #[test] -#[should_panic(expected: ('ERC1155: self approval', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: self approval',))] fn test_set_approval_for_all_owner_equal_operator_true() { - let (dispatcher, _) = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, dispatcher, _) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.set_approval_for_all(OWNER(), true); } #[test] -#[should_panic(expected: ('ERC1155: self approval', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: self approval',))] fn test_set_approval_for_all_owner_equal_operator_false() { - let (dispatcher, _) = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, dispatcher, _) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.set_approval_for_all(OWNER(), false); } @@ -640,39 +662,39 @@ fn test_set_approval_for_all_owner_equal_operator_false() { #[test] fn test_setApprovalForAll_and_isApprovedForAll() { - let (dispatcher, _) = setup_dispatcher(); + let (mut spy, dispatcher, _) = setup_dispatcher(); let contract = dispatcher.contract_address; - testing::set_contract_address(OWNER()); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); let not_approved_for_all = !dispatcher.isApprovedForAll(OWNER(), OPERATOR()); assert!(not_approved_for_all); dispatcher.setApprovalForAll(OPERATOR(), true); - assert_only_event_approval_for_all(contract, OWNER(), OPERATOR(), true); + spy.assert_only_event_approval_for_all(contract, OWNER(), OPERATOR(), true); let is_approved_for_all = dispatcher.isApprovedForAll(OWNER(), OPERATOR()); assert!(is_approved_for_all); dispatcher.setApprovalForAll(OPERATOR(), false); - assert_only_event_approval_for_all(contract, OWNER(), OPERATOR(), false); + spy.assert_only_event_approval_for_all(contract, OWNER(), OPERATOR(), false); let not_approved_for_all = !dispatcher.isApprovedForAll(OWNER(), OPERATOR()); assert!(not_approved_for_all); } #[test] -#[should_panic(expected: ('ERC1155: self approval', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: self approval',))] fn test_setApprovalForAll_owner_equal_operator_true() { - let (dispatcher, _) = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, dispatcher, _) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.set_approval_for_all(OWNER(), true); } #[test] -#[should_panic(expected: ('ERC1155: self approval', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('ERC1155: self approval',))] fn test_setApprovalForAll_owner_equal_operator_false() { - let (dispatcher, _) = setup_dispatcher(); - testing::set_contract_address(OWNER()); + let (_, dispatcher, _) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.setApprovalForAll(OWNER(), false); } @@ -682,69 +704,69 @@ fn test_setApprovalForAll_owner_equal_operator_false() { #[test] fn test_transfer_ownership() { - let (dispatcher, owner) = setup_dispatcher(); - testing::set_contract_address(owner); + let (mut spy, dispatcher, owner) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, owner); dispatcher.transfer_ownership(OTHER()); - assert_event_ownership_transferred(dispatcher.contract_address, owner, OTHER()); + spy.assert_event_ownership_transferred(dispatcher.contract_address, owner, OTHER()); assert_eq!(dispatcher.owner(), OTHER()); } #[test] -#[should_panic(expected: ('New owner is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('New owner is the zero address',))] fn test_transfer_ownership_to_zero() { - let (dispatcher, owner) = setup_dispatcher(); - testing::set_contract_address(owner); + let (_, dispatcher, owner) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, owner); dispatcher.transfer_ownership(ZERO()); } #[test] -#[should_panic(expected: ('Caller is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_transfer_ownership_from_zero() { - let (dispatcher, _) = setup_dispatcher(); - testing::set_contract_address(ZERO()); + let (_, dispatcher, _) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, ZERO()); dispatcher.transfer_ownership(OTHER()); } #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_transfer_ownership_from_nonowner() { - let (dispatcher, _) = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, dispatcher, _) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.transfer_ownership(OTHER()); } #[test] fn test_transferOwnership() { - let (dispatcher, owner) = setup_dispatcher(); - testing::set_contract_address(owner); + let (mut spy, dispatcher, owner) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, owner); dispatcher.transferOwnership(OTHER()); - assert_event_ownership_transferred(dispatcher.contract_address, owner, OTHER()); + spy.assert_event_ownership_transferred(dispatcher.contract_address, owner, OTHER()); assert_eq!(dispatcher.owner(), OTHER()); } #[test] -#[should_panic(expected: ('New owner is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('New owner is the zero address',))] fn test_transferOwnership_to_zero() { - let (dispatcher, owner) = setup_dispatcher(); - testing::set_contract_address(owner); + let (_, dispatcher, owner) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, owner); dispatcher.transferOwnership(ZERO()); } #[test] -#[should_panic(expected: ('Caller is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_transferOwnership_from_zero() { - let (dispatcher, _) = setup_dispatcher(); - testing::set_contract_address(ZERO()); + let (_, dispatcher, _) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, ZERO()); dispatcher.transferOwnership(OTHER()); } #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_transferOwnership_from_nonowner() { - let (dispatcher, _) = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, dispatcher, _) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.transferOwnership(OTHER()); } @@ -754,53 +776,53 @@ fn test_transferOwnership_from_nonowner() { #[test] fn test_renounce_ownership() { - let (dispatcher, owner) = setup_dispatcher(); - testing::set_contract_address(owner); + let (mut spy, dispatcher, owner) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, owner); dispatcher.renounce_ownership(); - assert_event_ownership_transferred(dispatcher.contract_address, owner, ZERO()); + spy.assert_event_ownership_transferred(dispatcher.contract_address, owner, ZERO()); assert!(dispatcher.owner().is_zero()); } #[test] -#[should_panic(expected: ('Caller is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_renounce_ownership_from_zero_address() { - let (dispatcher, _) = setup_dispatcher(); - testing::set_contract_address(ZERO()); + let (_, dispatcher, _) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, ZERO()); dispatcher.renounce_ownership(); } #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_renounce_ownership_from_nonowner() { - let (dispatcher, _) = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, dispatcher, _) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.renounce_ownership(); } #[test] fn test_renounceOwnership() { - let (dispatcher, owner) = setup_dispatcher(); - testing::set_contract_address(owner); + let (mut spy, dispatcher, owner) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, owner); dispatcher.renounceOwnership(); - assert_event_ownership_transferred(dispatcher.contract_address, owner, ZERO()); + spy.assert_event_ownership_transferred(dispatcher.contract_address, owner, ZERO()); assert!(dispatcher.owner().is_zero()); } #[test] -#[should_panic(expected: ('Caller is the zero address', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is the zero address',))] fn test_renounceOwnership_from_zero_address() { - let (dispatcher, _) = setup_dispatcher(); - testing::set_contract_address(ZERO()); + let (_, dispatcher, _) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, ZERO()); dispatcher.renounceOwnership(); } #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_renounceOwnership_from_nonowner() { - let (dispatcher, _) = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, dispatcher, _) = setup_dispatcher(); + start_cheat_caller_address(dispatcher.contract_address, OTHER()); dispatcher.renounceOwnership(); } @@ -809,53 +831,57 @@ fn test_renounceOwnership_from_nonowner() { // #[test] -#[should_panic(expected: ('Caller is not the owner', 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ('Caller is not the owner',))] fn test_upgrade_unauthorized() { - let (v1, _) = setup_dispatcher(); - testing::set_contract_address(OTHER()); + let (_, v1, _) = setup_dispatcher(); + start_cheat_caller_address(v1.contract_address, OTHER()); v1.upgrade(CLASS_HASH_ZERO()); } #[test] -#[should_panic(expected: ('Class hash cannot be zero', 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ('Class hash cannot be zero',))] fn test_upgrade_with_class_hash_zero() { - let (v1, owner) = setup_dispatcher(); + let (_, v1, owner) = setup_dispatcher(); - testing::set_contract_address(owner); + start_cheat_caller_address(v1.contract_address, owner); v1.upgrade(CLASS_HASH_ZERO()); } #[test] fn test_upgraded_event() { - let (v1, owner) = setup_dispatcher(); + let (mut spy, v1, owner) = setup_dispatcher(); let v2_class_hash = V2_CLASS_HASH(); - testing::set_contract_address(owner); + start_cheat_caller_address(v1.contract_address, owner); v1.upgrade(v2_class_hash); - assert_only_event_upgraded(v1.contract_address, v2_class_hash); + spy.assert_only_event_upgraded(v1.contract_address, v2_class_hash); } #[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +#[feature("safe_dispatcher")] fn test_v2_missing_camel_selector() { - let (v1, owner) = setup_dispatcher(); + let (_, v1, owner) = setup_dispatcher(); let v2_class_hash = V2_CLASS_HASH(); - testing::set_contract_address(owner); + start_cheat_caller_address(v1.contract_address, owner); v1.upgrade(v2_class_hash); - let dispatcher = IERC1155CamelDispatcher { contract_address: v1.contract_address }; - dispatcher.balanceOf(owner, TOKEN_ID); + let safe_dispatcher = IERC1155CamelSafeDispatcher { contract_address: v1.contract_address }; + let panic_data = safe_dispatcher.balanceOf(owner, TOKEN_ID).unwrap_err(); + + utils::assert_entrypoint_not_found_error( + panic_data, selector!("balanceOf"), v1.contract_address + ) } #[test] fn test_state_persists_after_upgrade() { - let (v1, owner) = setup_dispatcher(); + let (_, v1, owner) = setup_dispatcher(); let recipient = setup_receiver(); let v2_class_hash = V2_CLASS_HASH(); - testing::set_contract_address(owner); + start_cheat_caller_address(v1.contract_address, owner); v1.safeTransferFrom(owner, recipient, TOKEN_ID, TOKEN_VALUE, array![].span()); // Check RECIPIENT balance v1 diff --git a/src/tests/presets/test_erc20.cairo b/src/tests/presets/test_erc20.cairo index 2625ac249..8d10d7ab8 100644 --- a/src/tests/presets/test_erc20.cairo +++ b/src/tests/presets/test_erc20.cairo @@ -482,13 +482,9 @@ fn test_v2_missing_camel_selector() { }; let mut panic_data = safe_dispatcher.totalSupply().unwrap_err(); - let selector = selector!("totalSupply"); - let expected_panic_message = format!( - "Entry point selector {} not found in contract {}", - selector.into_base_16_string(), - v1.contract_address.into_base_16_string() - ); - assert_eq!(utils::panic_data_to_byte_array(panic_data), expected_panic_message); + utils::assert_entrypoint_not_found_error( + panic_data, selector!("totalSupply"), v1.contract_address + ) } #[test] diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index 1c31f8a61..509c33ee1 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -4,7 +4,10 @@ pub(crate) mod deployment; pub(crate) mod events; pub(crate) mod signing; -pub use common::{panic_data_to_byte_array, to_base_16_string, IntoBase16StringTrait}; +pub use common::{ + panic_data_to_byte_array, to_base_16_string, IntoBase16StringTrait, + assert_entrypoint_not_found_error +}; pub use deployment::{ declare_class, declare_and_deploy, declare_and_deploy_at, deploy, deploy_at, deploy_another_at }; diff --git a/src/tests/utils/common.cairo b/src/tests/utils/common.cairo index c48a85344..853be448b 100644 --- a/src/tests/utils/common.cairo +++ b/src/tests/utils/common.cairo @@ -1,4 +1,5 @@ use core::to_byte_array::FormatAsByteArray; +use starknet::ContractAddress; /// Converts panic data into a string (ByteArray). /// @@ -34,3 +35,16 @@ pub impl IntoBase16String> of IntoBase16StringTrait { to_base_16_string(self.into()) } } + +/// Asserts that the panic data is an "Entrypoint not found" error, following the starknet foundry +/// emitted error format. +pub fn assert_entrypoint_not_found_error( + panic_data: Array, selector: felt252, contract_address: ContractAddress +) { + let expected_panic_message = format!( + "Entry point selector {} not found in contract {}", + selector.into_base_16_string(), + contract_address.into_base_16_string() + ); + assert!(panic_data_to_byte_array(panic_data) == expected_panic_message); +} From c34d4d080acdf24c3a1daffc074d84fad4deff47 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Mon, 22 Jul 2024 14:22:34 -0400 Subject: [PATCH 13/20] Migrate cryptography and UDC tests (#1059) * fix import name * migrate cryptography tests * migrate udc tests * fix fmt * fix impl name * fix event assertions * re-add class hash helper --- src/tests.cairo | 4 +- src/tests/cryptography/test_snip12.cairo | 4 +- src/tests/presets.cairo | 4 +- src/tests/presets/test_erc1155.cairo | 2 +- .../presets/test_universal_deployer.cairo | 123 ++++++++++-------- 5 files changed, 79 insertions(+), 58 deletions(-) diff --git a/src/tests.cairo b/src/tests.cairo index a3fa799ac..f64c4098e 100644 --- a/src/tests.cairo +++ b/src/tests.cairo @@ -2,8 +2,8 @@ mod access; // #[cfg(test)] // mod account; -// #[cfg(test)] -// mod cryptography; +#[cfg(test)] +mod cryptography; #[cfg(test)] mod introspection; mod mocks; diff --git a/src/tests/cryptography/test_snip12.cairo b/src/tests/cryptography/test_snip12.cairo index e0caeffc1..6ffa80df8 100644 --- a/src/tests/cryptography/test_snip12.cairo +++ b/src/tests/cryptography/test_snip12.cairo @@ -4,6 +4,7 @@ use openzeppelin::tests::utils::constants::{OWNER, RECIPIENT}; use openzeppelin::utils::cryptography::snip12::{ STARKNET_DOMAIN_TYPE_HASH, StarknetDomain, StructHash, OffchainMessageHashImpl, SNIP12Metadata }; +use snforge_std::{start_cheat_chain_id, test_address}; use starknet::ContractAddress; const MESSAGE_TYPE_HASH: felt252 = @@ -59,7 +60,8 @@ fn test_OffchainMessageHashImpl() { let message = Message { recipient: RECIPIENT(), amount: 100, nonce: 1, expiry: 1000 }; let domain = StarknetDomain { name: 'DAPP_NAME', version: 'v1', chain_id: 'TEST', revision: 1 }; - starknet::testing::set_chain_id('TEST'); + let contract_address = test_address(); + start_cheat_chain_id(contract_address, 'TEST'); let expected = poseidon_hash_span( array!['StarkNet Message', domain.hash_struct(), OWNER().into(), message.hash_struct()] diff --git a/src/tests/presets.cairo b/src/tests/presets.cairo index d8b4fdd0e..cc599a931 100644 --- a/src/tests/presets.cairo +++ b/src/tests/presets.cairo @@ -3,6 +3,4 @@ mod test_erc1155; mod test_erc20; mod test_erc721; // mod test_eth_account; -// mod test_universal_deployer; - - +mod test_universal_deployer; diff --git a/src/tests/presets/test_erc1155.cairo b/src/tests/presets/test_erc1155.cairo index 454e97ae9..57c597612 100644 --- a/src/tests/presets/test_erc1155.cairo +++ b/src/tests/presets/test_erc1155.cairo @@ -9,7 +9,7 @@ use openzeppelin::tests::token::erc1155::common::{ setup_account, setup_receiver, setup_camel_receiver, deploy_another_account_at, setup_src5 }; use openzeppelin::tests::token::erc1155::common::{get_ids_and_values, get_ids_and_split_values}; -use openzeppelin::tests::upgrades::common::UpgradableSpyHelpers; +use openzeppelin::tests::upgrades::common::UpgradeableSpyHelpers; use openzeppelin::tests::utils::constants::{ EMPTY_DATA, ZERO, OWNER, RECIPIENT, CLASS_HASH_ZERO, OPERATOR, OTHER, TOKEN_ID, TOKEN_ID_2, TOKEN_VALUE, TOKEN_VALUE_2 diff --git a/src/tests/presets/test_universal_deployer.cairo b/src/tests/presets/test_universal_deployer.cairo index e5247a9ae..e5874bcbc 100644 --- a/src/tests/presets/test_universal_deployer.cairo +++ b/src/tests/presets/test_universal_deployer.cairo @@ -1,7 +1,7 @@ use openzeppelin::presets::universal_deployer::UniversalDeployer::ContractDeployed; use openzeppelin::presets::universal_deployer::UniversalDeployer; -use openzeppelin::tests::mocks::erc20_mocks::DualCaseERC20Mock; use openzeppelin::tests::utils::constants::{NAME, SYMBOL, SUPPLY, SALT, CALLER, RECIPIENT}; +use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::tests::utils; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use openzeppelin::utils::deployments::{DeployerInfo, calculate_contract_address_from_udc}; @@ -9,13 +9,12 @@ use openzeppelin::utils::interfaces::{ IUniversalDeployerDispatcher, IUniversalDeployerDispatcherTrait }; use openzeppelin::utils::serde::SerializedAppend; -use starknet::ClassHash; -use starknet::ContractAddress; -use starknet::testing; +use snforge_std::{EventSpy, spy_events, declare, start_cheat_caller_address}; +use starknet::{ClassHash, ContractAddress}; fn ERC20_CLASS_HASH() -> ClassHash { - DualCaseERC20Mock::TEST_CLASS_HASH.try_into().unwrap() + utils::declare_class("DualCaseERC20Mock").class_hash } fn ERC20_CALLDATA() -> Span { @@ -28,35 +27,45 @@ fn ERC20_CALLDATA() -> Span { } fn deploy_udc() -> IUniversalDeployerDispatcher { - let calldata = array![]; - let address = utils::deploy(UniversalDeployer::TEST_CLASS_HASH, calldata); + let mut calldata = array![]; + let address = utils::declare_and_deploy("UniversalDeployer", calldata); IUniversalDeployerDispatcher { contract_address: address } } #[test] fn test_deploy_from_zero() { let udc = deploy_udc(); + let caller = CALLER(); + + // Deploy args + let erc20_class_hash = ERC20_CLASS_HASH(); + let salt = SALT; let from_zero = true; - testing::set_contract_address(CALLER()); + let erc20_calldata = ERC20_CALLDATA(); + + let mut spy = spy_events(); + start_cheat_caller_address(udc.contract_address, caller); // Check address let expected_addr = calculate_contract_address_from_udc( - SALT, ERC20_CLASS_HASH(), ERC20_CALLDATA(), Option::None + salt, erc20_class_hash, erc20_calldata, Option::None ); - let deployed_addr = udc.deploy_contract(ERC20_CLASS_HASH(), SALT, from_zero, ERC20_CALLDATA()); + let deployed_addr = udc.deploy_contract(erc20_class_hash, salt, from_zero, erc20_calldata); assert_eq!(expected_addr, deployed_addr); - // Check event - assert_only_event_contract_deployed( - udc.contract_address, - deployed_addr, - CALLER(), - from_zero, - ERC20_CLASS_HASH(), - ERC20_CALLDATA(), - SALT - ); + // Drop ERC20 event, check deploy event + spy.drop_event(); + spy + .assert_only_event_contract_deployed( + udc.contract_address, + deployed_addr, + caller, + from_zero, + erc20_class_hash, + erc20_calldata, + salt + ); // Check deployment let erc20 = IERC20Dispatcher { contract_address: deployed_addr }; @@ -67,29 +76,39 @@ fn test_deploy_from_zero() { #[test] fn test_deploy_not_from_zero() { let udc = deploy_udc(); + let caller = CALLER(); + + // Deploy args + let erc20_class_hash = ERC20_CLASS_HASH(); + let salt = SALT; let from_zero = false; - testing::set_contract_address(CALLER()); + let erc20_calldata = ERC20_CALLDATA(); + + let mut spy = spy_events(); + start_cheat_caller_address(udc.contract_address, caller); // Check address let expected_addr = calculate_contract_address_from_udc( - SALT, - ERC20_CLASS_HASH(), - ERC20_CALLDATA(), - Option::Some(DeployerInfo { caller_address: CALLER(), udc_address: udc.contract_address }) + salt, + erc20_class_hash, + erc20_calldata, + Option::Some(DeployerInfo { caller_address: caller, udc_address: udc.contract_address }) ); - let deployed_addr = udc.deploy_contract(ERC20_CLASS_HASH(), SALT, from_zero, ERC20_CALLDATA()); + let deployed_addr = udc.deploy_contract(erc20_class_hash, salt, from_zero, erc20_calldata); assert_eq!(expected_addr, deployed_addr); - // Check event - assert_only_event_contract_deployed( - udc.contract_address, - deployed_addr, - CALLER(), - from_zero, - ERC20_CLASS_HASH(), - ERC20_CALLDATA(), - SALT - ); + // Drop ERC20 event, check deploy event + spy.drop_event(); + spy + .assert_only_event_contract_deployed( + udc.contract_address, + deployed_addr, + caller, + from_zero, + erc20_class_hash, + erc20_calldata, + salt + ); // Check deployment let erc20 = IERC20Dispatcher { contract_address: deployed_addr }; @@ -101,19 +120,21 @@ fn test_deploy_not_from_zero() { // Helpers // -fn assert_only_event_contract_deployed( - contract: ContractAddress, - address: ContractAddress, - deployer: ContractAddress, - from_zero: bool, - class_hash: ClassHash, - calldata: Span, - salt: felt252 -) { - let event = utils::pop_log::(contract).unwrap(); - let expected = UniversalDeployer::Event::ContractDeployed( - ContractDeployed { address, deployer, from_zero, class_hash, calldata, salt } - ); - assert!(event == expected); - utils::assert_no_events_left(contract); +#[generate_trait] +impl UniversalDeployerHelpersImpl of UniversalDeployerSpyHelpers { + fn assert_only_event_contract_deployed( + ref self: EventSpy, + contract: ContractAddress, + address: ContractAddress, + deployer: ContractAddress, + from_zero: bool, + class_hash: ClassHash, + calldata: Span, + salt: felt252 + ) { + let expected = UniversalDeployer::Event::ContractDeployed( + ContractDeployed { address, deployer, from_zero, class_hash, calldata, salt } + ); + self.assert_only_event(contract, expected); + } } From ba8004c029d963178722952ba73130c322c78636 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 23 Jul 2024 23:30:50 +0200 Subject: [PATCH 14/20] Migrate eth account tests (#1058) * feat: update dual_eth_account tests * feat: update eth_account tests * refactor: remove unused helpers * refactor: some inconsistencies * Update src/tests/account/ethereum/test_eth_account.cairo Co-authored-by: immrsd <103599616+immrsd@users.noreply.github.com> * feat: apply review updates * refactor: format files * Update src/tests/account/ethereum/test_eth_account.cairo Co-authored-by: Andrew Fleming --------- Co-authored-by: immrsd <103599616+immrsd@users.noreply.github.com> Co-authored-by: Andrew Fleming --- src/tests.cairo | 4 +- src/tests/account/ethereum.cairo | 1 + src/tests/account/ethereum/common.cairo | 122 ++++---- .../ethereum/test_dual_eth_account.cairo | 153 ++++------ .../account/ethereum/test_eth_account.cairo | 268 ++++++++---------- src/tests/account/starknet.cairo | 5 +- src/tests/account/starknet/common.cairo | 56 ++-- src/tests/account/test_secp256k1.cairo | 18 +- src/tests/account/test_signature.cairo | 27 +- src/tests/utils.cairo | 8 +- src/tests/utils/constants.cairo | 20 ++ src/tests/utils/signing.cairo | 20 +- 12 files changed, 335 insertions(+), 367 deletions(-) diff --git a/src/tests.cairo b/src/tests.cairo index f64c4098e..fab03b247 100644 --- a/src/tests.cairo +++ b/src/tests.cairo @@ -1,7 +1,7 @@ #[cfg(test)] mod access; -// #[cfg(test)] -// mod account; +#[cfg(test)] +mod account; #[cfg(test)] mod cryptography; #[cfg(test)] diff --git a/src/tests/account/ethereum.cairo b/src/tests/account/ethereum.cairo index d6fcf488b..2d87a5992 100644 --- a/src/tests/account/ethereum.cairo +++ b/src/tests/account/ethereum.cairo @@ -2,3 +2,4 @@ pub(crate) mod common; mod test_dual_eth_account; mod test_eth_account; + diff --git a/src/tests/account/ethereum/common.cairo b/src/tests/account/ethereum/common.cairo index faf88a25c..58f9a809f 100644 --- a/src/tests/account/ethereum/common.cairo +++ b/src/tests/account/ethereum/common.cairo @@ -1,104 +1,94 @@ +use core::hash::{HashStateTrait, HashStateExTrait}; +use core::poseidon::PoseidonTrait; use core::poseidon::poseidon_hash_span; use core::starknet::secp256_trait::Secp256PointTrait; use openzeppelin::account::EthAccountComponent::{OwnerAdded, OwnerRemoved}; use openzeppelin::account::EthAccountComponent; use openzeppelin::account::interface::EthPublicKey; use openzeppelin::account::utils::signature::EthSignature; -use openzeppelin::tests::mocks::erc20_mocks::DualCaseERC20Mock; +use openzeppelin::tests::utils::constants::TRANSACTION_HASH; use openzeppelin::tests::utils::constants::{NAME, SYMBOL}; +use openzeppelin::tests::utils::events::EventSpyExt; +use openzeppelin::tests::utils::signing::{Secp256k1KeyPair, Secp256k1KeyPairExt}; use openzeppelin::tests::utils; -use openzeppelin::token::erc20::interface::{IERC20DispatcherTrait, IERC20Dispatcher}; +use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use openzeppelin::utils::serde::SerializedAppend; -use starknet::ContractAddress; -use starknet::SyscallResultTrait; -use starknet::secp256_trait::Secp256Trait; -use starknet::secp256k1::Secp256k1Point; +use snforge_std::EventSpy; +use snforge_std::signature::secp256k1_curve::Secp256k1CurveSignerImpl; +use starknet::{ContractAddress, SyscallResultTrait}; #[derive(Drop)] pub(crate) struct SignedTransactionData { pub(crate) private_key: u256, pub(crate) public_key: EthPublicKey, - pub(crate) transaction_hash: felt252, + pub(crate) tx_hash: felt252, pub(crate) signature: EthSignature } -/// This signature was computed using ethers.js. -pub(crate) fn SIGNED_TX_DATA() -> SignedTransactionData { +pub(crate) fn SIGNED_TX_DATA(key_pair: Secp256k1KeyPair) -> SignedTransactionData { + let tx_hash = TRANSACTION_HASH; + let (r, s) = key_pair.sign(tx_hash.into()).unwrap(); SignedTransactionData { - private_key: 0x45397ee6ca34cb49060f1c303c6cb7ee2d6123e617601ef3e31ccf7bf5bef1f9, - public_key: NEW_ETH_PUBKEY(), - transaction_hash: 0x008f882c63d0396d216d57529fe29ad5e70b6cd51b47bd2458b0a4ccb2ba0957, - signature: EthSignature { - r: 0x82bb3efc0554ec181405468f273b0dbf935cca47182b22da78967d0770f7dcc3, - s: 0x6719fef30c11c74add873e4da0e1234deb69eae6a6bd4daa44b816dc199f3e86, - } + private_key: key_pair.secret_key, + public_key: key_pair.public_key, + tx_hash, + signature: EthSignature { r, s } } } -pub(crate) fn NEW_ETH_PUBKEY() -> EthPublicKey { - Secp256Trait::secp256_ec_new_syscall( - 0x829307f82a1883c2414503ba85fc85037f22c6fc6f80910801f6b01a4131da1e, - 0x2a23f7bddf3715d11767b1247eccc68c89e11b926e2615268db6ad1af8d8da96 - ) - .unwrap() - .unwrap() +pub(crate) fn get_accept_ownership_signature( + account_address: ContractAddress, current_owner: EthPublicKey, new_key_pair: Secp256k1KeyPair +) -> Span { + let msg_hash: u256 = PoseidonTrait::new() + .update_with('StarkNet Message') + .update_with('accept_ownership') + .update_with(account_address) + .update_with(current_owner.get_coordinates().unwrap_syscall()) + .finalize() + .into(); + + new_key_pair.serialized_sign(msg_hash).span() } pub(crate) fn deploy_erc20(recipient: ContractAddress, initial_supply: u256) -> IERC20Dispatcher { - let name = NAME(); - let symbol = SYMBOL(); let mut calldata = array![]; - calldata.append_serde(name); - calldata.append_serde(symbol); + calldata.append_serde(NAME()); + calldata.append_serde(SYMBOL()); calldata.append_serde(initial_supply); calldata.append_serde(recipient); - let address = utils::deploy(DualCaseERC20Mock::TEST_CLASS_HASH, calldata); + let address = utils::declare_and_deploy("DualCaseERC20Mock", calldata); IERC20Dispatcher { contract_address: address } } -pub(crate) fn get_points() -> (Secp256k1Point, Secp256k1Point) { - let curve_size = Secp256Trait::::get_curve_size(); - let point_1 = Secp256Trait::secp256_ec_get_point_from_x_syscall(curve_size, true) - .unwrap_syscall() - .unwrap(); - let point_2 = Secp256Trait::secp256_ec_get_point_from_x_syscall(curve_size, false) - .unwrap_syscall() - .unwrap(); - - (point_1, point_2) -} - -pub(crate) fn assert_event_owner_added(contract: ContractAddress, public_key: EthPublicKey) { - let event = utils::pop_log::(contract).unwrap(); - let new_owner_guid = get_guid_from_public_key(public_key); - let expected = EthAccountComponent::Event::OwnerAdded(OwnerAdded { new_owner_guid }); - assert!(event == expected); - - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("OwnerAdded")); - indexed_keys.append_serde(new_owner_guid); - utils::assert_indexed_keys(event, indexed_keys.span()); -} -pub(crate) fn assert_only_event_owner_added(contract: ContractAddress, public_key: EthPublicKey) { - assert_event_owner_added(contract, public_key); - utils::assert_no_events_left(contract); -} +#[generate_trait] +pub(crate) impl EthAccountSpyHelpersImpl of EthAccountSpyHelpers { + fn assert_event_owner_removed( + ref self: EventSpy, contract: ContractAddress, public_key: EthPublicKey + ) { + let removed_owner_guid = get_guid_from_public_key(public_key); + let expected = EthAccountComponent::Event::OwnerRemoved( + OwnerRemoved { removed_owner_guid } + ); + self.assert_emitted_single(contract, expected); + } -pub(crate) fn assert_event_owner_removed(contract: ContractAddress, public_key: EthPublicKey) { - let event = utils::pop_log::(contract).unwrap(); - let removed_owner_guid = get_guid_from_public_key(public_key); - let expected = EthAccountComponent::Event::OwnerRemoved(OwnerRemoved { removed_owner_guid }); - assert!(event == expected); + fn assert_event_owner_added( + ref self: EventSpy, contract: ContractAddress, public_key: EthPublicKey + ) { + let new_owner_guid = get_guid_from_public_key(public_key); + let expected = EthAccountComponent::Event::OwnerAdded(OwnerAdded { new_owner_guid }); + self.assert_emitted_single(contract, expected); + } - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("OwnerRemoved")); - indexed_keys.append_serde(removed_owner_guid); - utils::assert_indexed_keys(event, indexed_keys.span()); + fn assert_only_event_owner_added( + ref self: EventSpy, contract: ContractAddress, public_key: EthPublicKey + ) { + self.assert_event_owner_added(contract, public_key); + self.assert_no_events_left_from(contract); + } } fn get_guid_from_public_key(public_key: EthPublicKey) -> felt252 { diff --git a/src/tests/account/ethereum/test_dual_eth_account.cairo b/src/tests/account/ethereum/test_dual_eth_account.cairo index b627724d5..bad86716a 100644 --- a/src/tests/account/ethereum/test_dual_eth_account.cairo +++ b/src/tests/account/ethereum/test_dual_eth_account.cairo @@ -5,16 +5,14 @@ use openzeppelin::account::utils::secp256k1::{ }; use openzeppelin::account::utils::signature::EthSignature; use openzeppelin::introspection::interface::ISRC5_ID; -use openzeppelin::tests::mocks::eth_account_mocks::{ - CamelEthAccountPanicMock, CamelEthAccountMock, SnakeEthAccountMock, SnakeEthAccountPanicMock -}; -use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; -use openzeppelin::tests::utils::constants::ETH_PUBKEY; +use openzeppelin::tests::utils::constants::secp256k1::KEY_PAIR; +use openzeppelin::tests::utils::constants::{ETH_PUBKEY, NEW_ETH_PUBKEY, TRANSACTION_HASH}; +use openzeppelin::tests::utils::signing::Secp256k1KeyPairExt; use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; -use starknet::testing; +use snforge_std::start_cheat_caller_address; -use super::common::{NEW_ETH_PUBKEY, SIGNED_TX_DATA}; +use super::common::get_accept_ownership_signature; // // Setup @@ -24,7 +22,7 @@ fn setup_snake() -> (DualCaseEthAccount, EthAccountABIDispatcher) { let mut calldata = array![]; calldata.append_serde(ETH_PUBKEY()); - let target = utils::deploy(SnakeEthAccountMock::TEST_CLASS_HASH, calldata); + let target = utils::declare_and_deploy("SnakeEthAccountMock", calldata); ( DualCaseEthAccount { contract_address: target }, EthAccountABIDispatcher { contract_address: target } @@ -35,7 +33,7 @@ fn setup_camel() -> (DualCaseEthAccount, EthAccountABIDispatcher) { let mut calldata = array![]; calldata.append_serde(ETH_PUBKEY()); - let target = utils::deploy(CamelEthAccountMock::TEST_CLASS_HASH, calldata); + let target = utils::declare_and_deploy("CamelEthAccountMock", calldata); ( DualCaseEthAccount { contract_address: target }, EthAccountABIDispatcher { contract_address: target } @@ -44,13 +42,13 @@ fn setup_camel() -> (DualCaseEthAccount, EthAccountABIDispatcher) { fn setup_non_account() -> DualCaseEthAccount { let calldata = array![]; - let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); + let target = utils::declare_and_deploy("NonImplementingMock", calldata); DualCaseEthAccount { contract_address: target } } fn setup_account_panic() -> (DualCaseEthAccount, DualCaseEthAccount) { - let snake_target = utils::deploy(SnakeEthAccountPanicMock::TEST_CLASS_HASH, array![]); - let camel_target = utils::deploy(CamelEthAccountPanicMock::TEST_CLASS_HASH, array![]); + let snake_target = utils::declare_and_deploy("SnakeEthAccountPanicMock", array![]); + let camel_target = utils::declare_and_deploy("CamelEthAccountPanicMock", array![]); ( DualCaseEthAccount { contract_address: snake_target }, DualCaseEthAccount { contract_address: camel_target } @@ -64,15 +62,19 @@ fn setup_account_panic() -> (DualCaseEthAccount, DualCaseEthAccount) { #[test] fn test_dual_set_public_key() { let (snake_dispatcher, target) = setup_snake(); + let contract_address = snake_dispatcher.contract_address; + + start_cheat_caller_address(contract_address, contract_address); - testing::set_contract_address(snake_dispatcher.contract_address); + let key_pair = KEY_PAIR(); + let signature = get_accept_ownership_signature(contract_address, ETH_PUBKEY(), key_pair); - let new_public_key = NEW_ETH_PUBKEY(); - snake_dispatcher.set_public_key(new_public_key, get_accept_ownership_signature_snake()); - assert_eq!(target.get_public_key(), new_public_key); + snake_dispatcher.set_public_key(key_pair.public_key, signature); + assert_eq!(target.get_public_key(), key_pair.public_key); } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_set_public_key() { let dispatcher = setup_non_account(); @@ -80,7 +82,7 @@ fn test_dual_no_set_public_key() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_set_public_key_exists_and_panics() { let (dispatcher, _) = setup_account_panic(); dispatcher.set_public_key(NEW_ETH_PUBKEY(), array![].span()); @@ -93,6 +95,7 @@ fn test_dual_get_public_key() { } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_get_public_key() { let dispatcher = setup_non_account(); @@ -100,7 +103,7 @@ fn test_dual_no_get_public_key() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_get_public_key_exists_and_panics() { let (dispatcher, _) = setup_account_panic(); dispatcher.get_public_key(); @@ -109,20 +112,21 @@ fn test_dual_get_public_key_exists_and_panics() { #[test] fn test_dual_is_valid_signature() { let (snake_dispatcher, target) = setup_snake(); + let contract_address = snake_dispatcher.contract_address; - let data = SIGNED_TX_DATA(); - let hash = data.transaction_hash; - let mut serialized_signature = array![]; - data.signature.serialize(ref serialized_signature); + start_cheat_caller_address(contract_address, contract_address); + let key_pair = KEY_PAIR(); + let signature = get_accept_ownership_signature(contract_address, ETH_PUBKEY(), key_pair); - testing::set_contract_address(snake_dispatcher.contract_address); - target.set_public_key(data.public_key, get_accept_ownership_signature_snake()); + target.set_public_key(key_pair.public_key, signature); + let serialized_signature = key_pair.serialized_sign(TRANSACTION_HASH.into()); - let is_valid = snake_dispatcher.is_valid_signature(hash, serialized_signature); + let is_valid = snake_dispatcher.is_valid_signature(TRANSACTION_HASH, serialized_signature); assert_eq!(is_valid, starknet::VALIDATED); } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_is_valid_signature() { let hash = 0x0; @@ -133,7 +137,7 @@ fn test_dual_no_is_valid_signature() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_is_valid_signature_exists_and_panics() { let hash = 0x0; let signature = array![]; @@ -149,6 +153,7 @@ fn test_dual_supports_interface() { } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_supports_interface() { let dispatcher = setup_non_account(); @@ -156,7 +161,7 @@ fn test_dual_no_supports_interface() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_supports_interface_exists_and_panics() { let (dispatcher, _) = setup_account_panic(); dispatcher.supports_interface(ISRC5_ID); @@ -167,54 +172,64 @@ fn test_dual_supports_interface_exists_and_panics() { // #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_setPublicKey() { let (camel_dispatcher, target) = setup_camel(); - let new_public_key = NEW_ETH_PUBKEY(); + let contract_address = camel_dispatcher.contract_address; + + start_cheat_caller_address(contract_address, contract_address); - testing::set_contract_address(camel_dispatcher.contract_address); + let key_pair = KEY_PAIR(); + let signature = get_accept_ownership_signature(contract_address, ETH_PUBKEY(), key_pair); - camel_dispatcher.set_public_key(new_public_key, get_accept_ownership_signature_camel()); - assert_eq!(target.getPublicKey(), new_public_key); + camel_dispatcher.set_public_key(key_pair.public_key, signature); + assert_eq!(target.getPublicKey(), key_pair.public_key); } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ("Some error",))] fn test_dual_setPublicKey_exists_and_panics() { let (_, dispatcher) = setup_account_panic(); dispatcher.set_public_key(NEW_ETH_PUBKEY(), array![].span()); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_getPublicKey() { let (camel_dispatcher, _) = setup_camel(); assert_eq!(camel_dispatcher.get_public_key(), ETH_PUBKEY()); } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ("Some error",))] fn test_dual_getPublicKey_exists_and_panics() { let (_, dispatcher) = setup_account_panic(); dispatcher.get_public_key(); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_isValidSignature() { let (camel_dispatcher, target) = setup_camel(); + let contract_address = camel_dispatcher.contract_address; - let data = SIGNED_TX_DATA(); - let hash = data.transaction_hash; - let mut serialized_signature = array![]; - data.signature.serialize(ref serialized_signature); + start_cheat_caller_address(contract_address, contract_address); - testing::set_contract_address(camel_dispatcher.contract_address); - target.setPublicKey(data.public_key, get_accept_ownership_signature_camel()); + let key_pair = KEY_PAIR(); + let signature = get_accept_ownership_signature(contract_address, ETH_PUBKEY(), key_pair); - let is_valid = camel_dispatcher.is_valid_signature(hash, serialized_signature); + target.setPublicKey(key_pair.public_key, signature); + let serialized_signature = key_pair.serialized_sign(TRANSACTION_HASH.into()); + + let is_valid = camel_dispatcher.is_valid_signature(TRANSACTION_HASH, serialized_signature); assert_eq!(is_valid, starknet::VALIDATED); } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ("Some error",))] fn test_dual_isValidSignature_exists_and_panics() { let hash = 0x0; let signature = array![]; @@ -222,59 +237,3 @@ fn test_dual_isValidSignature_exists_and_panics() { let (_, dispatcher) = setup_account_panic(); dispatcher.is_valid_signature(hash, signature); } - -// -// Helpers -// - -fn get_accept_ownership_signature_snake() -> Span { - let mut output = array![]; - - // 0x03e8d3aa715dc5fc3b93c7572df7d6f227a6aad93a77873db3308b30897eee53 = - // PoseidonTrait::new() - // .update_with('StarkNet Message') - // .update_with('accept_ownership') - // .update_with(snake_dispatcher.contract_address) - // .update_with(ETH_PUBKEY().get_coordinates().unwrap_syscall()) - // .finalize(); - - // This signature was computed using ethers js sdk from the following values: - // - private_key: 0x45397ee6ca34cb49060f1c303c6cb7ee2d6123e617601ef3e31ccf7bf5bef1f9 - // - public_key: - // r: 0x829307f82a1883c2414503ba85fc85037f22c6fc6f80910801f6b01a4131da1e - // s: 0x2a23f7bddf3715d11767b1247eccc68c89e11b926e2615268db6ad1af8d8da96 - // - msg_hash: 0x03e8d3aa715dc5fc3b93c7572df7d6f227a6aad93a77873db3308b30897eee53 - EthSignature { - r: 0x7e1ff13cbdf03e92125a69cb1e4ad94f2178720d156df3827c8d3172484fbfd8, - s: 0x0def4eb71f21bc623c0ca896cb3356cee12504da7b19021d3253d433366e0a3e, - } - .serialize(ref output); - - output.span() -} - -fn get_accept_ownership_signature_camel() -> Span { - let mut output = array![]; - - // 0x048d4c831924c90963645d7473e0954d2ac37c1f20e201ed7c1778942df5d58d = - // PoseidonTrait::new() - // .update_with('StarkNet Message') - // .update_with('accept_ownership') - // .update_with(camel_dispatcher.contract_address) - // .update_with(ETH_PUBKEY().get_coordinates().unwrap_syscall()) - // .finalize(); - - // This signature was computed using ethers js sdk from the following values: - // - private_key: 0x45397ee6ca34cb49060f1c303c6cb7ee2d6123e617601ef3e31ccf7bf5bef1f9 - // - public_key: - // r: 0x829307f82a1883c2414503ba85fc85037f22c6fc6f80910801f6b01a4131da1e - // s: 0x2a23f7bddf3715d11767b1247eccc68c89e11b926e2615268db6ad1af8d8da96 - // - msg_hash: 0x048d4c831924c90963645d7473e0954d2ac37c1f20e201ed7c1778942df5d58d - EthSignature { - r: 0x7a0fa1e6bfc6a0b86cdbb9877551a108d42d3de50cb7a516e63fe5a26e5a9c52, - s: 0x3cc64ca8bf6963ae01125f0d932b8780ca0ed1612fb74a84d4f76593e6687b74, - } - .serialize(ref output); - - output.span() -} diff --git a/src/tests/account/ethereum/test_eth_account.cairo b/src/tests/account/ethereum/test_eth_account.cairo index 8c1a8e8d6..bb21bb6b9 100644 --- a/src/tests/account/ethereum/test_eth_account.cairo +++ b/src/tests/account/ethereum/test_eth_account.cairo @@ -1,7 +1,6 @@ use openzeppelin::account::EthAccountComponent::{InternalTrait, SRC6CamelOnlyImpl}; use openzeppelin::account::EthAccountComponent::{PublicKeyCamelImpl, PublicKeyImpl}; use openzeppelin::account::EthAccountComponent; -use openzeppelin::account::interface::EthPublicKey; use openzeppelin::account::interface::{EthAccountABIDispatcherTrait, EthAccountABIDispatcher}; use openzeppelin::account::interface::{ISRC6, ISRC6_ID}; use openzeppelin::account::utils::secp256k1::{ @@ -10,32 +9,27 @@ use openzeppelin::account::utils::secp256k1::{ use openzeppelin::account::utils::signature::EthSignature; use openzeppelin::introspection::interface::{ISRC5, ISRC5_ID}; use openzeppelin::tests::mocks::eth_account_mocks::DualCaseEthAccountMock; +use openzeppelin::tests::utils::constants::secp256k1::KEY_PAIR; use openzeppelin::tests::utils::constants::{ - ETH_PUBKEY, SALT, ZERO, OTHER, RECIPIENT, CALLER, QUERY_VERSION, MIN_TRANSACTION_VERSION + ETH_PUBKEY, NEW_ETH_PUBKEY, SALT, ZERO, OTHER, RECIPIENT, CALLER, QUERY_VERSION, + MIN_TRANSACTION_VERSION }; use openzeppelin::tests::utils; -use openzeppelin::token::erc20::interface::{IERC20DispatcherTrait, IERC20Dispatcher}; +use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::{ + cheat_signature_global, cheat_transaction_version_global, cheat_transaction_hash_global, + start_cheat_caller_address +}; +use snforge_std::{spy_events, test_address}; +use starknet::ContractAddress; use starknet::account::Call; -use starknet::testing; -use starknet::{contract_address_const, ContractAddress}; - -use super::common::{assert_only_event_owner_added, assert_event_owner_removed}; -use super::common::{deploy_erc20, SIGNED_TX_DATA, NEW_ETH_PUBKEY, SignedTransactionData}; - - -// -// Constants -// - -fn CLASS_HASH() -> felt252 { - DualCaseEthAccountMock::TEST_CLASS_HASH -} -fn ACCOUNT_ADDRESS() -> ContractAddress { - contract_address_const::<0x111111>() -} +use super::common::EthAccountSpyHelpers; +use super::common::{ + deploy_erc20, SIGNED_TX_DATA, SignedTransactionData, get_accept_ownership_signature +}; // // Setup @@ -54,28 +48,31 @@ fn COMPONENT_STATE() -> ComponentState { fn setup() -> ComponentState { let mut state = COMPONENT_STATE(); state.initializer(ETH_PUBKEY()); - utils::drop_event(ZERO()); state } -fn setup_dispatcher(data: Option<@SignedTransactionData>) -> EthAccountABIDispatcher { - testing::set_version(MIN_TRANSACTION_VERSION); - +fn setup_dispatcher(data: Option<@SignedTransactionData>) -> (EthAccountABIDispatcher, felt252) { let mut calldata = array![]; - if data.is_some() { - let data = data.unwrap(); + if let Option::Some(data) = data { let mut serialized_signature = array![]; data.signature.serialize(ref serialized_signature); - testing::set_signature(serialized_signature.span()); - testing::set_transaction_hash(*data.transaction_hash); + cheat_signature_global(serialized_signature.span()); + cheat_transaction_hash_global(*data.tx_hash); calldata.append_serde(*data.public_key); } else { calldata.append_serde(ETH_PUBKEY()); - } - let address = utils::deploy(CLASS_HASH(), calldata); - EthAccountABIDispatcher { contract_address: address } + }; + + let contract_class = utils::declare_class("DualCaseEthAccountMock"); + let address = utils::deploy(contract_class, calldata); + let dispatcher = EthAccountABIDispatcher { contract_address: address }; + + cheat_transaction_version_global(MIN_TRANSACTION_VERSION); + start_cheat_caller_address(address, ZERO()); + + (dispatcher, contract_class.class_hash.into()) } // @@ -85,8 +82,8 @@ fn setup_dispatcher(data: Option<@SignedTransactionData>) -> EthAccountABIDispat #[test] fn test_is_valid_signature() { let mut state = COMPONENT_STATE(); - let data = SIGNED_TX_DATA(); - let hash = data.transaction_hash; + let data = SIGNED_TX_DATA(KEY_PAIR()); + let hash = data.tx_hash; let mut bad_signature = data.signature; bad_signature.r += 1; @@ -109,8 +106,8 @@ fn test_is_valid_signature() { #[test] fn test_isValidSignature() { let mut state = COMPONENT_STATE(); - let data = SIGNED_TX_DATA(); - let hash = data.transaction_hash; + let data = SIGNED_TX_DATA(KEY_PAIR()); + let hash = data.tx_hash; let mut bad_signature = data.signature; @@ -137,93 +134,93 @@ fn test_isValidSignature() { #[test] fn test_validate_deploy() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let (account, class_hash) = setup_dispatcher(Option::Some(@SIGNED_TX_DATA(KEY_PAIR()))); // `__validate_deploy__` does not directly use the passed arguments. Their // values are already integrated in the tx hash. The passed arguments in this // testing context are decoupled from the signature and have no effect on the test. - let is_valid = account.__validate_deploy__(CLASS_HASH(), SALT, ETH_PUBKEY()); + let is_valid = account.__validate_deploy__(class_hash, SALT, ETH_PUBKEY()); assert_eq!(is_valid, starknet::VALIDATED); } #[test] -#[should_panic(expected: ('EthAccount: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('EthAccount: invalid signature',))] fn test_validate_deploy_invalid_signature_data() { - let mut data = SIGNED_TX_DATA(); - data.transaction_hash += 1; - let account = setup_dispatcher(Option::Some(@data)); + let mut data = SIGNED_TX_DATA(KEY_PAIR()); + data.tx_hash += 1; + let (account, class_hash) = setup_dispatcher(Option::Some(@data)); - account.__validate_deploy__(CLASS_HASH(), SALT, ETH_PUBKEY()); + account.__validate_deploy__(class_hash, SALT, ETH_PUBKEY()); } #[test] -#[should_panic(expected: ('Signature: Invalid format.', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Signature: Invalid format.',))] fn test_validate_deploy_invalid_signature_length() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let (account, class_hash) = setup_dispatcher(Option::Some(@SIGNED_TX_DATA(KEY_PAIR()))); let signature = array![0x1]; - testing::set_signature(signature.span()); + cheat_signature_global(signature.span()); - account.__validate_deploy__(CLASS_HASH(), SALT, ETH_PUBKEY()); + account.__validate_deploy__(class_hash, SALT, ETH_PUBKEY()); } #[test] -#[should_panic(expected: ('Signature: Invalid format.', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Signature: Invalid format.',))] fn test_validate_deploy_empty_signature() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let (account, class_hash) = setup_dispatcher(Option::Some(@SIGNED_TX_DATA(KEY_PAIR()))); let empty_sig = array![]; - testing::set_signature(empty_sig.span()); - account.__validate_deploy__(CLASS_HASH(), SALT, ETH_PUBKEY()); + cheat_signature_global(empty_sig.span()); + account.__validate_deploy__(class_hash, SALT, ETH_PUBKEY()); } #[test] fn test_validate_declare() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let (account, class_hash) = setup_dispatcher(Option::Some(@SIGNED_TX_DATA(KEY_PAIR()))); // `__validate_declare__` does not directly use the class_hash argument. Its // value is already integrated in the tx hash. The class_hash argument in this // testing context is decoupled from the signature and has no effect on the test. - let is_valid = account.__validate_declare__(CLASS_HASH()); + let is_valid = account.__validate_declare__(class_hash); assert_eq!(is_valid, starknet::VALIDATED); } #[test] -#[should_panic(expected: ('EthAccount: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('EthAccount: invalid signature',))] fn test_validate_declare_invalid_signature_data() { - let mut data = SIGNED_TX_DATA(); - data.transaction_hash += 1; - let account = setup_dispatcher(Option::Some(@data)); + let mut data = SIGNED_TX_DATA(KEY_PAIR()); + data.tx_hash += 1; + let (account, class_hash) = setup_dispatcher(Option::Some(@data)); - account.__validate_declare__(CLASS_HASH()); + account.__validate_declare__(class_hash); } #[test] -#[should_panic(expected: ('Signature: Invalid format.', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Signature: Invalid format.',))] fn test_validate_declare_invalid_signature_length() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let (account, class_hash) = setup_dispatcher(Option::Some(@SIGNED_TX_DATA(KEY_PAIR()))); let mut signature = array![]; signature.append(0x1); - testing::set_signature(signature.span()); + cheat_signature_global(signature.span()); - account.__validate_declare__(CLASS_HASH()); + account.__validate_declare__(class_hash); } #[test] -#[should_panic(expected: ('Signature: Invalid format.', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Signature: Invalid format.',))] fn test_validate_declare_empty_signature() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let (account, class_hash) = setup_dispatcher(Option::Some(@SIGNED_TX_DATA(KEY_PAIR()))); let empty_sig = array![]; - testing::set_signature(empty_sig.span()); + cheat_signature_global(empty_sig.span()); - account.__validate_declare__(CLASS_HASH()); + account.__validate_declare__(class_hash); } fn test_execute_with_version(version: Option) { - let data = SIGNED_TX_DATA(); - let account = setup_dispatcher(Option::Some(@data)); + let data = SIGNED_TX_DATA(KEY_PAIR()); + let (account, _) = setup_dispatcher(Option::Some(@data)); let erc20 = deploy_erc20(account.contract_address, 1000); let recipient = RECIPIENT(); @@ -240,7 +237,7 @@ fn test_execute_with_version(version: Option) { // Handle version for test if version.is_some() { - testing::set_version(version.unwrap()); + cheat_transaction_version_global(version.unwrap()); } // Execute @@ -267,7 +264,7 @@ fn test_execute_query_version() { } #[test] -#[should_panic(expected: ('EthAccount: invalid tx version', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('EthAccount: invalid tx version',))] fn test_execute_invalid_version() { test_execute_with_version(Option::Some(MIN_TRANSACTION_VERSION - 1)); } @@ -275,26 +272,26 @@ fn test_execute_invalid_version() { #[test] fn test_validate() { let calls = array![]; - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let (account, _) = setup_dispatcher(Option::Some(@SIGNED_TX_DATA(KEY_PAIR()))); let is_valid = account.__validate__(calls); assert_eq!(is_valid, starknet::VALIDATED); } #[test] -#[should_panic(expected: ('EthAccount: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('EthAccount: invalid signature',))] fn test_validate_invalid() { let calls = array![]; - let mut data = SIGNED_TX_DATA(); - data.transaction_hash += 1; - let account = setup_dispatcher(Option::Some(@data)); + let mut data = SIGNED_TX_DATA(KEY_PAIR()); + data.tx_hash += 1; + let (account, _) = setup_dispatcher(Option::Some(@data)); account.__validate__(calls); } #[test] fn test_multicall() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let (account, _) = setup_dispatcher(Option::Some(@SIGNED_TX_DATA(KEY_PAIR()))); let erc20 = deploy_erc20(account.contract_address, 1000); let recipient1 = RECIPIENT(); let recipient2 = OTHER(); @@ -339,7 +336,7 @@ fn test_multicall() { #[test] fn test_multicall_zero_calls() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let (account, _) = setup_dispatcher(Option::Some(@SIGNED_TX_DATA(KEY_PAIR()))); let mut calls = array![]; let ret = account.__execute__(calls); @@ -354,9 +351,7 @@ fn test_account_called_from_contract() { let state = setup(); let calls = array![]; - testing::set_contract_address(ACCOUNT_ADDRESS()); - testing::set_caller_address(CALLER()); - + start_cheat_caller_address(test_address(), CALLER()); state.__execute__(calls); } @@ -376,9 +371,7 @@ fn test_cannot_get_without_initialize() { fn test_cannot_set_without_initialize() { let mut state = COMPONENT_STATE(); - testing::set_contract_address(ACCOUNT_ADDRESS()); - testing::set_caller_address(ACCOUNT_ADDRESS()); - + start_cheat_caller_address(test_address(), test_address()); state.set_public_key(NEW_ETH_PUBKEY(), array![].span()); } @@ -386,36 +379,40 @@ fn test_cannot_set_without_initialize() { fn test_public_key_setter_and_getter() { let mut state = COMPONENT_STATE(); let public_key = ETH_PUBKEY(); - let new_public_key = NEW_ETH_PUBKEY(); - - testing::set_contract_address(ACCOUNT_ADDRESS()); - testing::set_caller_address(ACCOUNT_ADDRESS()); + let key_pair = KEY_PAIR(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, contract_address); state.initializer(public_key); - utils::drop_event(ACCOUNT_ADDRESS()); // Check default let current = state.get_public_key(); assert_eq!(current, public_key); + let mut spy = spy_events(); + // Set key - state.set_public_key(new_public_key, get_accept_ownership_signature()); + let signature = get_accept_ownership_signature(contract_address, ETH_PUBKEY(), key_pair); + state.set_public_key(key_pair.public_key, signature); - assert_event_owner_removed(ACCOUNT_ADDRESS(), current); - assert_only_event_owner_added(ACCOUNT_ADDRESS(), new_public_key); + spy.assert_event_owner_removed(contract_address, current); + spy.assert_only_event_owner_added(contract_address, key_pair.public_key); let public_key = state.get_public_key(); - assert_eq!(public_key, new_public_key); + assert_eq!(public_key, key_pair.public_key); } #[test] #[should_panic(expected: ('EthAccount: unauthorized',))] fn test_public_key_setter_different_account() { let mut state = COMPONENT_STATE(); - testing::set_contract_address(ACCOUNT_ADDRESS()); - testing::set_caller_address(CALLER()); + let key_pair = KEY_PAIR(); + let contract_address = test_address(); + + start_cheat_caller_address(contract_address, CALLER()); - state.set_public_key(NEW_ETH_PUBKEY(), get_accept_ownership_signature()); + let signature = get_accept_ownership_signature(contract_address, ETH_PUBKEY(), key_pair); + state.set_public_key(key_pair.public_key, signature); } // @@ -426,34 +423,37 @@ fn test_public_key_setter_different_account() { fn test_public_key_setter_and_getter_camel() { let mut state = COMPONENT_STATE(); let public_key = ETH_PUBKEY(); - let new_public_key = NEW_ETH_PUBKEY(); - - testing::set_contract_address(ACCOUNT_ADDRESS()); - testing::set_caller_address(ACCOUNT_ADDRESS()); + let key_pair = KEY_PAIR(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, contract_address); state.initializer(public_key); - utils::drop_event(ACCOUNT_ADDRESS()); let current = state.getPublicKey(); assert_eq!(current, public_key); - state.setPublicKey(new_public_key, get_accept_ownership_signature()); + let mut spy = spy_events(); + + let signature = get_accept_ownership_signature(contract_address, ETH_PUBKEY(), key_pair); + state.setPublicKey(key_pair.public_key, signature); - assert_event_owner_removed(ACCOUNT_ADDRESS(), public_key); - assert_only_event_owner_added(ACCOUNT_ADDRESS(), new_public_key); + spy.assert_event_owner_removed(contract_address, public_key); + spy.assert_only_event_owner_added(contract_address, key_pair.public_key); let public_key = state.getPublicKey(); - assert_eq!(public_key, new_public_key); + assert_eq!(public_key, key_pair.public_key); } #[test] #[should_panic(expected: ('EthAccount: unauthorized',))] fn test_public_key_setter_different_account_camel() { let mut state = COMPONENT_STATE(); - testing::set_contract_address(ACCOUNT_ADDRESS()); - testing::set_caller_address(CALLER()); + let key_pair = KEY_PAIR(); - state.setPublicKey(NEW_ETH_PUBKEY(), get_accept_ownership_signature()); + start_cheat_caller_address(test_address(), CALLER()); + + let signature = get_accept_ownership_signature(test_address(), ETH_PUBKEY(), key_pair); + state.setPublicKey(key_pair.public_key, signature); } // @@ -465,10 +465,11 @@ fn test_initializer() { let mut state = COMPONENT_STATE(); let mock_state = CONTRACT_STATE(); let public_key = ETH_PUBKEY(); + let mut spy = spy_events(); state.initializer(public_key); - assert_only_event_owner_added(ZERO(), public_key); + spy.assert_only_event_owner_added(test_address(), public_key); assert_eq!(state.get_public_key(), public_key); @@ -483,8 +484,7 @@ fn test_initializer() { fn test_assert_only_self_true() { let mut state = COMPONENT_STATE(); - testing::set_contract_address(ACCOUNT_ADDRESS()); - testing::set_caller_address(ACCOUNT_ADDRESS()); + start_cheat_caller_address(test_address(), test_address()); state.assert_only_self(); } @@ -493,17 +493,19 @@ fn test_assert_only_self_true() { fn test_assert_only_self_false() { let mut state = COMPONENT_STATE(); - testing::set_contract_address(ACCOUNT_ADDRESS()); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.assert_only_self(); } #[test] fn test_assert_valid_new_owner() { let mut state = setup(); + let contract_address = test_address(); + + let key_pair = KEY_PAIR(); + let signature = get_accept_ownership_signature(contract_address, ETH_PUBKEY(), key_pair); - testing::set_contract_address(ACCOUNT_ADDRESS()); - state.assert_valid_new_owner(ETH_PUBKEY(), NEW_ETH_PUBKEY(), get_accept_ownership_signature()); + state.assert_valid_new_owner(ETH_PUBKEY(), key_pair.public_key, signature); } #[test] @@ -511,7 +513,7 @@ fn test_assert_valid_new_owner() { fn test_assert_valid_new_owner_invalid_signature() { let mut state = setup(); - testing::set_contract_address(ACCOUNT_ADDRESS()); + start_cheat_caller_address(test_address(), test_address()); let mut bad_signature = array![]; EthSignature { r: 0xe2c02fbaa03809019ce6501cb5e57fc4a1e96e09dd8becfde8508ceddb53330b, @@ -524,8 +526,8 @@ fn test_assert_valid_new_owner_invalid_signature() { #[test] fn test__is_valid_signature() { let mut state = COMPONENT_STATE(); - let data = SIGNED_TX_DATA(); - let hash = data.transaction_hash; + let data = SIGNED_TX_DATA(KEY_PAIR()); + let hash = data.tx_hash; let mut bad_signature = data.signature; @@ -550,40 +552,12 @@ fn test__is_valid_signature() { fn test__set_public_key() { let mut state = COMPONENT_STATE(); let public_key = ETH_PUBKEY(); + let mut spy = spy_events(); + state._set_public_key(public_key); - assert_only_event_owner_added(ZERO(), public_key); + spy.assert_only_event_owner_added(test_address(), public_key); let public_key = state.get_public_key(); assert_eq!(public_key, ETH_PUBKEY()); } - -// -// Helpers -// - -fn get_accept_ownership_signature() -> Span { - let mut output = array![]; - - // 0x5b23679494e4634c66808d93eeef8301f5fd806b095e5e98b45ee97432a0d8d = - // PoseidonTrait::new() - // .update_with('StarkNet Message') - // .update_with('accept_ownership') - // .update_with(ACCOUNT_ADDRESS()) - // .update_with(ETH_PUBKEY().get_coordinates().unwrap_syscall()) - // .finalize(); - - // This signature was computed using ethers js sdk from the following values: - // - private_key: 0x45397ee6ca34cb49060f1c303c6cb7ee2d6123e617601ef3e31ccf7bf5bef1f9 - // - public_key: - // r: 0x829307f82a1883c2414503ba85fc85037f22c6fc6f80910801f6b01a4131da1e - // s: 0x2a23f7bddf3715d11767b1247eccc68c89e11b926e2615268db6ad1af8d8da96 - // - msg_hash: 0x5b23679494e4634c66808d93eeef8301f5fd806b095e5e98b45ee97432a0d8d - EthSignature { - r: 0x161de897c0232716792d7b580a577212a6573dbb60c0d0449fa673b95b22d942, - s: 0x7c7b279857889e20fb4c002fd2d1c112c9f30fa4c411f7cb32f55ab0af991a73, - } - .serialize(ref output); - - output.span() -} diff --git a/src/tests/account/starknet.cairo b/src/tests/account/starknet.cairo index e67636b10..f426692e3 100644 --- a/src/tests/account/starknet.cairo +++ b/src/tests/account/starknet.cairo @@ -1,4 +1,5 @@ pub(crate) mod common; +// mod test_account; +// mod test_dual_account; + -mod test_account; -mod test_dual_account; diff --git a/src/tests/account/starknet/common.cairo b/src/tests/account/starknet/common.cairo index 91905d856..f029dff0a 100644 --- a/src/tests/account/starknet/common.cairo +++ b/src/tests/account/starknet/common.cairo @@ -1,17 +1,18 @@ use openzeppelin::account::AccountComponent::{OwnerAdded, OwnerRemoved}; use openzeppelin::account::AccountComponent; -use openzeppelin::tests::mocks::erc20_mocks::DualCaseERC20Mock; use openzeppelin::tests::utils::constants::{NAME, SYMBOL, NEW_PUBKEY}; +use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::tests::utils; -use openzeppelin::token::erc20::interface::{IERC20DispatcherTrait, IERC20Dispatcher}; +use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::EventSpy; use starknet::ContractAddress; #[derive(Drop)] pub(crate) struct SignedTransactionData { pub(crate) private_key: felt252, pub(crate) public_key: felt252, - pub(crate) transaction_hash: felt252, + pub(crate) tx_hash: felt252, pub(crate) r: felt252, pub(crate) s: felt252 } @@ -20,7 +21,7 @@ pub(crate) fn SIGNED_TX_DATA() -> SignedTransactionData { SignedTransactionData { private_key: 1234, public_key: NEW_PUBKEY, - transaction_hash: 0x601d3d2e265c10ff645e1554c435e72ce6721f0ba5fc96f0c650bfc6231191a, + tx_hash: 0x601d3d2e265c10ff645e1554c435e72ce6721f0ba5fc96f0c650bfc6231191a, r: 0x6bc22689efcaeacb9459577138aff9f0af5b77ee7894cdc8efabaf760f6cf6e, s: 0x295989881583b9325436851934334faa9d639a2094cd1e2f8691c8a71cd4cdf } @@ -34,36 +35,31 @@ pub(crate) fn deploy_erc20(recipient: ContractAddress, initial_supply: u256) -> calldata.append_serde(initial_supply); calldata.append_serde(recipient); - let address = utils::deploy(DualCaseERC20Mock::TEST_CLASS_HASH, calldata); + let address = utils::declare_and_deploy("DualCaseERC20Mock", calldata); IERC20Dispatcher { contract_address: address } } -pub(crate) fn assert_event_owner_removed(contract: ContractAddress, removed_owner_guid: felt252) { - let event = utils::pop_log::(contract).unwrap(); - let expected = AccountComponent::Event::OwnerRemoved(OwnerRemoved { removed_owner_guid }); - assert!(event == expected); - - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("OwnerRemoved")); - indexed_keys.append_serde(removed_owner_guid); - utils::assert_indexed_keys(event, indexed_keys.span()); -} - -pub(crate) fn assert_event_owner_added(contract: ContractAddress, new_owner_guid: felt252) { - let event = utils::pop_log::(contract).unwrap(); - let expected = AccountComponent::Event::OwnerAdded(OwnerAdded { new_owner_guid }); - assert!(event == expected); +#[generate_trait] +pub(crate) impl AccountSpyHelpersImpl of AccountSpyHelpers { + fn assert_event_owner_removed( + ref self: EventSpy, contract: ContractAddress, removed_owner_guid: felt252 + ) { + let expected = AccountComponent::Event::OwnerRemoved(OwnerRemoved { removed_owner_guid }); + self.assert_emitted_single(contract, expected); + } - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("OwnerAdded")); - indexed_keys.append_serde(new_owner_guid); - utils::assert_indexed_keys(event, indexed_keys.span()); -} + fn assert_event_owner_added( + ref self: EventSpy, contract: ContractAddress, new_owner_guid: felt252 + ) { + let expected = AccountComponent::Event::OwnerAdded(OwnerAdded { new_owner_guid }); + self.assert_emitted_single(contract, expected); + } -pub(crate) fn assert_only_event_owner_added(contract: ContractAddress, new_owner_guid: felt252) { - assert_event_owner_added(contract, new_owner_guid); - utils::assert_no_events_left(contract); + fn assert_only_event_owner_added( + ref self: EventSpy, contract: ContractAddress, new_owner_guid: felt252 + ) { + self.assert_event_owner_added(contract, new_owner_guid); + self.assert_no_events_left_from(contract); + } } diff --git a/src/tests/account/test_secp256k1.cairo b/src/tests/account/test_secp256k1.cairo index 5466eee55..b41160a3d 100644 --- a/src/tests/account/test_secp256k1.cairo +++ b/src/tests/account/test_secp256k1.cairo @@ -6,8 +6,6 @@ use starknet::SyscallResultTrait; use starknet::secp256_trait::{Secp256Trait, Secp256PointTrait}; use starknet::secp256k1::Secp256k1Point; -use super::ethereum::common::get_points; - #[test] fn test_pack_big_secp256k1_points() { let (big_point_1, big_point_2) = get_points(); @@ -121,3 +119,19 @@ fn test_partial_eq() { assert!(big_point_1 != big_point_2); assert!(big_point_2 != big_point_1); } + +// +// Helpers +// + +fn get_points() -> (Secp256k1Point, Secp256k1Point) { + let curve_size = Secp256Trait::::get_curve_size(); + let point_1 = Secp256Trait::secp256_ec_get_point_from_x_syscall(curve_size, true) + .unwrap_syscall() + .unwrap(); + let point_2 = Secp256Trait::secp256_ec_get_point_from_x_syscall(curve_size, false) + .unwrap_syscall() + .unwrap(); + + (point_1, point_2) +} diff --git a/src/tests/account/test_signature.cairo b/src/tests/account/test_signature.cairo index ffdb2ce74..7da6fe741 100644 --- a/src/tests/account/test_signature.cairo +++ b/src/tests/account/test_signature.cairo @@ -1,4 +1,5 @@ use openzeppelin::account::utils::signature::{is_valid_stark_signature, is_valid_eth_signature}; +use openzeppelin::tests::utils::constants::secp256k1::KEY_PAIR; use starknet::secp256_trait::Secp256Trait; use starknet::secp256k1::Secp256k1Point; @@ -12,7 +13,7 @@ use super::starknet::common::SIGNED_TX_DATA as stark_signature_data; #[test] fn test_is_valid_stark_signature_good_sig() { let data = stark_signature_data(); - let hash = data.transaction_hash; + let hash = data.tx_hash; let mut good_signature = array![data.r, data.s].span(); @@ -23,7 +24,7 @@ fn test_is_valid_stark_signature_good_sig() { #[test] fn test_is_valid_stark_signature_bad_sig() { let data = stark_signature_data(); - let hash = data.transaction_hash; + let hash = data.tx_hash; let mut bad_signature = array![0x987, 0x564].span(); @@ -34,7 +35,7 @@ fn test_is_valid_stark_signature_bad_sig() { #[test] fn test_is_valid_stark_signature_invalid_len_sig() { let data = stark_signature_data(); - let hash = data.transaction_hash; + let hash = data.tx_hash; let mut bad_signature = array![0x987].span(); @@ -48,8 +49,8 @@ fn test_is_valid_stark_signature_invalid_len_sig() { #[test] fn test_is_valid_eth_signature_good_sig() { - let data = eth_signature_data(); - let hash = data.transaction_hash; + let data = eth_signature_data(KEY_PAIR()); + let hash = data.tx_hash; let mut serialized_good_signature = array![]; @@ -61,8 +62,8 @@ fn test_is_valid_eth_signature_good_sig() { #[test] fn test_is_valid_eth_signature_bad_sig() { - let data = eth_signature_data(); - let hash = data.transaction_hash; + let data = eth_signature_data(KEY_PAIR()); + let hash = data.tx_hash; let mut bad_signature = data.signature; bad_signature.r += 1; @@ -80,8 +81,8 @@ fn test_is_valid_eth_signature_bad_sig() { #[test] #[should_panic(expected: ('Signature: Invalid format.',))] fn test_is_valid_eth_signature_invalid_format_sig() { - let data = eth_signature_data(); - let hash = data.transaction_hash; + let data = eth_signature_data(KEY_PAIR()); + let hash = data.tx_hash; let mut serialized_bad_signature = array![0x1]; @@ -90,8 +91,8 @@ fn test_is_valid_eth_signature_invalid_format_sig() { #[test] fn test_signature_r_out_of_range() { - let data = eth_signature_data(); - let hash = data.transaction_hash; + let data = eth_signature_data(KEY_PAIR()); + let hash = data.tx_hash; let mut bad_signature = data.signature; let curve_size = Secp256Trait::::get_curve_size(); @@ -110,8 +111,8 @@ fn test_signature_r_out_of_range() { #[test] fn test_signature_s_out_of_range() { - let data = eth_signature_data(); - let hash = data.transaction_hash; + let data = eth_signature_data(KEY_PAIR()); + let hash = data.tx_hash; let mut bad_signature = data.signature; let curve_size = Secp256Trait::::get_curve_size(); diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index 509c33ee1..597fa9659 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -1,8 +1,8 @@ -pub(crate) mod common; +pub mod common; pub(crate) mod constants; -pub(crate) mod deployment; -pub(crate) mod events; -pub(crate) mod signing; +pub mod deployment; +pub mod events; +pub mod signing; pub use common::{ panic_data_to_byte_array, to_base_16_string, IntoBase16StringTrait, diff --git a/src/tests/utils/constants.cairo b/src/tests/utils/constants.cairo index c84190c20..2af608bae 100644 --- a/src/tests/utils/constants.cairo +++ b/src/tests/utils/constants.cairo @@ -25,6 +25,7 @@ pub(crate) const SALT: felt252 = 'SALT'; pub(crate) const SUCCESS: felt252 = 123123; pub(crate) const FAILURE: felt252 = 456456; pub(crate) const MIN_TRANSACTION_VERSION: felt252 = 1; +pub(crate) const TRANSACTION_HASH: felt252 = 'TRANSACTION_HASH'; // 2**128 pub(crate) const QUERY_OFFSET: felt252 = 0x100000000000000000000000000000000; // QUERY_OFFSET + MIN_TRANSACTION_VERSION @@ -115,3 +116,22 @@ pub(crate) fn DATA(success: bool) -> Span { pub(crate) fn EMPTY_DATA() -> Span { array![].span() } + +// +// Signing keys +// + +pub(crate) mod secp256k1 { + use openzeppelin::tests::utils::signing::{Secp256k1KeyPair, get_secp256k1_keys_from}; + + pub(crate) const PRIVATE_KEY: u256 = u256 { low: 'PRIVATE_LOW', high: 'PRIVATE_HIGH' }; + pub(crate) fn KEY_PAIR() -> Secp256k1KeyPair { + get_secp256k1_keys_from(PRIVATE_KEY) + } + + pub(crate) const PRIVATE_KEY_2: u256 = u256 { low: 'PRIVATE_LOW_2', high: 'PRIVATE_HIGH_2' }; + pub(crate) fn KEY_PAIR_2() -> Secp256k1KeyPair { + get_secp256k1_keys_from(PRIVATE_KEY_2) + } +} + diff --git a/src/tests/utils/signing.cairo b/src/tests/utils/signing.cairo index 90be99ed5..ee596c8d0 100644 --- a/src/tests/utils/signing.cairo +++ b/src/tests/utils/signing.cairo @@ -1,11 +1,23 @@ +use snforge_std::signature::secp256k1_curve::{Secp256k1CurveSignerImpl, Secp256k1CurveKeyPairImpl}; use snforge_std::signature::stark_curve::{StarkCurveSignerImpl, StarkCurveKeyPairImpl}; use snforge_std::signature::{KeyPair, KeyPairTrait}; +use starknet::secp256k1::Secp256k1Point; pub type StarkKeyPair = KeyPair; +pub type Secp256k1KeyPair = KeyPair; -pub fn KEY_PAIR() -> StarkKeyPair { - KeyPairTrait::from_secret_key('SECRET_KEY') +pub fn get_stark_keys_from(private_key: felt252) -> StarkKeyPair { + StarkCurveKeyPairImpl::from_secret_key(private_key) } -pub fn KEY_PAIR_2() -> StarkKeyPair { - KeyPairTrait::from_secret_key('SECRET_KEY_2') + +pub fn get_secp256k1_keys_from(private_key: u256) -> Secp256k1KeyPair { + Secp256k1CurveKeyPairImpl::from_secret_key(private_key) +} + +#[generate_trait] +pub impl Secp256k1KeyPairExt of Secp256k1KeyPairExtTrait { + fn serialized_sign(self: Secp256k1KeyPair, msg: u256) -> Array { + let (r, s) = self.sign(msg).unwrap(); + array![r.low.into(), r.high.into(), s.low.into(), s.high.into()] + } } From 9e091d4dcf72c687848e9454bfbc765079f259fb Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 23 Jul 2024 23:36:27 +0200 Subject: [PATCH 15/20] Migrate eth account preset tests (#1060) * feat: update dual_eth_account tests * feat: update eth_account tests * refactor: remove unused helpers * feat: update eth account preset tests * Update src/tests/account/ethereum/test_eth_account.cairo Co-authored-by: immrsd <103599616+immrsd@users.noreply.github.com> * Update src/tests/presets/test_eth_account.cairo Co-authored-by: immrsd <103599616+immrsd@users.noreply.github.com> * feat: apply review updates --------- Co-authored-by: immrsd <103599616+immrsd@users.noreply.github.com> --- .../account/ethereum/test_eth_account.cairo | 10 +- src/tests/presets.cairo | 2 +- src/tests/presets/test_eth_account.cairo | 291 +++++++++--------- src/tests/utils/constants.cairo | 1 - 4 files changed, 160 insertions(+), 144 deletions(-) diff --git a/src/tests/account/ethereum/test_eth_account.cairo b/src/tests/account/ethereum/test_eth_account.cairo index bb21bb6b9..a239de7f6 100644 --- a/src/tests/account/ethereum/test_eth_account.cairo +++ b/src/tests/account/ethereum/test_eth_account.cairo @@ -1,3 +1,4 @@ +use core::starknet::SyscallResultTrait; use openzeppelin::account::EthAccountComponent::{InternalTrait, SRC6CamelOnlyImpl}; use openzeppelin::account::EthAccountComponent::{PublicKeyCamelImpl, PublicKeyImpl}; use openzeppelin::account::EthAccountComponent; @@ -236,8 +237,8 @@ fn test_execute_with_version(version: Option) { calls.append(call); // Handle version for test - if version.is_some() { - cheat_transaction_version_global(version.unwrap()); + if let Option::Some(version) = version { + cheat_transaction_version_global(version); } // Execute @@ -449,10 +450,11 @@ fn test_public_key_setter_and_getter_camel() { fn test_public_key_setter_different_account_camel() { let mut state = COMPONENT_STATE(); let key_pair = KEY_PAIR(); + let contract_address = test_address(); - start_cheat_caller_address(test_address(), CALLER()); + start_cheat_caller_address(contract_address, CALLER()); - let signature = get_accept_ownership_signature(test_address(), ETH_PUBKEY(), key_pair); + let signature = get_accept_ownership_signature(contract_address, ETH_PUBKEY(), key_pair); state.setPublicKey(key_pair.public_key, signature); } diff --git a/src/tests/presets.cairo b/src/tests/presets.cairo index cc599a931..5edfe8b18 100644 --- a/src/tests/presets.cairo +++ b/src/tests/presets.cairo @@ -2,5 +2,5 @@ mod test_erc1155; mod test_erc20; mod test_erc721; -// mod test_eth_account; +mod test_eth_account; mod test_universal_deployer; diff --git a/src/tests/presets/test_eth_account.cairo b/src/tests/presets/test_eth_account.cairo index b8510958a..81ea869ea 100644 --- a/src/tests/presets/test_eth_account.cairo +++ b/src/tests/presets/test_eth_account.cairo @@ -3,38 +3,42 @@ use openzeppelin::account::interface::ISRC6_ID; use openzeppelin::account::utils::secp256k1::{ DebugSecp256k1Point, Secp256k1PointSerde, Secp256k1PointPartialEq }; -use openzeppelin::account::utils::signature::EthSignature; use openzeppelin::introspection::interface::ISRC5_ID; use openzeppelin::presets::EthAccountUpgradeable; +use openzeppelin::presets::interfaces::eth_account::{ + EthAccountUpgradeableABISafeDispatcher, EthAccountUpgradeableABISafeDispatcherTrait +}; use openzeppelin::presets::interfaces::{ EthAccountUpgradeableABIDispatcher, EthAccountUpgradeableABIDispatcherTrait }; +use openzeppelin::tests::account::ethereum::common::EthAccountSpyHelpers; use openzeppelin::tests::account::ethereum::common::{ - assert_only_event_owner_added, assert_event_owner_removed -}; -use openzeppelin::tests::account::ethereum::common::{ - deploy_erc20, get_points, NEW_ETH_PUBKEY, SIGNED_TX_DATA, SignedTransactionData + deploy_erc20, SIGNED_TX_DATA, SignedTransactionData, get_accept_ownership_signature }; -use openzeppelin::tests::mocks::eth_account_mocks::SnakeEthAccountMock; -use openzeppelin::tests::upgrades::common::assert_only_event_upgraded; +use openzeppelin::tests::upgrades::common::UpgradeableSpyHelpers; +use openzeppelin::tests::utils::constants::secp256k1::KEY_PAIR; use openzeppelin::tests::utils::constants::{ - CLASS_HASH_ZERO, ETH_PUBKEY, SALT, ZERO, RECIPIENT, QUERY_VERSION, MIN_TRANSACTION_VERSION + CLASS_HASH_ZERO, ETH_PUBKEY, NEW_ETH_PUBKEY, SALT, ZERO, RECIPIENT, QUERY_VERSION, + MIN_TRANSACTION_VERSION }; use openzeppelin::tests::utils; use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::{ + cheat_signature_global, cheat_transaction_version_global, cheat_transaction_hash_global, + start_cheat_caller_address +}; +use snforge_std::{spy_events, test_address}; +use starknet::ClassHash; +use starknet::SyscallResultTrait; use starknet::account::Call; use starknet::contract_address_const; -use starknet::testing; -use starknet::{ContractAddress, ClassHash}; - -fn CLASS_HASH() -> felt252 { - EthAccountUpgradeable::TEST_CLASS_HASH -} +use starknet::secp256_trait::Secp256Trait; +use starknet::secp256k1::Secp256k1Point; -fn V2_CLASS_HASH() -> ClassHash { - SnakeEthAccountMock::TEST_CLASS_HASH.try_into().unwrap() +fn declare_v2_class_hash() -> ClassHash { + utils::declare_class("SnakeEthAccountMock").class_hash } // @@ -45,32 +49,33 @@ fn setup_dispatcher() -> EthAccountUpgradeableABIDispatcher { let mut calldata = array![]; calldata.append_serde(ETH_PUBKEY()); - let target = utils::deploy(CLASS_HASH(), calldata); - utils::drop_event(target); - + let target = utils::declare_and_deploy("EthAccountUpgradeable", calldata); EthAccountUpgradeableABIDispatcher { contract_address: target } } fn setup_dispatcher_with_data( data: Option<@SignedTransactionData> -) -> EthAccountUpgradeableABIDispatcher { - testing::set_version(MIN_TRANSACTION_VERSION); - +) -> (EthAccountUpgradeableABIDispatcher, felt252) { let mut calldata = array![]; - if data.is_some() { - let data = data.unwrap(); + if let Option::Some(data) = data { let mut serialized_signature = array![]; data.signature.serialize(ref serialized_signature); - testing::set_signature(serialized_signature.span()); - testing::set_transaction_hash(*data.transaction_hash); + cheat_signature_global(serialized_signature.span()); + cheat_transaction_hash_global(*data.tx_hash); calldata.append_serde(*data.public_key); } else { calldata.append_serde(ETH_PUBKEY()); } - let address = utils::deploy(CLASS_HASH(), calldata); - EthAccountUpgradeableABIDispatcher { contract_address: address } + let contract_class = utils::declare_class("EthAccountUpgradeable"); + let address = utils::deploy(contract_class, calldata); + let dispatcher = EthAccountUpgradeableABIDispatcher { contract_address: address }; + + cheat_transaction_version_global(MIN_TRANSACTION_VERSION); + start_cheat_caller_address(address, ZERO()); + + (dispatcher, contract_class.class_hash.into()) } // @@ -80,19 +85,18 @@ fn setup_dispatcher_with_data( #[test] fn test_constructor() { let mut state = EthAccountUpgradeable::contract_state_for_testing(); + let mut spy = spy_events(); EthAccountUpgradeable::constructor(ref state, ETH_PUBKEY()); - assert_only_event_owner_added(ZERO(), ETH_PUBKEY()); + spy.assert_only_event_owner_added(test_address(), ETH_PUBKEY()); let public_key = EthAccountUpgradeable::EthAccountMixinImpl::get_public_key(@state); assert_eq!(public_key, ETH_PUBKEY()); - let supports_isrc5 = EthAccountUpgradeable::EthAccountMixinImpl::supports_interface( @state, ISRC5_ID ); assert!(supports_isrc5); - let supports_isrc6 = EthAccountUpgradeable::EthAccountMixinImpl::supports_interface( @state, ISRC6_ID ); @@ -104,42 +108,48 @@ fn test_constructor() { // #[test] -fn test_public_key_setter_and_getter_2() { +fn test_public_key_setter_and_getter() { let dispatcher = setup_dispatcher(); - let new_public_key = NEW_ETH_PUBKEY(); + let key_pair = KEY_PAIR(); + let contract_address = dispatcher.contract_address; + let mut spy = spy_events(); - testing::set_contract_address(dispatcher.contract_address); + start_cheat_caller_address(contract_address, contract_address); - dispatcher.set_public_key(new_public_key, get_accept_ownership_signature()); - assert_eq!(dispatcher.get_public_key(), new_public_key); + let signature = get_accept_ownership_signature(contract_address, ETH_PUBKEY(), key_pair); + dispatcher.set_public_key(key_pair.public_key, signature); + assert_eq!(dispatcher.get_public_key(), key_pair.public_key); - assert_event_owner_removed(dispatcher.contract_address, ETH_PUBKEY()); - assert_only_event_owner_added(dispatcher.contract_address, new_public_key); + spy.assert_event_owner_removed(contract_address, ETH_PUBKEY()); + spy.assert_only_event_owner_added(contract_address, key_pair.public_key); } #[test] fn test_public_key_setter_and_getter_camel() { let dispatcher = setup_dispatcher(); - let new_public_key = NEW_ETH_PUBKEY(); + let key_pair = KEY_PAIR(); + let contract_address = dispatcher.contract_address; + let mut spy = spy_events(); - testing::set_contract_address(dispatcher.contract_address); + start_cheat_caller_address(contract_address, contract_address); - dispatcher.setPublicKey(new_public_key, get_accept_ownership_signature()); - assert_eq!(dispatcher.getPublicKey(), new_public_key); + let signature = get_accept_ownership_signature(contract_address, ETH_PUBKEY(), key_pair); + dispatcher.setPublicKey(key_pair.public_key, signature); + assert_eq!(dispatcher.getPublicKey(), key_pair.public_key); - assert_event_owner_removed(dispatcher.contract_address, ETH_PUBKEY()); - assert_only_event_owner_added(dispatcher.contract_address, new_public_key); + spy.assert_event_owner_removed(contract_address, ETH_PUBKEY()); + spy.assert_only_event_owner_added(contract_address, key_pair.public_key); } #[test] -#[should_panic(expected: ('EthAccount: unauthorized', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('EthAccount: unauthorized',))] fn test_set_public_key_different_account() { let dispatcher = setup_dispatcher(); dispatcher.set_public_key(NEW_ETH_PUBKEY(), array![].span()); } #[test] -#[should_panic(expected: ('EthAccount: unauthorized', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('EthAccount: unauthorized',))] fn test_setPublicKey_different_account() { let dispatcher = setup_dispatcher(); dispatcher.setPublicKey(NEW_ETH_PUBKEY(), array![].span()); @@ -151,14 +161,18 @@ fn test_setPublicKey_different_account() { fn is_valid_sig_dispatcher() -> (EthAccountUpgradeableABIDispatcher, felt252, Array) { let dispatcher = setup_dispatcher(); + let key_pair = KEY_PAIR(); + let contract_address = dispatcher.contract_address; + let data = SIGNED_TX_DATA(key_pair); + let hash = data.tx_hash; - let data = SIGNED_TX_DATA(); - let hash = data.transaction_hash; let mut serialized_signature = array![]; data.signature.serialize(ref serialized_signature); - testing::set_contract_address(dispatcher.contract_address); - dispatcher.set_public_key(data.public_key, get_accept_ownership_signature()); + start_cheat_caller_address(contract_address, contract_address); + + let signature = get_accept_ownership_signature(contract_address, ETH_PUBKEY(), key_pair); + dispatcher.set_public_key(data.public_key, signature); (dispatcher, hash, serialized_signature) } @@ -217,92 +231,104 @@ fn test_supports_interface() { #[test] fn test_validate_deploy() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); + let (account, class_hash) = setup_dispatcher_with_data( + Option::Some(@SIGNED_TX_DATA(KEY_PAIR())) + ); // `__validate_deploy__` does not directly use the passed arguments. Their // values are already integrated in the tx hash. The passed arguments in this // testing context are decoupled from the signature and have no effect on the test. - let is_valid = account.__validate_deploy__(CLASS_HASH(), SALT, ETH_PUBKEY()); + let is_valid = account.__validate_deploy__(class_hash, SALT, ETH_PUBKEY()); assert_eq!(is_valid, starknet::VALIDATED); } #[test] -#[should_panic(expected: ('EthAccount: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('EthAccount: invalid signature',))] fn test_validate_deploy_invalid_signature_data() { - let mut data = SIGNED_TX_DATA(); - data.transaction_hash += 1; - let account = setup_dispatcher_with_data(Option::Some(@data)); + let mut data = SIGNED_TX_DATA(KEY_PAIR()); + data.tx_hash += 1; + let (account, class_hash) = setup_dispatcher_with_data(Option::Some(@data)); - account.__validate_deploy__(CLASS_HASH(), SALT, ETH_PUBKEY()); + account.__validate_deploy__(class_hash, SALT, ETH_PUBKEY()); } #[test] -#[should_panic(expected: ('Signature: Invalid format.', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Signature: Invalid format.',))] fn test_validate_deploy_invalid_signature_length() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); + let (account, class_hash) = setup_dispatcher_with_data( + Option::Some(@SIGNED_TX_DATA(KEY_PAIR())) + ); let mut signature = array![0x1]; - testing::set_signature(signature.span()); + cheat_signature_global(signature.span()); - account.__validate_deploy__(CLASS_HASH(), SALT, ETH_PUBKEY()); + account.__validate_deploy__(class_hash, SALT, ETH_PUBKEY()); } #[test] -#[should_panic(expected: ('Signature: Invalid format.', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Signature: Invalid format.',))] fn test_validate_deploy_empty_signature() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); + let (account, class_hash) = setup_dispatcher_with_data( + Option::Some(@SIGNED_TX_DATA(KEY_PAIR())) + ); let empty_sig = array![]; - testing::set_signature(empty_sig.span()); - account.__validate_deploy__(CLASS_HASH(), SALT, ETH_PUBKEY()); + cheat_signature_global(empty_sig.span()); + account.__validate_deploy__(class_hash, SALT, ETH_PUBKEY()); } #[test] fn test_validate_declare() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); + let (account, class_hash) = setup_dispatcher_with_data( + Option::Some(@SIGNED_TX_DATA(KEY_PAIR())) + ); // `__validate_declare__` does not directly use the class_hash argument. Its // value is already integrated in the tx hash. The class_hash argument in this // testing context is decoupled from the signature and has no effect on the test. - let is_valid = account.__validate_declare__(CLASS_HASH()); + let is_valid = account.__validate_declare__(class_hash); assert_eq!(is_valid, starknet::VALIDATED,); } #[test] -#[should_panic(expected: ('EthAccount: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('EthAccount: invalid signature',))] fn test_validate_declare_invalid_signature_data() { - let mut data = SIGNED_TX_DATA(); - data.transaction_hash += 1; - let account = setup_dispatcher_with_data(Option::Some(@data)); + let mut data = SIGNED_TX_DATA(KEY_PAIR()); + data.tx_hash += 1; + let (account, class_hash) = setup_dispatcher_with_data(Option::Some(@data)); - account.__validate_declare__(CLASS_HASH()); + account.__validate_declare__(class_hash); } #[test] -#[should_panic(expected: ('Signature: Invalid format.', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Signature: Invalid format.',))] fn test_validate_declare_invalid_signature_length() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); + let (account, class_hash) = setup_dispatcher_with_data( + Option::Some(@SIGNED_TX_DATA(KEY_PAIR())) + ); let mut signature = array![0x1]; - testing::set_signature(signature.span()); + cheat_signature_global(signature.span()); - account.__validate_declare__(CLASS_HASH()); + account.__validate_declare__(class_hash); } #[test] -#[should_panic(expected: ('Signature: Invalid format.', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Signature: Invalid format.',))] fn test_validate_declare_empty_signature() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); + let (account, class_hash) = setup_dispatcher_with_data( + Option::Some(@SIGNED_TX_DATA(KEY_PAIR())) + ); let empty_sig = array![]; - testing::set_signature(empty_sig.span()); + cheat_signature_global(empty_sig.span()); - account.__validate_declare__(CLASS_HASH()); + account.__validate_declare__(class_hash); } fn test_execute_with_version(version: Option) { - let data = SIGNED_TX_DATA(); - let account = setup_dispatcher_with_data(Option::Some(@data)); + let data = SIGNED_TX_DATA(KEY_PAIR()); + let (account, _) = setup_dispatcher_with_data(Option::Some(@data)); let erc20 = deploy_erc20(account.contract_address, 1000); let amount: u256 = 200; @@ -317,8 +343,8 @@ fn test_execute_with_version(version: Option) { let mut calls = array![]; calls.append(call); - if version.is_some() { - testing::set_version(version.unwrap()); + if let Option::Some(version) = version { + cheat_transaction_version_global(version); } let ret = account.__execute__(calls); @@ -342,7 +368,7 @@ fn test_execute_query_version() { } #[test] -#[should_panic(expected: ('EthAccount: invalid tx version', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('EthAccount: invalid tx version',))] fn test_execute_invalid_version() { test_execute_with_version(Option::Some(MIN_TRANSACTION_VERSION - 1)); } @@ -350,26 +376,26 @@ fn test_execute_invalid_version() { #[test] fn test_validate() { let calls = array![]; - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); + let (account, _) = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA(KEY_PAIR()))); let is_valid = account.__validate__(calls); assert_eq!(is_valid, starknet::VALIDATED); } #[test] -#[should_panic(expected: ('EthAccount: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('EthAccount: invalid signature',))] fn test_validate_invalid() { let calls = array![]; - let mut data = SIGNED_TX_DATA(); - data.transaction_hash += 1; - let account = setup_dispatcher_with_data(Option::Some(@data)); + let mut data = SIGNED_TX_DATA(KEY_PAIR()); + data.tx_hash += 1; + let (account, _) = setup_dispatcher_with_data(Option::Some(@data)); account.__validate__(calls); } #[test] fn test_multicall() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); + let (account, _) = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA(KEY_PAIR()))); let erc20 = deploy_erc20(account.contract_address, 1000); let recipient1 = contract_address_const::<0x123>(); let recipient2 = contract_address_const::<0x456>(); @@ -409,7 +435,7 @@ fn test_multicall() { #[test] fn test_multicall_zero_calls() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); + let (account, _) = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA(KEY_PAIR()))); let mut calls = array![]; let ret = account.__execute__(calls); @@ -418,14 +444,13 @@ fn test_multicall_zero_calls() { } #[test] -#[should_panic(expected: ('EthAccount: invalid caller', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('EthAccount: invalid caller',))] fn test_account_called_from_contract() { let account = setup_dispatcher(); let calls = array![]; let caller = contract_address_const::<0x123>(); - testing::set_contract_address(account.contract_address); - testing::set_caller_address(caller); + start_cheat_caller_address(account.contract_address, caller); account.__execute__(calls); } @@ -435,57 +460,66 @@ fn test_account_called_from_contract() { // #[test] -#[should_panic(expected: ('EthAccount: unauthorized', 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ('EthAccount: unauthorized',))] fn test_upgrade_access_control() { let v1 = setup_dispatcher(); v1.upgrade(CLASS_HASH_ZERO()); } #[test] -#[should_panic(expected: ('Class hash cannot be zero', 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ('Class hash cannot be zero',))] fn test_upgrade_with_class_hash_zero() { let v1 = setup_dispatcher(); - set_contract_and_caller(v1.contract_address); + start_cheat_caller_address(v1.contract_address, v1.contract_address); v1.upgrade(CLASS_HASH_ZERO()); } #[test] fn test_upgraded_event() { let v1 = setup_dispatcher(); - let v2_class_hash = V2_CLASS_HASH(); + let v2_class_hash = declare_v2_class_hash(); + let mut spy = spy_events(); - set_contract_and_caller(v1.contract_address); + start_cheat_caller_address(v1.contract_address, v1.contract_address); v1.upgrade(v2_class_hash); - assert_only_event_upgraded(v1.contract_address, v2_class_hash); + spy.assert_only_event_upgraded(v1.contract_address, v2_class_hash); } #[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +#[feature("safe_dispatcher")] fn test_v2_missing_camel_selector() { let v1 = setup_dispatcher(); - let v2_class_hash = V2_CLASS_HASH(); + let v2_class_hash = declare_v2_class_hash(); - set_contract_and_caller(v1.contract_address); + start_cheat_caller_address(v1.contract_address, v1.contract_address); v1.upgrade(v2_class_hash); - let dispatcher = EthAccountUpgradeableABIDispatcher { contract_address: v1.contract_address }; - dispatcher.getPublicKey(); + let safe_dispatcher = EthAccountUpgradeableABISafeDispatcher { + contract_address: v1.contract_address + }; + let panic_data = safe_dispatcher.getPublicKey().unwrap_err(); + + utils::assert_entrypoint_not_found_error( + panic_data, selector!("getPublicKey"), v1.contract_address + ) } #[test] fn test_state_persists_after_upgrade() { let v1 = setup_dispatcher(); - let v2_class_hash = V2_CLASS_HASH(); + let key_pair = KEY_PAIR(); + let v2_class_hash = declare_v2_class_hash(); - set_contract_and_caller(v1.contract_address); + start_cheat_caller_address(v1.contract_address, v1.contract_address); let dispatcher = EthAccountUpgradeableABIDispatcher { contract_address: v1.contract_address }; - dispatcher.set_public_key(NEW_ETH_PUBKEY(), get_accept_ownership_signature()); + let signature = get_accept_ownership_signature(v1.contract_address, ETH_PUBKEY(), KEY_PAIR()); + dispatcher.set_public_key(key_pair.public_key, signature); let camel_public_key = dispatcher.getPublicKey(); - assert_eq!(camel_public_key, NEW_ETH_PUBKEY()); + assert_eq!(camel_public_key, key_pair.public_key); v1.upgrade(v2_class_hash); let snake_public_key = dispatcher.get_public_key(); @@ -497,33 +531,14 @@ fn test_state_persists_after_upgrade() { // Helpers // -fn set_contract_and_caller(address: ContractAddress) { - testing::set_contract_address(address); - testing::set_caller_address(address); -} - -fn get_accept_ownership_signature() -> Span { - let mut output = array![]; - - // 0x054308383e1c733aa36ccf3cc62e3107b6bcb10bafcab39912108c6b52655b4c = - // PoseidonTrait::new() - // .update_with('StarkNet Message') - // .update_with('accept_ownership') - // .update_with(dispatcher.contract_address) - // .update_with(ETH_PUBKEY().get_coordinates().unwrap_syscall()) - // .finalize(); - - // This signature was computed using ethers js sdk from the following values: - // - private_key: 0x45397ee6ca34cb49060f1c303c6cb7ee2d6123e617601ef3e31ccf7bf5bef1f9 - // - public_key: - // r: 0x829307f82a1883c2414503ba85fc85037f22c6fc6f80910801f6b01a4131da1e - // s: 0x2a23f7bddf3715d11767b1247eccc68c89e11b926e2615268db6ad1af8d8da96 - // - msg_hash: 0x054308383e1c733aa36ccf3cc62e3107b6bcb10bafcab39912108c6b52655b4c - EthSignature { - r: 0xc4de7637e4206e64ddae9261782dad6d3c99eaaede20ff3fb183f751b94ee9ff, - s: 0x77c24e1ad34f5ba0627048f6a11c1e7ee1ddd3b5d518ee4c71ebd5725390f860, - } - .serialize(ref output); +fn get_points() -> (Secp256k1Point, Secp256k1Point) { + let curve_size = Secp256Trait::::get_curve_size(); + let point_1 = Secp256Trait::secp256_ec_get_point_from_x_syscall(curve_size, true) + .unwrap_syscall() + .unwrap(); + let point_2 = Secp256Trait::secp256_ec_get_point_from_x_syscall(curve_size, false) + .unwrap_syscall() + .unwrap(); - output.span() + (point_1, point_2) } diff --git a/src/tests/utils/constants.cairo b/src/tests/utils/constants.cairo index 2af608bae..2259ef968 100644 --- a/src/tests/utils/constants.cairo +++ b/src/tests/utils/constants.cairo @@ -134,4 +134,3 @@ pub(crate) mod secp256k1 { get_secp256k1_keys_from(PRIVATE_KEY_2) } } - From a794ca0222b3b70e4cb0aba26be7a1a153b4d4c7 Mon Sep 17 00:00:00 2001 From: immrsd <103599616+immrsd@users.noreply.github.com> Date: Thu, 25 Jul 2024 17:55:23 +0200 Subject: [PATCH 16/20] Migrate AccessControl Tests (#1044) * Migrate AccessControl tests * Migrate AccessControl Dual Dispatcher tests * Fix imports * Address review comments * Update error messages * Bring back separator line * Fix ignore reasons in access module * Fix review issues --- src/tests/access.cairo | 4 +- src/tests/access/test_accesscontrol.cairo | 203 ++++++++++-------- .../access/test_dual_accesscontrol.cairo | 124 ++++++----- src/tests/access/test_dual_ownable.cairo | 12 +- 4 files changed, 189 insertions(+), 154 deletions(-) diff --git a/src/tests/access.cairo b/src/tests/access.cairo index c59f6dbc4..cfa981899 100644 --- a/src/tests/access.cairo +++ b/src/tests/access.cairo @@ -1,7 +1,7 @@ pub(crate) mod common; -// mod test_accesscontrol; -// mod test_dual_accesscontrol; +mod test_accesscontrol; +mod test_dual_accesscontrol; mod test_dual_ownable; mod test_ownable; mod test_ownable_twostep; diff --git a/src/tests/access/test_accesscontrol.cairo b/src/tests/access/test_accesscontrol.cairo index 35a76c136..809669e69 100644 --- a/src/tests/access/test_accesscontrol.cairo +++ b/src/tests/access/test_accesscontrol.cairo @@ -1,19 +1,18 @@ -use openzeppelin::access::accesscontrol::AccessControlComponent::InternalImpl; -use openzeppelin::access::accesscontrol::AccessControlComponent::RoleAdminChanged; -use openzeppelin::access::accesscontrol::AccessControlComponent::RoleGranted; -use openzeppelin::access::accesscontrol::AccessControlComponent::RoleRevoked; -use openzeppelin::access::accesscontrol::AccessControlComponent; -use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; -use openzeppelin::access::accesscontrol::interface::IACCESSCONTROL_ID; -use openzeppelin::access::accesscontrol::interface::{IAccessControl, IAccessControlCamel}; +use openzeppelin::access::accesscontrol::AccessControlComponent::{ + InternalImpl, RoleAdminChanged, RoleGranted, RoleRevoked +}; +use openzeppelin::access::accesscontrol::interface::{ + IAccessControl, IAccessControlCamel, IACCESSCONTROL_ID +}; +use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE}; use openzeppelin::introspection::interface::ISRC5; use openzeppelin::tests::mocks::accesscontrol_mocks::DualCaseAccessControlMock; use openzeppelin::tests::utils::constants::{ ADMIN, AUTHORIZED, OTHER, OTHER_ADMIN, ROLE, OTHER_ROLE, ZERO }; -use openzeppelin::tests::utils; +use openzeppelin::tests::utils::events::EventSpyExt; +use snforge_std::{EventSpy, spy_events, start_cheat_caller_address, test_address}; use starknet::ContractAddress; -use starknet::testing; // // Setup @@ -33,7 +32,6 @@ fn COMPONENT_STATE() -> ComponentState { fn setup() -> ComponentState { let mut state = COMPONENT_STATE(); state._grant_role(DEFAULT_ADMIN_ROLE, ADMIN()); - utils::drop_event(ZERO()); state } @@ -76,10 +74,11 @@ fn test_hasRole() { #[test] fn test_assert_only_role() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, ADMIN()); state.grant_role(ROLE, AUTHORIZED()); - testing::set_caller_address(AUTHORIZED()); + start_cheat_caller_address(contract_address, AUTHORIZED()); state.assert_only_role(ROLE); } @@ -87,7 +86,7 @@ fn test_assert_only_role() { #[should_panic(expected: ('Caller is missing role',))] fn test_assert_only_role_unauthorized() { let state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.assert_only_role(ROLE); } @@ -97,7 +96,7 @@ fn test_assert_only_role_unauthorized_when_authorized_for_another_role() { let mut state = setup(); state.grant_role(ROLE, AUTHORIZED()); - testing::set_caller_address(AUTHORIZED()); + start_cheat_caller_address(test_address(), AUTHORIZED()); state.assert_only_role(OTHER_ROLE); } @@ -108,10 +107,12 @@ fn test_assert_only_role_unauthorized_when_authorized_for_another_role() { #[test] fn test_grant_role() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, ADMIN()); state.grant_role(ROLE, AUTHORIZED()); - assert_event_role_granted(ROLE, AUTHORIZED(), ADMIN()); + spy.assert_only_event_role_granted(contract_address, ROLE, AUTHORIZED(), ADMIN()); let has_role = state.has_role(ROLE, AUTHORIZED()); assert!(has_role); @@ -120,10 +121,12 @@ fn test_grant_role() { #[test] fn test_grantRole() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + let mut spy = spy_events(); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, ADMIN()); state.grantRole(ROLE, AUTHORIZED()); - assert_event_role_granted(ROLE, AUTHORIZED(), ADMIN()); + spy.assert_only_event_role_granted(contract_address, ROLE, AUTHORIZED(), ADMIN()); let has_role = state.hasRole(ROLE, AUTHORIZED()); assert!(has_role); @@ -132,7 +135,7 @@ fn test_grantRole() { #[test] fn test_grant_role_multiple_times_for_granted_role() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + start_cheat_caller_address(test_address(), ADMIN()); state.grant_role(ROLE, AUTHORIZED()); state.grant_role(ROLE, AUTHORIZED()); @@ -142,7 +145,7 @@ fn test_grant_role_multiple_times_for_granted_role() { #[test] fn test_grantRole_multiple_times_for_granted_role() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + start_cheat_caller_address(test_address(), ADMIN()); state.grantRole(ROLE, AUTHORIZED()); state.grantRole(ROLE, AUTHORIZED()); @@ -153,7 +156,7 @@ fn test_grantRole_multiple_times_for_granted_role() { #[should_panic(expected: ('Caller is missing role',))] fn test_grant_role_unauthorized() { let mut state = setup(); - testing::set_caller_address(AUTHORIZED()); + start_cheat_caller_address(test_address(), AUTHORIZED()); state.grant_role(ROLE, AUTHORIZED()); } @@ -161,7 +164,7 @@ fn test_grant_role_unauthorized() { #[should_panic(expected: ('Caller is missing role',))] fn test_grantRole_unauthorized() { let mut state = setup(); - testing::set_caller_address(AUTHORIZED()); + start_cheat_caller_address(test_address(), AUTHORIZED()); state.grantRole(ROLE, AUTHORIZED()); } @@ -172,28 +175,29 @@ fn test_grantRole_unauthorized() { #[test] fn test_revoke_role_for_role_not_granted() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + start_cheat_caller_address(test_address(), ADMIN()); state.revoke_role(ROLE, AUTHORIZED()); } #[test] fn test_revokeRole_for_role_not_granted() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + start_cheat_caller_address(test_address(), ADMIN()); state.revokeRole(ROLE, AUTHORIZED()); } #[test] fn test_revoke_role_for_granted_role() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, ADMIN()); state.grant_role(ROLE, AUTHORIZED()); - utils::drop_event(ZERO()); + let mut spy = spy_events(); state.revoke_role(ROLE, AUTHORIZED()); - assert_event_role_revoked(ROLE, AUTHORIZED(), ADMIN()); + spy.assert_only_event_role_revoked(contract_address, ROLE, AUTHORIZED(), ADMIN()); let has_not_role = !state.has_role(ROLE, AUTHORIZED()); assert!(has_not_role); @@ -202,14 +206,15 @@ fn test_revoke_role_for_granted_role() { #[test] fn test_revokeRole_for_granted_role() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, ADMIN()); state.grantRole(ROLE, AUTHORIZED()); - utils::drop_event(ZERO()); + let mut spy = spy_events(); state.revokeRole(ROLE, AUTHORIZED()); - assert_event_role_revoked(ROLE, AUTHORIZED(), ADMIN()); + spy.assert_only_event_role_revoked(contract_address, ROLE, AUTHORIZED(), ADMIN()); let has_not_role = !state.hasRole(ROLE, AUTHORIZED()); assert!(has_not_role); @@ -218,7 +223,7 @@ fn test_revokeRole_for_granted_role() { #[test] fn test_revoke_role_multiple_times_for_granted_role() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + start_cheat_caller_address(test_address(), ADMIN()); state.grant_role(ROLE, AUTHORIZED()); state.revoke_role(ROLE, AUTHORIZED()); @@ -231,7 +236,7 @@ fn test_revoke_role_multiple_times_for_granted_role() { #[test] fn test_revokeRole_multiple_times_for_granted_role() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + start_cheat_caller_address(test_address(), ADMIN()); state.grantRole(ROLE, AUTHORIZED()); state.revokeRole(ROLE, AUTHORIZED()); @@ -245,7 +250,7 @@ fn test_revokeRole_multiple_times_for_granted_role() { #[should_panic(expected: ('Caller is missing role',))] fn test_revoke_role_unauthorized() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.revoke_role(ROLE, AUTHORIZED()); } @@ -253,7 +258,7 @@ fn test_revoke_role_unauthorized() { #[should_panic(expected: ('Caller is missing role',))] fn test_revokeRole_unauthorized() { let mut state = setup(); - testing::set_caller_address(OTHER()); + start_cheat_caller_address(test_address(), OTHER()); state.revokeRole(ROLE, AUTHORIZED()); } @@ -264,29 +269,30 @@ fn test_revokeRole_unauthorized() { #[test] fn test_renounce_role_for_role_not_granted() { let mut state = setup(); - testing::set_caller_address(AUTHORIZED()); + start_cheat_caller_address(test_address(), AUTHORIZED()); state.renounce_role(ROLE, AUTHORIZED()); } #[test] fn test_renounceRole_for_role_not_granted() { let mut state = setup(); - testing::set_caller_address(AUTHORIZED()); + start_cheat_caller_address(test_address(), AUTHORIZED()); state.renounceRole(ROLE, AUTHORIZED()); } #[test] fn test_renounce_role_for_granted_role() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, ADMIN()); state.grant_role(ROLE, AUTHORIZED()); - utils::drop_event(ZERO()); - testing::set_caller_address(AUTHORIZED()); + let mut spy = spy_events(); + start_cheat_caller_address(contract_address, AUTHORIZED()); state.renounce_role(ROLE, AUTHORIZED()); - assert_event_role_revoked(ROLE, AUTHORIZED(), AUTHORIZED()); + spy.assert_only_event_role_revoked(contract_address, ROLE, AUTHORIZED(), AUTHORIZED()); let has_not_role = !state.has_role(ROLE, AUTHORIZED()); assert!(has_not_role); @@ -295,15 +301,16 @@ fn test_renounce_role_for_granted_role() { #[test] fn test_renounceRole_for_granted_role() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, ADMIN()); state.grantRole(ROLE, AUTHORIZED()); - utils::drop_event(ZERO()); - testing::set_caller_address(AUTHORIZED()); + let mut spy = spy_events(); + start_cheat_caller_address(contract_address, AUTHORIZED()); state.renounceRole(ROLE, AUTHORIZED()); - assert_event_role_revoked(ROLE, AUTHORIZED(), AUTHORIZED()); + spy.assert_only_event_role_revoked(contract_address, ROLE, AUTHORIZED(), AUTHORIZED()); let has_not_role = !state.hasRole(ROLE, AUTHORIZED()); assert!(has_not_role); @@ -312,10 +319,11 @@ fn test_renounceRole_for_granted_role() { #[test] fn test_renounce_role_multiple_times_for_granted_role() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, ADMIN()); state.grant_role(ROLE, AUTHORIZED()); - testing::set_caller_address(AUTHORIZED()); + start_cheat_caller_address(contract_address, AUTHORIZED()); state.renounce_role(ROLE, AUTHORIZED()); state.renounce_role(ROLE, AUTHORIZED()); @@ -326,10 +334,11 @@ fn test_renounce_role_multiple_times_for_granted_role() { #[test] fn test_renounceRole_multiple_times_for_granted_role() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, ADMIN()); state.grantRole(ROLE, AUTHORIZED()); - testing::set_caller_address(AUTHORIZED()); + start_cheat_caller_address(contract_address, AUTHORIZED()); state.renounceRole(ROLE, AUTHORIZED()); state.renounceRole(ROLE, AUTHORIZED()); @@ -341,10 +350,11 @@ fn test_renounceRole_multiple_times_for_granted_role() { #[should_panic(expected: ('Can only renounce role for self',))] fn test_renounce_role_unauthorized() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, ADMIN()); state.grant_role(ROLE, AUTHORIZED()); - testing::set_caller_address(ZERO()); + start_cheat_caller_address(contract_address, ZERO()); state.renounce_role(ROLE, AUTHORIZED()); } @@ -352,7 +362,7 @@ fn test_renounce_role_unauthorized() { #[should_panic(expected: ('Can only renounce role for self',))] fn test_renounceRole_unauthorized() { let mut state = setup(); - testing::set_caller_address(ADMIN()); + start_cheat_caller_address(test_address(), ADMIN()); state.grantRole(ROLE, AUTHORIZED()); // Admin is unauthorized caller @@ -366,10 +376,16 @@ fn test_renounceRole_unauthorized() { #[test] fn test_set_role_admin() { let mut state = setup(); + let contract_address = test_address(); + let mut spy = spy_events(); + assert_eq!(state.get_role_admin(ROLE), DEFAULT_ADMIN_ROLE); state.set_role_admin(ROLE, OTHER_ROLE); - assert_event_role_admin_changed(ROLE, DEFAULT_ADMIN_ROLE, OTHER_ROLE); + spy + .assert_only_event_role_admin_changed( + contract_address, ROLE, DEFAULT_ADMIN_ROLE, OTHER_ROLE + ); let current_admin_role = state.get_role_admin(ROLE); assert_eq!(current_admin_role, OTHER_ROLE); @@ -378,12 +394,13 @@ fn test_set_role_admin() { #[test] fn test_new_admin_can_grant_roles() { let mut state = setup(); + let contract_address = test_address(); state.set_role_admin(ROLE, OTHER_ROLE); - testing::set_caller_address(ADMIN()); + start_cheat_caller_address(contract_address, ADMIN()); state.grant_role(OTHER_ROLE, OTHER_ADMIN()); - testing::set_caller_address(OTHER_ADMIN()); + start_cheat_caller_address(contract_address, OTHER_ADMIN()); state.grant_role(ROLE, AUTHORIZED()); let has_role = state.has_role(ROLE, AUTHORIZED()); @@ -393,12 +410,13 @@ fn test_new_admin_can_grant_roles() { #[test] fn test_new_admin_can_revoke_roles() { let mut state = setup(); + let contract_address = test_address(); state.set_role_admin(ROLE, OTHER_ROLE); - testing::set_caller_address(ADMIN()); + start_cheat_caller_address(contract_address, ADMIN()); state.grant_role(OTHER_ROLE, OTHER_ADMIN()); - testing::set_caller_address(OTHER_ADMIN()); + start_cheat_caller_address(contract_address, OTHER_ADMIN()); state.grant_role(ROLE, AUTHORIZED()); state.revoke_role(ROLE, AUTHORIZED()); @@ -411,7 +429,7 @@ fn test_new_admin_can_revoke_roles() { fn test_previous_admin_cannot_grant_roles() { let mut state = setup(); state.set_role_admin(ROLE, OTHER_ROLE); - testing::set_caller_address(ADMIN()); + start_cheat_caller_address(test_address(), ADMIN()); state.grant_role(ROLE, AUTHORIZED()); } @@ -420,7 +438,7 @@ fn test_previous_admin_cannot_grant_roles() { fn test_previous_admin_cannot_revoke_roles() { let mut state = setup(); state.set_role_admin(ROLE, OTHER_ROLE); - testing::set_caller_address(ADMIN()); + start_cheat_caller_address(test_address(), ADMIN()); state.revoke_role(ROLE, AUTHORIZED()); } @@ -447,31 +465,44 @@ fn test_default_admin_role_is_its_own_admin() { // Helpers // -fn assert_event_role_revoked(role: felt252, account: ContractAddress, sender: ContractAddress) { - let event = utils::pop_log::(ZERO()).unwrap(); - let expected = AccessControlComponent::Event::RoleRevoked( - RoleRevoked { role, account, sender } - ); - assert!(event == expected); - utils::assert_no_events_left(ZERO()); -} - -fn assert_event_role_granted(role: felt252, account: ContractAddress, sender: ContractAddress) { - let event = utils::pop_log::(ZERO()).unwrap(); - let expected = AccessControlComponent::Event::RoleGranted( - RoleGranted { role, account, sender } - ); - assert!(event == expected); - utils::assert_no_events_left(ZERO()); -} - -fn assert_event_role_admin_changed( - role: felt252, previous_admin_role: felt252, new_admin_role: felt252 -) { - let event = utils::pop_log::(ZERO()).unwrap(); - let expected = AccessControlComponent::Event::RoleAdminChanged( - RoleAdminChanged { role, previous_admin_role, new_admin_role } - ); - assert!(event == expected); - utils::assert_no_events_left(ZERO()); +#[generate_trait] +impl AccessControlSpyHelpersImpl of AccessControlSpyHelpers { + fn assert_only_event_role_revoked( + ref self: EventSpy, + contract: ContractAddress, + role: felt252, + account: ContractAddress, + sender: ContractAddress + ) { + let expected = AccessControlComponent::Event::RoleRevoked( + RoleRevoked { role, account, sender } + ); + self.assert_only_event(contract, expected); + } + + fn assert_only_event_role_granted( + ref self: EventSpy, + contract: ContractAddress, + role: felt252, + account: ContractAddress, + sender: ContractAddress + ) { + let expected = AccessControlComponent::Event::RoleGranted( + RoleGranted { role, account, sender } + ); + self.assert_only_event(contract, expected); + } + + fn assert_only_event_role_admin_changed( + ref self: EventSpy, + from_address: ContractAddress, + role: felt252, + previous_admin_role: felt252, + new_admin_role: felt252 + ) { + let expected = AccessControlComponent::Event::RoleAdminChanged( + RoleAdminChanged { role, previous_admin_role, new_admin_role } + ); + self.assert_only_event(from_address, expected); + } } diff --git a/src/tests/access/test_dual_accesscontrol.cairo b/src/tests/access/test_dual_accesscontrol.cairo index e172384a7..bd8f17781 100644 --- a/src/tests/access/test_dual_accesscontrol.cairo +++ b/src/tests/access/test_dual_accesscontrol.cairo @@ -1,22 +1,16 @@ use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; use openzeppelin::access::accesscontrol::dual_accesscontrol::DualCaseAccessControl; use openzeppelin::access::accesscontrol::dual_accesscontrol::DualCaseAccessControlTrait; -use openzeppelin::access::accesscontrol::interface::IACCESSCONTROL_ID; -use openzeppelin::access::accesscontrol::interface::IAccessControlCamelDispatcher; -use openzeppelin::access::accesscontrol::interface::IAccessControlCamelDispatcherTrait; -use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcher; -use openzeppelin::access::accesscontrol::interface::IAccessControlDispatcherTrait; -use openzeppelin::tests::mocks::accesscontrol_mocks::{ - CamelAccessControlMock, SnakeAccessControlMock, CamelAccessControlPanicMock, - SnakeAccessControlPanicMock +use openzeppelin::access::accesscontrol::interface::{ + IACCESSCONTROL_ID, IAccessControlCamelDispatcher, IAccessControlCamelDispatcherTrait +}; +use openzeppelin::access::accesscontrol::interface::{ + IAccessControlDispatcher, IAccessControlDispatcherTrait }; -use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; use openzeppelin::tests::utils::constants::{ADMIN, AUTHORIZED, ROLE}; use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; -use starknet::ContractAddress; -use starknet::contract_address_const; -use starknet::testing::set_contract_address; +use snforge_std::start_cheat_caller_address; // // Setup @@ -25,7 +19,7 @@ use starknet::testing::set_contract_address; fn setup_snake() -> (DualCaseAccessControl, IAccessControlDispatcher) { let mut calldata = array![]; calldata.append_serde(ADMIN()); - let target = utils::deploy(SnakeAccessControlMock::TEST_CLASS_HASH, calldata); + let target = utils::declare_and_deploy("SnakeAccessControlMock", calldata); ( DualCaseAccessControl { contract_address: target }, IAccessControlDispatcher { contract_address: target } @@ -35,7 +29,7 @@ fn setup_snake() -> (DualCaseAccessControl, IAccessControlDispatcher) { fn setup_camel() -> (DualCaseAccessControl, IAccessControlCamelDispatcher) { let mut calldata = array![]; calldata.append_serde(ADMIN()); - let target = utils::deploy(CamelAccessControlMock::TEST_CLASS_HASH, calldata); + let target = utils::declare_and_deploy("CamelAccessControlMock", calldata); ( DualCaseAccessControl { contract_address: target }, IAccessControlCamelDispatcher { contract_address: target } @@ -43,13 +37,13 @@ fn setup_camel() -> (DualCaseAccessControl, IAccessControlCamelDispatcher) { } fn setup_non_accesscontrol() -> DualCaseAccessControl { - let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, array![]); + let target = utils::declare_and_deploy("NonImplementingMock", array![]); DualCaseAccessControl { contract_address: target } } fn setup_accesscontrol_panic() -> (DualCaseAccessControl, DualCaseAccessControl) { - let snake_target = utils::deploy(SnakeAccessControlPanicMock::TEST_CLASS_HASH, array![]); - let camel_target = utils::deploy(CamelAccessControlPanicMock::TEST_CLASS_HASH, array![]); + let snake_target = utils::declare_and_deploy("SnakeAccessControlPanicMock", array![]); + let camel_target = utils::declare_and_deploy("CamelAccessControlPanicMock", array![]); ( DualCaseAccessControl { contract_address: snake_target }, DualCaseAccessControl { contract_address: camel_target } @@ -68,6 +62,7 @@ fn test_dual_supports_interface() { } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_supports_interface() { let dispatcher = setup_non_accesscontrol(); @@ -75,20 +70,21 @@ fn test_dual_no_supports_interface() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_supports_interface_exists_and_panics() { - let (dispatcher, _) = setup_accesscontrol_panic(); - dispatcher.supports_interface(IACCESSCONTROL_ID); + let (snake_dispatcher, _) = setup_accesscontrol_panic(); + snake_dispatcher.supports_interface(IACCESSCONTROL_ID); } #[test] fn test_dual_has_role() { - let (dispatcher, _) = setup_snake(); - let has_role = dispatcher.has_role(DEFAULT_ADMIN_ROLE, ADMIN()); + let (snake_dispatcher, _) = setup_snake(); + let has_role = snake_dispatcher.has_role(DEFAULT_ADMIN_ROLE, ADMIN()); assert!(has_role); } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_has_role() { let dispatcher = setup_non_accesscontrol(); @@ -96,7 +92,7 @@ fn test_dual_no_has_role() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_has_role_exists_and_panics() { let (dispatcher, _) = setup_accesscontrol_panic(); dispatcher.has_role(DEFAULT_ADMIN_ROLE, ADMIN()); @@ -105,12 +101,12 @@ fn test_dual_has_role_exists_and_panics() { #[test] fn test_dual_get_role_admin() { let (dispatcher, _) = setup_snake(); - let current_admin_role = dispatcher.get_role_admin(ROLE); assert_eq!(current_admin_role, DEFAULT_ADMIN_ROLE); } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_get_role_admin() { let dispatcher = setup_non_accesscontrol(); @@ -118,16 +114,16 @@ fn test_dual_no_get_role_admin() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_get_role_admin_exists_and_panics() { - let (dispatcher, _) = setup_accesscontrol_panic(); - dispatcher.get_role_admin(ROLE); + let (snake_dispatcher, _) = setup_accesscontrol_panic(); + snake_dispatcher.get_role_admin(ROLE); } #[test] fn test_dual_grant_role() { let (dispatcher, target) = setup_snake(); - set_contract_address(ADMIN()); + start_cheat_caller_address(target.contract_address, ADMIN()); dispatcher.grant_role(ROLE, AUTHORIZED()); let has_role = target.has_role(ROLE, AUTHORIZED()); @@ -135,6 +131,7 @@ fn test_dual_grant_role() { } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_grant_role() { let dispatcher = setup_non_accesscontrol(); @@ -142,16 +139,16 @@ fn test_dual_no_grant_role() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_grant_role_exists_and_panics() { - let (dispatcher, _) = setup_accesscontrol_panic(); - dispatcher.grant_role(ROLE, AUTHORIZED()); + let (snake_dispatcher, _) = setup_accesscontrol_panic(); + snake_dispatcher.grant_role(ROLE, AUTHORIZED()); } #[test] fn test_dual_revoke_role() { let (dispatcher, target) = setup_snake(); - set_contract_address(ADMIN()); + start_cheat_caller_address(target.contract_address, ADMIN()); dispatcher.revoke_role(ROLE, AUTHORIZED()); let has_not_role = !target.has_role(ROLE, AUTHORIZED()); @@ -159,6 +156,7 @@ fn test_dual_revoke_role() { } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_revoke_role() { let dispatcher = setup_non_accesscontrol(); @@ -166,16 +164,16 @@ fn test_dual_no_revoke_role() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_revoke_role_exists_and_panics() { - let (dispatcher, _) = setup_accesscontrol_panic(); - dispatcher.revoke_role(ROLE, AUTHORIZED()); + let (snake_dispatcher, _) = setup_accesscontrol_panic(); + snake_dispatcher.revoke_role(ROLE, AUTHORIZED()); } #[test] fn test_dual_renounce_role() { let (dispatcher, target) = setup_snake(); - set_contract_address(ADMIN()); + start_cheat_caller_address(target.contract_address, ADMIN()); dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); let has_not_role = !target.has_role(DEFAULT_ADMIN_ROLE, ADMIN()); @@ -183,6 +181,7 @@ fn test_dual_renounce_role() { } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_renounce_role() { let dispatcher = setup_non_accesscontrol(); @@ -190,10 +189,10 @@ fn test_dual_no_renounce_role() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_renounce_role_exists_and_panics() { - let (dispatcher, _) = setup_accesscontrol_panic(); - dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); + let (snake_dispatcher, _) = setup_accesscontrol_panic(); + snake_dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); } // @@ -201,6 +200,7 @@ fn test_dual_renounce_role_exists_and_panics() { // #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_hasRole() { let (dispatcher, _) = setup_camel(); @@ -209,13 +209,15 @@ fn test_dual_hasRole() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ("Some error",))] fn test_dual_hasRole_exists_and_panics() { - let (_, dispatcher) = setup_accesscontrol_panic(); - dispatcher.has_role(DEFAULT_ADMIN_ROLE, ADMIN()); + let (_, camel_dispatcher) = setup_accesscontrol_panic(); + camel_dispatcher.has_role(DEFAULT_ADMIN_ROLE, ADMIN()); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_getRoleAdmin() { let (dispatcher, _) = setup_camel(); @@ -224,16 +226,18 @@ fn test_dual_getRoleAdmin() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ("Some error",))] fn test_dual_getRoleAdmin_exists_and_panics() { - let (_, dispatcher) = setup_accesscontrol_panic(); - dispatcher.get_role_admin(ROLE); + let (_, camel_dispatcher) = setup_accesscontrol_panic(); + camel_dispatcher.get_role_admin(ROLE); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_grantRole() { let (dispatcher, target) = setup_camel(); - set_contract_address(ADMIN()); + start_cheat_caller_address(target.contract_address, ADMIN()); dispatcher.grant_role(ROLE, AUTHORIZED()); let has_role = target.hasRole(ROLE, AUTHORIZED()); @@ -241,16 +245,18 @@ fn test_dual_grantRole() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ("Some error",))] fn test_dual_grantRole_exists_and_panics() { - let (_, dispatcher) = setup_accesscontrol_panic(); - dispatcher.grant_role(ROLE, AUTHORIZED()); + let (_, camel_dispatcher) = setup_accesscontrol_panic(); + camel_dispatcher.grant_role(ROLE, AUTHORIZED()); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_revokeRole() { let (dispatcher, target) = setup_camel(); - set_contract_address(ADMIN()); + start_cheat_caller_address(target.contract_address, ADMIN()); dispatcher.grant_role(ROLE, AUTHORIZED()); dispatcher.revoke_role(ROLE, AUTHORIZED()); @@ -259,16 +265,18 @@ fn test_dual_revokeRole() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ("Some error",))] fn test_dual_revokeRole_exists_and_panics() { - let (_, dispatcher) = setup_accesscontrol_panic(); - dispatcher.revoke_role(ROLE, AUTHORIZED()); + let (_, camel_dispatcher) = setup_accesscontrol_panic(); + camel_dispatcher.revoke_role(ROLE, AUTHORIZED()); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_renounceRole() { let (dispatcher, target) = setup_camel(); - set_contract_address(ADMIN()); + start_cheat_caller_address(target.contract_address, ADMIN()); dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); let has_not_role = !target.hasRole(DEFAULT_ADMIN_ROLE, ADMIN()); @@ -276,9 +284,9 @@ fn test_dual_renounceRole() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ("Some error",))] fn test_dual_renounceRole_exists_and_panics() { - let (_, dispatcher) = setup_accesscontrol_panic(); - dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); + let (_, camel_dispatcher) = setup_accesscontrol_panic(); + camel_dispatcher.renounce_role(DEFAULT_ADMIN_ROLE, ADMIN()); } - diff --git a/src/tests/access/test_dual_ownable.cairo b/src/tests/access/test_dual_ownable.cairo index 16c22514f..08a863498 100644 --- a/src/tests/access/test_dual_ownable.cairo +++ b/src/tests/access/test_dual_ownable.cairo @@ -3,10 +3,6 @@ use openzeppelin::access::ownable::dual_ownable::{DualCaseOwnable, DualCaseOwnab use openzeppelin::access::ownable::interface::{ IOwnableDispatcher, IOwnableCamelOnlyDispatcher, IOwnableDispatcherTrait }; -use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; -use openzeppelin::tests::mocks::ownable_mocks::{ - CamelOwnableMock, CamelOwnablePanicMock, SnakeOwnableMock, SnakeOwnablePanicMock -}; use openzeppelin::tests::utils::constants::{OWNER, NEW_OWNER}; use openzeppelin::tests::utils; use openzeppelin::utils::serde::SerializedAppend; @@ -138,7 +134,7 @@ fn test_dual_renounce_ownership_exists_and_panics() { // #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_transferOwnership() { let (dispatcher, _) = setup_camel(); start_cheat_caller_address(dispatcher.contract_address, OWNER()); @@ -149,7 +145,7 @@ fn test_dual_transferOwnership() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_transferOwnership_exists_and_panics() { let (_, camel_dispatcher) = setup_ownable_panic(); @@ -157,7 +153,7 @@ fn test_dual_transferOwnership_exists_and_panics() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_renounceOwnership() { let (dispatcher, _) = setup_camel(); start_cheat_caller_address(dispatcher.contract_address, OWNER()); @@ -168,7 +164,7 @@ fn test_dual_renounceOwnership() { } #[test] -#[ignore] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. #[should_panic(expected: ("Some error",))] fn test_dual_renounceOwnership_exists_and_panics() { let (_, camel_dispatcher) = setup_ownable_panic(); From bc316cae4c09bac6e27f3e43a3ff410ed1f3eec6 Mon Sep 17 00:00:00 2001 From: immrsd <103599616+immrsd@users.noreply.github.com> Date: Fri, 26 Jul 2024 12:51:53 +0200 Subject: [PATCH 17/20] Migrate Starknet Account tests (#1050) * Update test utils and helpers * Update signature tests * Update account tests * Update dual account tests * Run linter * Run linter * Fix review issues * Update ignore reason messages * Run linter * Support eth account tests changes * Run linter * Improve setup functions, remove unused imports * Remove unnecessary accept_ownership step, make use of serialized_sign fn --- src/tests/account/ethereum.cairo | 1 - src/tests/account/starknet.cairo | 6 +- src/tests/account/starknet/common.cairo | 35 +- src/tests/account/starknet/test_account.cairo | 391 +++++++++--------- .../account/starknet/test_dual_account.cairo | 221 ++++------ src/tests/account/test_signature.cairo | 58 ++- src/tests/utils/constants.cairo | 20 +- src/tests/utils/signing.cairo | 8 + 8 files changed, 349 insertions(+), 391 deletions(-) diff --git a/src/tests/account/ethereum.cairo b/src/tests/account/ethereum.cairo index 2d87a5992..d6fcf488b 100644 --- a/src/tests/account/ethereum.cairo +++ b/src/tests/account/ethereum.cairo @@ -2,4 +2,3 @@ pub(crate) mod common; mod test_dual_eth_account; mod test_eth_account; - diff --git a/src/tests/account/starknet.cairo b/src/tests/account/starknet.cairo index f426692e3..8c3e80c69 100644 --- a/src/tests/account/starknet.cairo +++ b/src/tests/account/starknet.cairo @@ -1,5 +1,3 @@ pub(crate) mod common; -// mod test_account; -// mod test_dual_account; - - +mod test_account; +mod test_dual_account; diff --git a/src/tests/account/starknet/common.cairo b/src/tests/account/starknet/common.cairo index f029dff0a..26554ba33 100644 --- a/src/tests/account/starknet/common.cairo +++ b/src/tests/account/starknet/common.cairo @@ -1,35 +1,32 @@ +use core::hash::{HashStateTrait, HashStateExTrait}; +use core::poseidon::PoseidonTrait; use openzeppelin::account::AccountComponent::{OwnerAdded, OwnerRemoved}; use openzeppelin::account::AccountComponent; -use openzeppelin::tests::utils::constants::{NAME, SYMBOL, NEW_PUBKEY}; +use openzeppelin::tests::utils::constants::{NAME, SYMBOL, TRANSACTION_HASH}; use openzeppelin::tests::utils::events::EventSpyExt; +use openzeppelin::tests::utils::signing::StarkKeyPair; use openzeppelin::tests::utils; -use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; +use openzeppelin::token::erc20::interface::IERC20Dispatcher; use openzeppelin::utils::serde::SerializedAppend; use snforge_std::EventSpy; +use snforge_std::signature::stark_curve::StarkCurveSignerImpl; use starknet::ContractAddress; #[derive(Drop)] pub(crate) struct SignedTransactionData { - pub(crate) private_key: felt252, - pub(crate) public_key: felt252, pub(crate) tx_hash: felt252, pub(crate) r: felt252, pub(crate) s: felt252 } -pub(crate) fn SIGNED_TX_DATA() -> SignedTransactionData { - SignedTransactionData { - private_key: 1234, - public_key: NEW_PUBKEY, - tx_hash: 0x601d3d2e265c10ff645e1554c435e72ce6721f0ba5fc96f0c650bfc6231191a, - r: 0x6bc22689efcaeacb9459577138aff9f0af5b77ee7894cdc8efabaf760f6cf6e, - s: 0x295989881583b9325436851934334faa9d639a2094cd1e2f8691c8a71cd4cdf - } +pub(crate) fn SIGNED_TX_DATA(key_pair: StarkKeyPair) -> SignedTransactionData { + let tx_hash = TRANSACTION_HASH; + let (r, s) = key_pair.sign(tx_hash).unwrap(); + SignedTransactionData { tx_hash, r, s } } pub(crate) fn deploy_erc20(recipient: ContractAddress, initial_supply: u256) -> IERC20Dispatcher { let mut calldata = array![]; - calldata.append_serde(NAME()); calldata.append_serde(SYMBOL()); calldata.append_serde(initial_supply); @@ -39,6 +36,18 @@ pub(crate) fn deploy_erc20(recipient: ContractAddress, initial_supply: u256) -> IERC20Dispatcher { contract_address: address } } +pub(crate) fn get_accept_ownership_signature( + account_address: ContractAddress, current_public_key: felt252, new_key_pair: StarkKeyPair +) -> Span { + let msg_hash = PoseidonTrait::new() + .update_with('StarkNet Message') + .update_with('accept_ownership') + .update_with(account_address) + .update_with(current_public_key) + .finalize(); + let (sig_r, sig_s) = new_key_pair.sign(msg_hash).unwrap(); + array![sig_r, sig_s].span() +} #[generate_trait] pub(crate) impl AccountSpyHelpersImpl of AccountSpyHelpers { diff --git a/src/tests/account/starknet/test_account.cairo b/src/tests/account/starknet/test_account.cairo index ed4dc81ed..13dd7f36e 100644 --- a/src/tests/account/starknet/test_account.cairo +++ b/src/tests/account/starknet/test_account.cairo @@ -1,4 +1,5 @@ use core::num::traits::Zero; +use core::starknet::SyscallResultTrait; use openzeppelin::account::AccountComponent::{InternalTrait, SRC6CamelOnlyImpl}; use openzeppelin::account::AccountComponent::{PublicKeyCamelImpl, PublicKeyImpl}; use openzeppelin::account::AccountComponent; @@ -6,32 +7,24 @@ use openzeppelin::account::interface::{AccountABIDispatcherTrait, AccountABIDisp use openzeppelin::account::interface::{ISRC6, ISRC6_ID}; use openzeppelin::introspection::interface::{ISRC5, ISRC5_ID}; use openzeppelin::tests::mocks::account_mocks::DualCaseAccountMock; +use openzeppelin::tests::utils::constants::stark::{KEY_PAIR, KEY_PAIR_2}; use openzeppelin::tests::utils::constants::{ - PUBKEY, NEW_PUBKEY, SALT, ZERO, QUERY_OFFSET, QUERY_VERSION, MIN_TRANSACTION_VERSION + SALT, ZERO, OTHER, CALLER, RECIPIENT, QUERY_OFFSET, QUERY_VERSION, MIN_TRANSACTION_VERSION }; +use openzeppelin::tests::utils::signing::StarkKeyPair; use openzeppelin::tests::utils; -use openzeppelin::token::erc20::interface::{IERC20DispatcherTrait, IERC20Dispatcher}; +use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; -use starknet::ContractAddress; +use snforge_std::{ + cheat_signature_global, cheat_transaction_version_global, cheat_transaction_hash_global +}; +use snforge_std::{spy_events, declare, test_address, start_cheat_caller_address}; use starknet::account::Call; -use starknet::contract_address_const; -use starknet::testing; +use starknet::{contract_address_const, ContractAddress, ClassHash}; -use super::common::{assert_only_event_owner_added, assert_event_owner_removed}; -use super::common::{deploy_erc20, SIGNED_TX_DATA, SignedTransactionData}; - -// -// Constants -// - -fn CLASS_HASH() -> felt252 { - DualCaseAccountMock::TEST_CLASS_HASH -} - -fn ACCOUNT_ADDRESS() -> ContractAddress { - contract_address_const::<0x111111>() -} +use super::common::{AccountSpyHelpers, SignedTransactionData}; +use super::common::{deploy_erc20, SIGNED_TX_DATA, get_accept_ownership_signature}; // // Setup @@ -47,28 +40,26 @@ fn COMPONENT_STATE() -> ComponentState { AccountComponent::component_state_for_testing() } -fn setup() -> ComponentState { +fn setup(key_pair: StarkKeyPair) -> ComponentState { let mut state = COMPONENT_STATE(); - state.initializer(PUBKEY); - utils::drop_event(ZERO()); + state.initializer(key_pair.public_key); state } -fn setup_dispatcher(data: Option<@SignedTransactionData>) -> AccountABIDispatcher { - testing::set_version(MIN_TRANSACTION_VERSION); +fn setup_dispatcher( + key_pair: StarkKeyPair, data: SignedTransactionData +) -> (AccountABIDispatcher, felt252) { + let contract_class = declare("DualCaseAccountMock").unwrap_syscall(); + let calldata = array![key_pair.public_key]; + let address = utils::deploy(contract_class, calldata); + let dispatcher = AccountABIDispatcher { contract_address: address }; - let mut calldata = array![]; - if data.is_some() { - let data = data.unwrap(); - testing::set_signature(array![*data.r, *data.s].span()); - testing::set_transaction_hash(*data.transaction_hash); - - calldata.append(*data.public_key); - } else { - calldata.append(PUBKEY); - } - let address = utils::deploy(CLASS_HASH(), calldata); - AccountABIDispatcher { contract_address: address } + cheat_signature_global(array![data.r, data.s].span()); + cheat_transaction_hash_global(data.tx_hash); + cheat_transaction_version_global(MIN_TRANSACTION_VERSION); + start_cheat_caller_address(address, ZERO()); + + (dispatcher, contract_class.class_hash.into()) } // @@ -78,36 +69,34 @@ fn setup_dispatcher(data: Option<@SignedTransactionData>) -> AccountABIDispatche #[test] fn test_is_valid_signature() { let mut state = COMPONENT_STATE(); - let data = SIGNED_TX_DATA(); - let hash = data.transaction_hash; + let key_pair = KEY_PAIR(); + let data = SIGNED_TX_DATA(key_pair); - let mut good_signature = array![data.r, data.s]; - let mut bad_signature = array![0x987, 0x564]; + state._set_public_key(key_pair.public_key); - state._set_public_key(data.public_key); - - let is_valid = state.is_valid_signature(hash, good_signature); + let good_signature = array![data.r, data.s]; + let is_valid = state.is_valid_signature(data.tx_hash, good_signature); assert_eq!(is_valid, starknet::VALIDATED); - let is_valid = state.is_valid_signature(hash, bad_signature); + let bad_signature = array!['BAD', 'SIGNATURE']; + let is_valid = state.is_valid_signature(data.tx_hash, bad_signature); assert!(is_valid.is_zero(), "Should reject invalid signature"); } #[test] fn test_isValidSignature() { let mut state = COMPONENT_STATE(); - let data = SIGNED_TX_DATA(); - let hash = data.transaction_hash; - - let mut good_signature = array![data.r, data.s]; - let mut bad_signature = array![0x987, 0x564]; + let key_pair = KEY_PAIR(); + let data = SIGNED_TX_DATA(key_pair); - state._set_public_key(data.public_key); + state._set_public_key(key_pair.public_key); - let is_valid = state.isValidSignature(hash, good_signature); + let good_signature = array![data.r, data.s]; + let is_valid = state.isValidSignature(data.tx_hash, good_signature); assert_eq!(is_valid, starknet::VALIDATED); - let is_valid = state.isValidSignature(hash, bad_signature); + let bad_signature = array!['BAD', 'SIGNATURE']; + let is_valid = state.isValidSignature(data.tx_hash, bad_signature); assert!(is_valid.is_zero(), "Should reject invalid signature"); } @@ -117,96 +106,99 @@ fn test_isValidSignature() { #[test] fn test_validate_deploy() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let key_pair = KEY_PAIR(); + let (account, class_hash) = setup_dispatcher(key_pair, SIGNED_TX_DATA(key_pair)); // `__validate_deploy__` does not directly use the passed arguments. Their // values are already integrated in the tx hash. The passed arguments in this // testing context are decoupled from the signature and have no effect on the test. - let is_valid = account.__validate_deploy__(CLASS_HASH(), SALT, PUBKEY); + let is_valid = account.__validate_deploy__(class_hash, SALT, key_pair.public_key); assert_eq!(is_valid, starknet::VALIDATED); } #[test] -#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid signature',))] fn test_validate_deploy_invalid_signature_data() { - let mut data = SIGNED_TX_DATA(); - data.transaction_hash += 1; - let account = setup_dispatcher(Option::Some(@data)); + let key_pair = KEY_PAIR(); + let mut data = SIGNED_TX_DATA(key_pair); + data.tx_hash += 1; + let (account, class_hash) = setup_dispatcher(key_pair, data); - account.__validate_deploy__(CLASS_HASH(), SALT, PUBKEY); + account.__validate_deploy__(class_hash, SALT, key_pair.public_key); } #[test] -#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid signature',))] fn test_validate_deploy_invalid_signature_length() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); - let mut signature = array![]; + let key_pair = KEY_PAIR(); + let (account, class_hash) = setup_dispatcher(key_pair, SIGNED_TX_DATA(key_pair)); + let invalid_len_sig = array!['INVALID_LEN_SIG']; + cheat_signature_global(invalid_len_sig.span()); - signature.append(0x1); - testing::set_signature(signature.span()); - - account.__validate_deploy__(CLASS_HASH(), SALT, PUBKEY); + account.__validate_deploy__(class_hash, SALT, key_pair.public_key); } #[test] -#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid signature',))] fn test_validate_deploy_empty_signature() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let key_pair = KEY_PAIR(); + let (account, class_hash) = setup_dispatcher(key_pair, SIGNED_TX_DATA(key_pair)); let empty_sig = array![]; - testing::set_signature(empty_sig.span()); - account.__validate_deploy__(CLASS_HASH(), SALT, PUBKEY); + cheat_signature_global(empty_sig.span()); + account.__validate_deploy__(class_hash, SALT, key_pair.public_key); } #[test] fn test_validate_declare() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let key_pair = KEY_PAIR(); + let (account, class_hash) = setup_dispatcher(key_pair, SIGNED_TX_DATA(key_pair)); // `__validate_declare__` does not directly use the class_hash argument. Its // value is already integrated in the tx hash. The class_hash argument in this // testing context is decoupled from the signature and has no effect on the test. - let is_valid = account.__validate_declare__(CLASS_HASH()); + let is_valid = account.__validate_declare__(class_hash); assert_eq!(is_valid, starknet::VALIDATED); } #[test] -#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid signature',))] fn test_validate_declare_invalid_signature_data() { - let mut data = SIGNED_TX_DATA(); - data.transaction_hash += 1; - let account = setup_dispatcher(Option::Some(@data)); + let key_pair = KEY_PAIR(); + let mut data = SIGNED_TX_DATA(key_pair); + data.tx_hash += 1; + let (account, class_hash) = setup_dispatcher(key_pair, data); - account.__validate_declare__(CLASS_HASH()); + account.__validate_declare__(class_hash); } #[test] -#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid signature',))] fn test_validate_declare_invalid_signature_length() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); - let mut signature = array![]; + let key_pair = KEY_PAIR(); + let (account, class_hash) = setup_dispatcher(key_pair, SIGNED_TX_DATA(key_pair)); + let invalid_len_sig = array!['INVALID_LEN_SIG']; + cheat_signature_global(invalid_len_sig.span()); - signature.append(0x1); - testing::set_signature(signature.span()); - - account.__validate_declare__(CLASS_HASH()); + account.__validate_declare__(class_hash); } #[test] -#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid signature',))] fn test_validate_declare_empty_signature() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let key_pair = KEY_PAIR(); + let (account, class_hash) = setup_dispatcher(key_pair, SIGNED_TX_DATA(key_pair)); let empty_sig = array![]; + cheat_signature_global(empty_sig.span()); - testing::set_signature(empty_sig.span()); - - account.__validate_declare__(CLASS_HASH()); + account.__validate_declare__(class_hash); } fn test_execute_with_version(version: Option) { - let data = SIGNED_TX_DATA(); - let account = setup_dispatcher(Option::Some(@data)); + let key_pair = KEY_PAIR(); + let (account, _) = setup_dispatcher(key_pair, SIGNED_TX_DATA(key_pair)); let erc20 = deploy_erc20(account.contract_address, 1000); - let recipient = contract_address_const::<0x123>(); + let recipient = RECIPIENT(); // Craft call and add to calls array let mut calldata = array![]; @@ -216,12 +208,11 @@ fn test_execute_with_version(version: Option) { let call = Call { to: erc20.contract_address, selector: selectors::transfer, calldata: calldata.span() }; - let mut calls = array![]; - calls.append(call); + let calls = array![call]; // Handle version for test - if version.is_some() { - testing::set_version(version.unwrap()); + if let Option::Some(version) = version { + cheat_transaction_version_global(version); } // Execute @@ -239,7 +230,7 @@ fn test_execute_with_version(version: Option) { #[test] fn test_execute() { - test_execute_with_version(Option::None(())); + test_execute_with_version(Option::None); } #[test] @@ -253,7 +244,7 @@ fn test_execute_query_version() { } #[test] -#[should_panic(expected: ('Account: invalid tx version', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid tx version',))] fn test_execute_invalid_query_version() { test_execute_with_version(Option::Some(QUERY_OFFSET)); } @@ -264,38 +255,40 @@ fn test_execute_future_query_version() { } #[test] -#[should_panic(expected: ('Account: invalid tx version', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid tx version',))] fn test_execute_invalid_version() { test_execute_with_version(Option::Some(MIN_TRANSACTION_VERSION - 1)); } #[test] fn test_validate() { + let key_pair = KEY_PAIR(); + let (account, _) = setup_dispatcher(key_pair, SIGNED_TX_DATA(key_pair)); let calls = array![]; - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); let is_valid = account.__validate__(calls); assert_eq!(is_valid, starknet::VALIDATED); } #[test] -#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid signature',))] fn test_validate_invalid() { + let key_pair = KEY_PAIR(); + let mut data = SIGNED_TX_DATA(key_pair); + data.tx_hash += 1; + let (account, _) = setup_dispatcher(key_pair, data); let calls = array![]; - let mut data = SIGNED_TX_DATA(); - data.transaction_hash += 1; - let account = setup_dispatcher(Option::Some(@data)); account.__validate__(calls); } #[test] fn test_multicall() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); + let key_pair = KEY_PAIR(); + let (account, _) = setup_dispatcher(key_pair, SIGNED_TX_DATA(key_pair)); let erc20 = deploy_erc20(account.contract_address, 1000); - let recipient1 = contract_address_const::<0x123>(); - let recipient2 = contract_address_const::<0x456>(); - let mut calls = array![]; + let recipient1 = RECIPIENT(); + let recipient2 = OTHER(); // Craft call1 let mut calldata1 = array![]; @@ -316,8 +309,7 @@ fn test_multicall() { }; // Bundle calls and exeute - calls.append(call1); - calls.append(call2); + let calls = array![call1, call2]; let ret = account.__execute__(calls); // Assert that the transfers were successful @@ -338,8 +330,9 @@ fn test_multicall() { #[test] fn test_multicall_zero_calls() { - let account = setup_dispatcher(Option::Some(@SIGNED_TX_DATA())); - let mut calls = array![]; + let key_pair = KEY_PAIR(); + let (account, _) = setup_dispatcher(key_pair, SIGNED_TX_DATA(key_pair)); + let calls = array![]; let response = account.__execute__(calls); assert!(response.is_empty()); @@ -348,12 +341,11 @@ fn test_multicall_zero_calls() { #[test] #[should_panic(expected: ('Account: invalid caller',))] fn test_account_called_from_contract() { - let state = setup(); + let state = setup(KEY_PAIR()); + let account_address = test_address(); let calls = array![]; - let caller = contract_address_const::<0x123>(); - testing::set_contract_address(ACCOUNT_ADDRESS()); - testing::set_caller_address(caller); + start_cheat_caller_address(account_address, CALLER()); state.__execute__(calls); } @@ -365,33 +357,36 @@ fn test_account_called_from_contract() { #[test] fn test_public_key_setter_and_getter() { let mut state = COMPONENT_STATE(); - testing::set_contract_address(ACCOUNT_ADDRESS()); - testing::set_caller_address(ACCOUNT_ADDRESS()); + let account_address = test_address(); + let key_pair = KEY_PAIR(); + let new_key_pair = KEY_PAIR_2(); + start_cheat_caller_address(account_address, account_address); - state._set_public_key(PUBKEY); - utils::drop_event(ACCOUNT_ADDRESS()); - let public_key = state.get_public_key(); - assert_eq!(public_key, PUBKEY); + state._set_public_key(key_pair.public_key); + assert_eq!(state.get_public_key(), key_pair.public_key); // Set key - state.set_public_key(NEW_PUBKEY, get_accept_ownership_signature()); + let mut spy = spy_events(); + let signature = get_accept_ownership_signature( + account_address, key_pair.public_key, new_key_pair + ); + state.set_public_key(new_key_pair.public_key, signature); - assert_event_owner_removed(ACCOUNT_ADDRESS(), PUBKEY); - assert_only_event_owner_added(ACCOUNT_ADDRESS(), NEW_PUBKEY); + spy.assert_event_owner_removed(account_address, key_pair.public_key); + spy.assert_only_event_owner_added(account_address, new_key_pair.public_key); - let public_key = state.get_public_key(); - assert_eq!(public_key, NEW_PUBKEY); + assert_eq!(state.get_public_key(), new_key_pair.public_key); } #[test] #[should_panic(expected: ('Account: unauthorized',))] fn test_public_key_setter_different_account() { let mut state = COMPONENT_STATE(); - let caller = contract_address_const::<0x123>(); - testing::set_contract_address(ACCOUNT_ADDRESS()); - testing::set_caller_address(caller); + let account_address = test_address(); + let new_public_key = KEY_PAIR_2().public_key; + start_cheat_caller_address(account_address, CALLER()); - state.set_public_key(NEW_PUBKEY, array![].span()); + state.set_public_key(new_public_key, array![].span()); } // @@ -401,33 +396,36 @@ fn test_public_key_setter_different_account() { #[test] fn test_public_key_setter_and_getter_camel() { let mut state = COMPONENT_STATE(); - testing::set_contract_address(ACCOUNT_ADDRESS()); - testing::set_caller_address(ACCOUNT_ADDRESS()); + let account_address = test_address(); + let key_pair = KEY_PAIR(); + let new_key_pair = KEY_PAIR_2(); + start_cheat_caller_address(account_address, account_address); - state._set_public_key(PUBKEY); - utils::drop_event(ACCOUNT_ADDRESS()); - let public_key = state.getPublicKey(); - assert_eq!(public_key, PUBKEY); + state._set_public_key(key_pair.public_key); + assert_eq!(state.getPublicKey(), key_pair.public_key); // Set key - state.setPublicKey(NEW_PUBKEY, get_accept_ownership_signature()); + let mut spy = spy_events(); + let signature = get_accept_ownership_signature( + account_address, key_pair.public_key, new_key_pair + ); + state.setPublicKey(new_key_pair.public_key, signature); - assert_event_owner_removed(ACCOUNT_ADDRESS(), PUBKEY); - assert_only_event_owner_added(ACCOUNT_ADDRESS(), NEW_PUBKEY); + spy.assert_event_owner_removed(account_address, key_pair.public_key); + spy.assert_only_event_owner_added(account_address, new_key_pair.public_key); - let public_key = state.getPublicKey(); - assert_eq!(public_key, NEW_PUBKEY); + assert_eq!(state.getPublicKey(), new_key_pair.public_key); } #[test] #[should_panic(expected: ('Account: unauthorized',))] fn test_public_key_setter_different_account_camel() { let mut state = COMPONENT_STATE(); - let caller = contract_address_const::<0x123>(); - testing::set_contract_address(ACCOUNT_ADDRESS()); - testing::set_caller_address(caller); + let account_address = test_address(); + let new_public_key = KEY_PAIR_2().public_key; + start_cheat_caller_address(account_address, CALLER()); - state.setPublicKey(NEW_PUBKEY, array![].span()); + state.setPublicKey(new_public_key, array![].span()); } // @@ -438,12 +436,14 @@ fn test_public_key_setter_different_account_camel() { fn test_initializer() { let mut state = COMPONENT_STATE(); let mock_state = CONTRACT_STATE(); + let account_address = test_address(); + let public_key = KEY_PAIR().public_key; + let mut spy = spy_events(); - state.initializer(PUBKEY); - assert_only_event_owner_added(ZERO(), PUBKEY); + state.initializer(public_key); + spy.assert_only_event_owner_added(account_address, public_key); - let public_key = state.get_public_key(); - assert_eq!(public_key, PUBKEY); + assert_eq!(state.get_public_key(), public_key); let supports_isrc5 = mock_state.supports_interface(ISRC5_ID); assert!(supports_isrc5); @@ -455,9 +455,9 @@ fn test_initializer() { #[test] fn test_assert_only_self_true() { let mut state = COMPONENT_STATE(); + let account_address = test_address(); + start_cheat_caller_address(account_address, account_address); - testing::set_contract_address(ACCOUNT_ADDRESS()); - testing::set_caller_address(ACCOUNT_ADDRESS()); state.assert_only_self(); } @@ -465,88 +465,67 @@ fn test_assert_only_self_true() { #[should_panic(expected: ('Account: unauthorized',))] fn test_assert_only_self_false() { let mut state = COMPONENT_STATE(); + let account_address = test_address(); + start_cheat_caller_address(account_address, OTHER()); - testing::set_contract_address(ACCOUNT_ADDRESS()); - let other = contract_address_const::<0x4567>(); - testing::set_caller_address(other); state.assert_only_self(); } #[test] fn test_assert_valid_new_owner() { - let state = setup(); + let key_pair = KEY_PAIR(); + let state = setup(key_pair); + let account_address = test_address(); + + let new_key_pair = KEY_PAIR_2(); + let signature = get_accept_ownership_signature( + account_address, key_pair.public_key, new_key_pair + ); - testing::set_contract_address(ACCOUNT_ADDRESS()); - state.assert_valid_new_owner(PUBKEY, NEW_PUBKEY, get_accept_ownership_signature()); + state.assert_valid_new_owner(key_pair.public_key, new_key_pair.public_key, signature); } #[test] #[should_panic(expected: ('Account: invalid signature',))] fn test_assert_valid_new_owner_invalid_signature() { - let state = setup(); + let key_pair = KEY_PAIR(); + let state = setup(key_pair); - testing::set_contract_address(ACCOUNT_ADDRESS()); - let bad_signature = array![ - 0x2ce8fbcf8a793ee5b2a57254dc96863b696698943e3bc7845285f3851336318, - 0x6009f5720649ff1ceb0aba44f85bec3572c81aecb9d2dada7c0cc70b791debe - ]; - state.assert_valid_new_owner(PUBKEY, NEW_PUBKEY, bad_signature.span()); + let new_key_pair = KEY_PAIR_2(); + let bad_signature = array!['BAD', 'SIGNATURE']; + + state + .assert_valid_new_owner(key_pair.public_key, new_key_pair.public_key, bad_signature.span()); } #[test] fn test__is_valid_signature() { let mut state = COMPONENT_STATE(); - let data = SIGNED_TX_DATA(); - let hash = data.transaction_hash; + let key_pair = KEY_PAIR(); + let data = SIGNED_TX_DATA(key_pair); - let mut good_signature = array![data.r, data.s]; - let mut bad_signature = array![0x987, 0x564]; - let mut invalid_length_signature = array![0x987]; + state._set_public_key(key_pair.public_key); - state._set_public_key(data.public_key); + let good_signature = array![data.r, data.s]; + assert!(state._is_valid_signature(data.tx_hash, good_signature.span())); - let is_valid = state._is_valid_signature(hash, good_signature.span()); - assert!(is_valid); + let bad_signature = array!['BAD', 'SIGNATURE']; + assert!(!state._is_valid_signature(data.tx_hash, bad_signature.span())); - let is_not_valid = !state._is_valid_signature(hash, bad_signature.span()); - assert!(is_not_valid); - - let is_not_valid = !state._is_valid_signature(hash, invalid_length_signature.span()); - assert!(is_not_valid); + let invalid_length_signature = array!['SINGLE_ELEMENT']; + assert!(!state._is_valid_signature(data.tx_hash, invalid_length_signature.span())); } #[test] fn test__set_public_key() { let mut state = COMPONENT_STATE(); - state._set_public_key(PUBKEY); - - assert_only_event_owner_added(ZERO(), PUBKEY); + let mut spy = spy_events(); + let public_key = KEY_PAIR().public_key; + let account_address = test_address(); - let public_key = state.get_public_key(); - assert_eq!(public_key, PUBKEY); -} - -// -// Helpers -// + state._set_public_key(public_key); -fn get_accept_ownership_signature() -> Span { - // 0x7b3d2ce38c132a36e692f6e809b518276d091513af3baf0e94ce2abceee3632 = - // PoseidonTrait::new() - // .update_with('StarkNet Message') - // .update_with('accept_ownership') - // .update_with(ACCOUNT_ADDRESS()) - // .update_with(PUBKEY) - // .finalize(); - - // This signature was computed using starknet js sdk from the following values: - // - private_key: '1234' - // - public_key: 0x26da8d11938b76025862be14fdb8b28438827f73e75e86f7bfa38b196951fa7 - // - msg_hash: 0x7b3d2ce38c132a36e692f6e809b518276d091513af3baf0e94ce2abceee3632 - array![ - 0x1ce8fbcf8a793ee5b2a57254dc96863b696698943e3bc7845285f3851336318, - 0x6009f5720649ff1ceb0aba44f85bec3572c81aecb9d2dada7c0cc70b791debe - ] - .span() + spy.assert_only_event_owner_added(account_address, public_key); + assert_eq!(state.get_public_key(), public_key); } diff --git a/src/tests/account/starknet/test_dual_account.cairo b/src/tests/account/starknet/test_dual_account.cairo index 003075b1f..ab0887896 100644 --- a/src/tests/account/starknet/test_dual_account.cairo +++ b/src/tests/account/starknet/test_dual_account.cairo @@ -2,45 +2,39 @@ use openzeppelin::account::dual_account::{DualCaseAccountABI, DualCaseAccount}; use openzeppelin::account::interface::{AccountABIDispatcherTrait, AccountABIDispatcher}; use openzeppelin::introspection::interface::ISRC5_ID; use openzeppelin::tests::account::starknet::common::SIGNED_TX_DATA; -use openzeppelin::tests::mocks::account_mocks::{ - CamelAccountPanicMock, CamelAccountMock, SnakeAccountMock, SnakeAccountPanicMock -}; -use openzeppelin::tests::mocks::non_implementing_mock::NonImplementingMock; -use openzeppelin::tests::utils::constants::{PUBKEY, NEW_PUBKEY}; +use openzeppelin::tests::utils::constants::TRANSACTION_HASH; +use openzeppelin::tests::utils::constants::stark::{KEY_PAIR, KEY_PAIR_2}; +use openzeppelin::tests::utils::signing::{StarkKeyPair, StarkKeyPairExt}; use openzeppelin::tests::utils; -use starknet::testing; +use snforge_std::{declare, start_cheat_caller_address}; + +use super::common::get_accept_ownership_signature; // // Setup // -fn setup_snake() -> (DualCaseAccount, AccountABIDispatcher) { - let mut calldata = array![PUBKEY]; - let target = utils::deploy(SnakeAccountMock::TEST_CLASS_HASH, calldata); - ( - DualCaseAccount { contract_address: target }, - AccountABIDispatcher { contract_address: target } - ) +fn setup_snake(key_pair: StarkKeyPair) -> (DualCaseAccount, AccountABIDispatcher) { + let calldata = array![key_pair.public_key]; + let contract_address = utils::declare_and_deploy("SnakeAccountMock", calldata); + (DualCaseAccount { contract_address }, AccountABIDispatcher { contract_address }) } -fn setup_camel() -> (DualCaseAccount, AccountABIDispatcher) { - let mut calldata = array![PUBKEY]; - let target = utils::deploy(CamelAccountMock::TEST_CLASS_HASH, calldata); - ( - DualCaseAccount { contract_address: target }, - AccountABIDispatcher { contract_address: target } - ) +fn setup_camel(key_pair: StarkKeyPair) -> (DualCaseAccount, AccountABIDispatcher) { + let calldata = array![key_pair.public_key]; + let contract_address = utils::declare_and_deploy("CamelAccountMock", calldata); + (DualCaseAccount { contract_address }, AccountABIDispatcher { contract_address }) } fn setup_non_account() -> DualCaseAccount { let calldata = array![]; - let target = utils::deploy(NonImplementingMock::TEST_CLASS_HASH, calldata); - DualCaseAccount { contract_address: target } + let contract_address = utils::declare_and_deploy("NonImplementingMock", calldata); + DualCaseAccount { contract_address } } fn setup_account_panic() -> (DualCaseAccount, DualCaseAccount) { - let snake_target = utils::deploy(SnakeAccountPanicMock::TEST_CLASS_HASH, array![]); - let camel_target = utils::deploy(CamelAccountPanicMock::TEST_CLASS_HASH, array![]); + let snake_target = utils::declare_and_deploy("SnakeAccountPanicMock", array![]); + let camel_target = utils::declare_and_deploy("CamelAccountPanicMock", array![]); ( DualCaseAccount { contract_address: snake_target }, DualCaseAccount { contract_address: camel_target } @@ -53,38 +47,46 @@ fn setup_account_panic() -> (DualCaseAccount, DualCaseAccount) { #[test] fn test_dual_set_public_key() { - let (snake_dispatcher, target) = setup_snake(); + let key_pair = KEY_PAIR(); + let (snake_dispatcher, target) = setup_snake(key_pair); + let new_key_pair = KEY_PAIR_2(); + let signature = get_accept_ownership_signature( + snake_dispatcher.contract_address, key_pair.public_key, new_key_pair + ); + start_cheat_caller_address(target.contract_address, target.contract_address); - testing::set_contract_address(snake_dispatcher.contract_address); + snake_dispatcher.set_public_key(new_key_pair.public_key, signature); - snake_dispatcher.set_public_key(NEW_PUBKEY, get_accept_ownership_signature_snake()); - - let public_key = target.get_public_key(); - assert_eq!(public_key, NEW_PUBKEY); + assert_eq!(target.get_public_key(), new_key_pair.public_key); } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_set_public_key() { let dispatcher = setup_non_account(); - dispatcher.set_public_key(NEW_PUBKEY, array![].span()); + let new_public_key = KEY_PAIR_2().public_key; + dispatcher.set_public_key(new_public_key, array![].span()); } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_set_public_key_exists_and_panics() { - let (dispatcher, _) = setup_account_panic(); - dispatcher.set_public_key(NEW_PUBKEY, array![].span()); + let (snake_dispatcher, _) = setup_account_panic(); + let new_public_key = KEY_PAIR_2().public_key; + snake_dispatcher.set_public_key(new_public_key, array![].span()); } #[test] fn test_dual_get_public_key() { - let (snake_dispatcher, _) = setup_snake(); - let public_key = snake_dispatcher.get_public_key(); - assert_eq!(public_key, PUBKEY); + let key_pair = KEY_PAIR(); + let (snake_dispatcher, _) = setup_snake(key_pair); + let expected_public_key = key_pair.public_key; + assert_eq!(snake_dispatcher.get_public_key(), expected_public_key); } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_get_public_key() { let dispatcher = setup_non_account(); @@ -92,55 +94,51 @@ fn test_dual_no_get_public_key() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_get_public_key_exists_and_panics() { - let (dispatcher, _) = setup_account_panic(); - dispatcher.get_public_key(); + let (snake_dispatcher, _) = setup_account_panic(); + snake_dispatcher.get_public_key(); } #[test] fn test_dual_is_valid_signature() { - let (snake_dispatcher, target) = setup_snake(); - - let data = SIGNED_TX_DATA(); - let hash = data.transaction_hash; - let mut signature = array![data.r, data.s]; + let key_pair = KEY_PAIR(); + let (snake_dispatcher, _) = setup_snake(key_pair); + let tx_hash = TRANSACTION_HASH; + let serialized_signature = key_pair.serialized_sign(tx_hash); - testing::set_contract_address(snake_dispatcher.contract_address); - target.set_public_key(data.public_key, get_accept_ownership_signature_snake()); - - let is_valid = snake_dispatcher.is_valid_signature(hash, signature); + let is_valid = snake_dispatcher.is_valid_signature(tx_hash, serialized_signature); assert_eq!(is_valid, starknet::VALIDATED); } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_is_valid_signature() { - let hash = 0x0; let signature = array![]; let dispatcher = setup_non_account(); - dispatcher.is_valid_signature(hash, signature); + dispatcher.is_valid_signature(TRANSACTION_HASH, signature); } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_is_valid_signature_exists_and_panics() { - let hash = 0x0; let signature = array![]; + let (snake_dispatcher, _) = setup_account_panic(); - let (dispatcher, _) = setup_account_panic(); - dispatcher.is_valid_signature(hash, signature); + snake_dispatcher.is_valid_signature(TRANSACTION_HASH, signature); } #[test] fn test_dual_supports_interface() { - let (snake_dispatcher, _) = setup_snake(); + let (snake_dispatcher, _) = setup_snake(KEY_PAIR()); let supports_isrc5 = snake_dispatcher.supports_interface(ISRC5_ID); assert!(supports_isrc5); } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_dual_no_supports_interface() { let dispatcher = setup_non_account(); @@ -148,10 +146,10 @@ fn test_dual_no_supports_interface() { } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ("Some error",))] fn test_dual_supports_interface_exists_and_panics() { - let (dispatcher, _) = setup_account_panic(); - dispatcher.supports_interface(ISRC5_ID); + let (snake_dispatcher, _) = setup_account_panic(); + snake_dispatcher.supports_interface(ISRC5_ID); } // @@ -159,102 +157,65 @@ fn test_dual_supports_interface_exists_and_panics() { // #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_setPublicKey() { - let (camel_dispatcher, target) = setup_camel(); + let key_pair = KEY_PAIR(); + let (camel_dispatcher, target) = setup_camel(key_pair); + let new_key_pair = KEY_PAIR_2(); + let signature = get_accept_ownership_signature( + camel_dispatcher.contract_address, key_pair.public_key, new_key_pair + ); + start_cheat_caller_address(target.contract_address, target.contract_address); - testing::set_contract_address(camel_dispatcher.contract_address); - camel_dispatcher.set_public_key(NEW_PUBKEY, get_accept_ownership_signature_camel()); + camel_dispatcher.set_public_key(new_key_pair.public_key, signature); - let public_key = target.getPublicKey(); - assert_eq!(public_key, NEW_PUBKEY); + assert_eq!(target.getPublicKey(), new_key_pair.public_key); } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ("Some error",))] fn test_dual_setPublicKey_exists_and_panics() { - let (_, dispatcher) = setup_account_panic(); - dispatcher.set_public_key(NEW_PUBKEY, array![].span()); + let (_, camel_dispatcher) = setup_account_panic(); + let new_public_key = KEY_PAIR_2().public_key; + camel_dispatcher.set_public_key(new_public_key, array![].span()); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_getPublicKey() { - let (camel_dispatcher, _) = setup_camel(); - let public_key = camel_dispatcher.get_public_key(); - assert_eq!(public_key, PUBKEY); + let key_pair = KEY_PAIR(); + let (camel_dispatcher, _) = setup_camel(key_pair); + let expected_public_key = key_pair.public_key; + assert_eq!(camel_dispatcher.get_public_key(), expected_public_key); } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ("Some error",))] fn test_dual_getPublicKey_exists_and_panics() { - let (_, dispatcher) = setup_account_panic(); - dispatcher.get_public_key(); + let (_, camel_dispatcher) = setup_account_panic(); + camel_dispatcher.get_public_key(); } #[test] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. fn test_dual_isValidSignature() { - let (camel_dispatcher, target) = setup_camel(); - - let data = SIGNED_TX_DATA(); - let hash = data.transaction_hash; - let signature = array![data.r, data.s]; - - testing::set_contract_address(camel_dispatcher.contract_address); - target.setPublicKey(data.public_key, get_accept_ownership_signature_camel()); + let key_pair = KEY_PAIR(); + let (camel_dispatcher, _) = setup_camel(key_pair); + let tx_hash = TRANSACTION_HASH; + let serialized_signature = key_pair.serialized_sign(tx_hash); - let is_valid = camel_dispatcher.is_valid_signature(hash, signature); + let is_valid = camel_dispatcher.is_valid_signature(tx_hash, serialized_signature); assert_eq!(is_valid, starknet::VALIDATED); } #[test] -#[should_panic(expected: ("Some error", 'ENTRYPOINT_FAILED',))] +#[ignore] // REASON: foundry entrypoint_not_found error message inconsistent with mainnet. +#[should_panic(expected: ("Some error",))] fn test_dual_isValidSignature_exists_and_panics() { - let hash = 0x0; let signature = array![]; - let (_, dispatcher) = setup_account_panic(); - dispatcher.is_valid_signature(hash, signature); -} - -// -// Helpers -// - -fn get_accept_ownership_signature_snake() -> Span { - // 0x26a14ae81fad8adcb81337e49fd68ac44d508cfca09c4a6167c71e85759e98d = - // PoseidonTrait::new() - // .update_with('StarkNet Message') - // .update_with('accept_ownership') - // .update_with(snake_dispatcher.contract_address) - // .update_with(PUBKEY) - // .finalize(); - - // This signature was computed using starknet js sdk from the following values: - // - private_key: '1234' - // - public_key: 0x26da8d11938b76025862be14fdb8b28438827f73e75e86f7bfa38b196951fa7 - // - msg_hash: 0x26a14ae81fad8adcb81337e49fd68ac44d508cfca09c4a6167c71e85759e98d - array![ - 0x14de5d99a3a43bc7e8e0ddf8ff72fab172798ac3dd7bd858d4f6f489a2a4bcb, - 0x88dd5cb7f27a8932a5c6d226b486830e952e3d9e78996e7ca2315afa9c0be2 - ] - .span() -} - -fn get_accept_ownership_signature_camel() -> Span { - // 0x1e2acd787c2778bebfbf4d8770fdc0834db1576b5fd190f35afae5a6f7f469f = - // PoseidonTrait::new() - // .update_with('StarkNet Message') - // .update_with('accept_ownership') - // .update_with(camel_dispatcher.contract_address) - // .update_with(PUBKEY) - // .finalize(); - - // This signature was computed using starknet js sdk from the following values: - // - private_key: '1234' - // - public_key: 0x26da8d11938b76025862be14fdb8b28438827f73e75e86f7bfa38b196951fa7 - // - msg_hash: 0x1e2acd787c2778bebfbf4d8770fdc0834db1576b5fd190f35afae5a6f7f469f - array![ - 0xd0eab69db72a46ee0fb39c833cfaec035ca809eb884285d0e603262f15e0dd, - 0x768e984a96d5f731c3cf1660c5a00f508f3d99d8240bf3ba4c3e69561a545d1 - ] - .span() + let (_, camel_dispatcher) = setup_account_panic(); + camel_dispatcher.is_valid_signature(TRANSACTION_HASH, signature); } diff --git a/src/tests/account/test_signature.cairo b/src/tests/account/test_signature.cairo index 7da6fe741..548afb817 100644 --- a/src/tests/account/test_signature.cairo +++ b/src/tests/account/test_signature.cairo @@ -1,5 +1,5 @@ use openzeppelin::account::utils::signature::{is_valid_stark_signature, is_valid_eth_signature}; -use openzeppelin::tests::utils::constants::secp256k1::KEY_PAIR; +use openzeppelin::tests::utils::constants::{stark, secp256k1}; use starknet::secp256_trait::Secp256Trait; use starknet::secp256k1::Secp256k1Point; @@ -12,34 +12,31 @@ use super::starknet::common::SIGNED_TX_DATA as stark_signature_data; #[test] fn test_is_valid_stark_signature_good_sig() { - let data = stark_signature_data(); - let hash = data.tx_hash; + let key_pair = stark::KEY_PAIR(); + let data = stark_signature_data(key_pair); + let good_signature = array![data.r, data.s].span(); - let mut good_signature = array![data.r, data.s].span(); - - let is_valid = is_valid_stark_signature(hash, data.public_key, good_signature); + let is_valid = is_valid_stark_signature(data.tx_hash, key_pair.public_key, good_signature); assert!(is_valid); } #[test] fn test_is_valid_stark_signature_bad_sig() { - let data = stark_signature_data(); - let hash = data.tx_hash; - - let mut bad_signature = array![0x987, 0x564].span(); + let key_pair = stark::KEY_PAIR(); + let data = stark_signature_data(key_pair); + let bad_signature = array!['BAD', 'SIGNATURE'].span(); - let is_invalid = !is_valid_stark_signature(hash, data.public_key, bad_signature); + let is_invalid = !is_valid_stark_signature(data.tx_hash, key_pair.public_key, bad_signature); assert!(is_invalid); } #[test] fn test_is_valid_stark_signature_invalid_len_sig() { - let data = stark_signature_data(); - let hash = data.tx_hash; - - let mut bad_signature = array![0x987].span(); + let key_pair = stark::KEY_PAIR(); + let data = stark_signature_data(key_pair); + let bad_signature = array!['BAD_SIGNATURE'].span(); - let is_invalid = !is_valid_stark_signature(hash, data.public_key, bad_signature); + let is_invalid = !is_valid_stark_signature(data.tx_hash, key_pair.public_key, bad_signature); assert!(is_invalid); } @@ -49,21 +46,20 @@ fn test_is_valid_stark_signature_invalid_len_sig() { #[test] fn test_is_valid_eth_signature_good_sig() { - let data = eth_signature_data(KEY_PAIR()); - let hash = data.tx_hash; + let data = eth_signature_data(secp256k1::KEY_PAIR()); let mut serialized_good_signature = array![]; - data.signature.serialize(ref serialized_good_signature); - let is_valid = is_valid_eth_signature(hash, data.public_key, serialized_good_signature.span()); + let is_valid = is_valid_eth_signature( + data.tx_hash, data.public_key, serialized_good_signature.span() + ); assert!(is_valid); } #[test] fn test_is_valid_eth_signature_bad_sig() { - let data = eth_signature_data(KEY_PAIR()); - let hash = data.tx_hash; + let data = eth_signature_data(secp256k1::KEY_PAIR()); let mut bad_signature = data.signature; bad_signature.r += 1; @@ -73,7 +69,7 @@ fn test_is_valid_eth_signature_bad_sig() { bad_signature.serialize(ref serialized_bad_signature); let is_invalid = !is_valid_eth_signature( - hash, data.public_key, serialized_bad_signature.span() + data.tx_hash, data.public_key, serialized_bad_signature.span() ); assert!(is_invalid); } @@ -81,18 +77,15 @@ fn test_is_valid_eth_signature_bad_sig() { #[test] #[should_panic(expected: ('Signature: Invalid format.',))] fn test_is_valid_eth_signature_invalid_format_sig() { - let data = eth_signature_data(KEY_PAIR()); - let hash = data.tx_hash; - + let data = eth_signature_data(secp256k1::KEY_PAIR()); let mut serialized_bad_signature = array![0x1]; - is_valid_eth_signature(hash, data.public_key, serialized_bad_signature.span()); + is_valid_eth_signature(data.tx_hash, data.public_key, serialized_bad_signature.span()); } #[test] fn test_signature_r_out_of_range() { - let data = eth_signature_data(KEY_PAIR()); - let hash = data.tx_hash; + let data = eth_signature_data(secp256k1::KEY_PAIR()); let mut bad_signature = data.signature; let curve_size = Secp256Trait::::get_curve_size(); @@ -104,15 +97,14 @@ fn test_signature_r_out_of_range() { bad_signature.serialize(ref serialized_bad_signature); let is_invalid = !is_valid_eth_signature( - hash, data.public_key, serialized_bad_signature.span() + data.tx_hash, data.public_key, serialized_bad_signature.span() ); assert!(is_invalid); } #[test] fn test_signature_s_out_of_range() { - let data = eth_signature_data(KEY_PAIR()); - let hash = data.tx_hash; + let data = eth_signature_data(secp256k1::KEY_PAIR()); let mut bad_signature = data.signature; let curve_size = Secp256Trait::::get_curve_size(); @@ -124,7 +116,7 @@ fn test_signature_s_out_of_range() { bad_signature.serialize(ref serialized_bad_signature); let is_invalid = !is_valid_eth_signature( - hash, data.public_key, serialized_bad_signature.span() + data.tx_hash, data.public_key, serialized_bad_signature.span() ); assert!(is_invalid); } diff --git a/src/tests/utils/constants.cairo b/src/tests/utils/constants.cairo index 2259ef968..4d8df0911 100644 --- a/src/tests/utils/constants.cairo +++ b/src/tests/utils/constants.cairo @@ -124,13 +124,25 @@ pub(crate) fn EMPTY_DATA() -> Span { pub(crate) mod secp256k1 { use openzeppelin::tests::utils::signing::{Secp256k1KeyPair, get_secp256k1_keys_from}; - pub(crate) const PRIVATE_KEY: u256 = u256 { low: 'PRIVATE_LOW', high: 'PRIVATE_HIGH' }; pub(crate) fn KEY_PAIR() -> Secp256k1KeyPair { - get_secp256k1_keys_from(PRIVATE_KEY) + let private_key = u256 { low: 'PRIVATE_LOW', high: 'PRIVATE_HIGH' }; + get_secp256k1_keys_from(private_key) } - pub(crate) const PRIVATE_KEY_2: u256 = u256 { low: 'PRIVATE_LOW_2', high: 'PRIVATE_HIGH_2' }; pub(crate) fn KEY_PAIR_2() -> Secp256k1KeyPair { - get_secp256k1_keys_from(PRIVATE_KEY_2) + let private_key = u256 { low: 'PRIVATE_LOW_2', high: 'PRIVATE_HIGH_2' }; + get_secp256k1_keys_from(private_key) + } +} + +pub(crate) mod stark { + use openzeppelin::tests::utils::signing::{StarkKeyPair, get_stark_keys_from}; + + pub(crate) fn KEY_PAIR() -> StarkKeyPair { + get_stark_keys_from('PRIVATE_KEY') + } + + pub(crate) fn KEY_PAIR_2() -> StarkKeyPair { + get_stark_keys_from('PRIVATE_KEY_2') } } diff --git a/src/tests/utils/signing.cairo b/src/tests/utils/signing.cairo index ee596c8d0..8b1e7ca44 100644 --- a/src/tests/utils/signing.cairo +++ b/src/tests/utils/signing.cairo @@ -14,6 +14,14 @@ pub fn get_secp256k1_keys_from(private_key: u256) -> Secp256k1KeyPair { Secp256k1CurveKeyPairImpl::from_secret_key(private_key) } +#[generate_trait] +pub impl StarkKeyPairExt of StarkKeyPairExtTrait { + fn serialized_sign(self: StarkKeyPair, msg: felt252) -> Array { + let (r, s) = self.sign(msg).unwrap(); + array![r, s] + } +} + #[generate_trait] pub impl Secp256k1KeyPairExt of Secp256k1KeyPairExtTrait { fn serialized_sign(self: Secp256k1KeyPair, msg: u256) -> Array { From 2e0f3c641feb28796272990c4247ba668296b809 Mon Sep 17 00:00:00 2001 From: immrsd <103599616+immrsd@users.noreply.github.com> Date: Mon, 29 Jul 2024 20:19:01 +0200 Subject: [PATCH 18/20] Migrate Starknet Account Preset tests (#1069) * Update test utils and helpers * Update signature tests * Update account tests * Update dual account tests * Run linter * Run linter * Fix review issues * Update ignore reason messages * Run linter * Support eth account tests changes * Run linter * Improve setup functions, remove unused imports * Remove unnecessary accept_ownership step, make use of serialized_sign fn * Migrate Starknet Account Preset tests, make some code improvements * Run linter * Remove unnecessary clone * Update the comment for assert_entrypoint_not_found_error function * Minor review fixes --- .../account/ethereum/test_eth_account.cairo | 2 +- src/tests/account/starknet/test_account.cairo | 2 +- src/tests/presets.cairo | 2 +- src/tests/presets/test_account.cairo | 442 ++++++++++-------- src/tests/presets/test_erc1155.cairo | 6 +- src/tests/presets/test_erc20.cairo | 6 +- src/tests/presets/test_eth_account.cairo | 6 +- src/tests/utils/common.cairo | 32 +- src/tests/utils/deployment.cairo | 14 +- 9 files changed, 275 insertions(+), 237 deletions(-) diff --git a/src/tests/account/ethereum/test_eth_account.cairo b/src/tests/account/ethereum/test_eth_account.cairo index a239de7f6..1f26956e0 100644 --- a/src/tests/account/ethereum/test_eth_account.cairo +++ b/src/tests/account/ethereum/test_eth_account.cairo @@ -316,7 +316,7 @@ fn test_multicall() { to: erc20.contract_address, selector: selectors::transfer, calldata: calldata2.span() }; - // Bundle calls and exeute + // Bundle calls and execute calls.append(call1); calls.append(call2); let ret = account.__execute__(calls); diff --git a/src/tests/account/starknet/test_account.cairo b/src/tests/account/starknet/test_account.cairo index 13dd7f36e..7bfa8e377 100644 --- a/src/tests/account/starknet/test_account.cairo +++ b/src/tests/account/starknet/test_account.cairo @@ -308,7 +308,7 @@ fn test_multicall() { to: erc20.contract_address, selector: selectors::transfer, calldata: calldata2.span() }; - // Bundle calls and exeute + // Bundle calls and execute let calls = array![call1, call2]; let ret = account.__execute__(calls); diff --git a/src/tests/presets.cairo b/src/tests/presets.cairo index 5edfe8b18..87e7cb2f4 100644 --- a/src/tests/presets.cairo +++ b/src/tests/presets.cairo @@ -1,4 +1,4 @@ -// mod test_account; +mod test_account; mod test_erc1155; mod test_erc20; mod test_erc721; diff --git a/src/tests/presets/test_account.cairo b/src/tests/presets/test_account.cairo index 61dc1c803..3aaf59543 100644 --- a/src/tests/presets/test_account.cairo +++ b/src/tests/presets/test_account.cairo @@ -1,68 +1,65 @@ use core::num::traits::Zero; -use openzeppelin::account::AccountComponent::{OwnerAdded, OwnerRemoved}; use openzeppelin::account::interface::ISRC6_ID; use openzeppelin::introspection::interface::ISRC5_ID; use openzeppelin::presets::AccountUpgradeable; +use openzeppelin::presets::interfaces::account::{ + AccountUpgradeableABISafeDispatcher, AccountUpgradeableABISafeDispatcherTrait +}; use openzeppelin::presets::interfaces::{ AccountUpgradeableABIDispatcher, AccountUpgradeableABIDispatcherTrait }; use openzeppelin::tests::account::starknet::common::{ - assert_only_event_owner_added, assert_event_owner_removed -}; -use openzeppelin::tests::account::starknet::common::{ - deploy_erc20, SIGNED_TX_DATA, SignedTransactionData + get_accept_ownership_signature, deploy_erc20, SIGNED_TX_DATA, }; -use openzeppelin::tests::mocks::account_mocks::SnakeAccountMock; -use openzeppelin::tests::upgrades::common::assert_only_event_upgraded; +use openzeppelin::tests::account::starknet::common::{AccountSpyHelpers, SignedTransactionData}; +use openzeppelin::tests::upgrades::common::UpgradeableSpyHelpers; +use openzeppelin::tests::utils::constants::stark::{KEY_PAIR, KEY_PAIR_2}; use openzeppelin::tests::utils::constants::{ - PUBKEY, NEW_PUBKEY, SALT, ZERO, CALLER, RECIPIENT, OTHER, QUERY_OFFSET, QUERY_VERSION, - MIN_TRANSACTION_VERSION, CLASS_HASH_ZERO + SALT, QUERY_OFFSET, QUERY_VERSION, MIN_TRANSACTION_VERSION }; +use openzeppelin::tests::utils::constants::{ZERO, CALLER, RECIPIENT, OTHER, CLASS_HASH_ZERO}; +use openzeppelin::tests::utils::signing::{StarkKeyPair, StarkKeyPairExt}; use openzeppelin::tests::utils; -use openzeppelin::token::erc20::interface::{IERC20DispatcherTrait, IERC20Dispatcher}; +use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::{ + cheat_signature_global, cheat_transaction_version_global, cheat_transaction_hash_global +}; +use snforge_std::{spy_events, test_address, start_cheat_caller_address}; use starknet::account::Call; -use starknet::testing; use starknet::{ContractAddress, ClassHash}; -fn CLASS_HASH() -> felt252 { - AccountUpgradeable::TEST_CLASS_HASH -} - -fn V2_CLASS_HASH() -> ClassHash { - SnakeAccountMock::TEST_CLASS_HASH.try_into().unwrap() -} - // // Setup // -fn setup_dispatcher() -> AccountUpgradeableABIDispatcher { - let calldata = array![PUBKEY]; - let target = utils::deploy(CLASS_HASH(), calldata); - utils::drop_event(target); +fn declare_v2_class() -> ClassHash { + utils::declare_class("SnakeAccountMock").class_hash +} + +fn setup_dispatcher(key_pair: StarkKeyPair) -> (ContractAddress, AccountUpgradeableABIDispatcher) { + let calldata = array![key_pair.public_key]; + let account_address = utils::declare_and_deploy("AccountUpgradeable", calldata); + let dispatcher = AccountUpgradeableABIDispatcher { contract_address: account_address }; - AccountUpgradeableABIDispatcher { contract_address: target } + (account_address, dispatcher) } fn setup_dispatcher_with_data( - data: Option<@SignedTransactionData> -) -> AccountUpgradeableABIDispatcher { - testing::set_version(MIN_TRANSACTION_VERSION); + key_pair: StarkKeyPair, data: SignedTransactionData +) -> (AccountUpgradeableABIDispatcher, felt252) { + let account_class = utils::declare_class("AccountUpgradeable"); + let calldata = array![key_pair.public_key]; + let contract_address = utils::deploy(account_class, calldata); + let account_dispatcher = AccountUpgradeableABIDispatcher { contract_address }; - let mut calldata = array![]; - if data.is_some() { - let data = data.unwrap(); - testing::set_signature(array![*data.r, *data.s].span()); - testing::set_transaction_hash(*data.transaction_hash); - - calldata.append(*data.public_key); - } else { - calldata.append(PUBKEY); - } - let address = utils::deploy(CLASS_HASH(), calldata); - AccountUpgradeableABIDispatcher { contract_address: address } + cheat_signature_global(array![data.r, data.s].span()); + cheat_transaction_hash_global(data.tx_hash); + cheat_transaction_version_global(MIN_TRANSACTION_VERSION); + start_cheat_caller_address(contract_address, ZERO()); + + (account_dispatcher, account_class.class_hash.into()) } // @@ -72,12 +69,15 @@ fn setup_dispatcher_with_data( #[test] fn test_constructor() { let mut state = AccountUpgradeable::contract_state_for_testing(); - AccountUpgradeable::constructor(ref state, PUBKEY); + let mut spy = spy_events(); + let key_pair = KEY_PAIR(); + let account_address = test_address(); + AccountUpgradeable::constructor(ref state, key_pair.public_key); - assert_only_event_owner_added(ZERO(), PUBKEY); + spy.assert_only_event_owner_added(account_address, key_pair.public_key); let public_key = AccountUpgradeable::AccountMixinImpl::get_public_key(@state); - assert_eq!(public_key, PUBKEY); + assert_eq!(public_key, key_pair.public_key); let supports_isrc5 = AccountUpgradeable::AccountMixinImpl::supports_interface(@state, ISRC5_ID); assert!(supports_isrc5); @@ -92,44 +92,66 @@ fn test_constructor() { #[test] fn test_public_key_setter_and_getter() { - let dispatcher = setup_dispatcher(); + let key_pair = KEY_PAIR(); + let (account_address, dispatcher) = setup_dispatcher(key_pair); + let mut spy = spy_events(); - testing::set_contract_address(dispatcher.contract_address); + let new_key_pair = KEY_PAIR_2(); + let signature = get_accept_ownership_signature( + account_address, key_pair.public_key, new_key_pair + ); + start_cheat_caller_address(account_address, account_address); + dispatcher.set_public_key(new_key_pair.public_key, signature); - dispatcher.set_public_key(NEW_PUBKEY, get_accept_ownership_signature()); - let public_key = dispatcher.get_public_key(); - assert_eq!(public_key, NEW_PUBKEY); + assert_eq!(dispatcher.get_public_key(), new_key_pair.public_key); - assert_event_owner_removed(dispatcher.contract_address, PUBKEY); - assert_only_event_owner_added(dispatcher.contract_address, NEW_PUBKEY); + spy.assert_event_owner_removed(dispatcher.contract_address, key_pair.public_key); + spy.assert_only_event_owner_added(dispatcher.contract_address, new_key_pair.public_key); } #[test] fn test_public_key_setter_and_getter_camel() { - let dispatcher = setup_dispatcher(); + let key_pair = KEY_PAIR(); + let (account_address, dispatcher) = setup_dispatcher(key_pair); + let mut spy = spy_events(); - testing::set_contract_address(dispatcher.contract_address); + let new_key_pair = KEY_PAIR_2(); + let signature = get_accept_ownership_signature( + account_address, key_pair.public_key, new_key_pair + ); + start_cheat_caller_address(account_address, account_address); + dispatcher.setPublicKey(new_key_pair.public_key, signature); - dispatcher.setPublicKey(NEW_PUBKEY, get_accept_ownership_signature()); - let public_key = dispatcher.getPublicKey(); - assert_eq!(public_key, NEW_PUBKEY); + assert_eq!(dispatcher.getPublicKey(), new_key_pair.public_key); - assert_event_owner_removed(dispatcher.contract_address, PUBKEY); - assert_only_event_owner_added(dispatcher.contract_address, NEW_PUBKEY); + spy.assert_event_owner_removed(dispatcher.contract_address, key_pair.public_key); + spy.assert_only_event_owner_added(dispatcher.contract_address, new_key_pair.public_key); } #[test] -#[should_panic(expected: ('Account: unauthorized', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: unauthorized',))] fn test_set_public_key_different_account() { - let dispatcher = setup_dispatcher(); - dispatcher.set_public_key(NEW_PUBKEY, get_accept_ownership_signature()); + let key_pair = KEY_PAIR(); + let (account_address, dispatcher) = setup_dispatcher(key_pair); + + let new_key_pair = KEY_PAIR_2(); + let signature = get_accept_ownership_signature( + account_address, key_pair.public_key, new_key_pair + ); + dispatcher.set_public_key(new_key_pair.public_key, signature); } #[test] -#[should_panic(expected: ('Account: unauthorized', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: unauthorized',))] fn test_setPublicKey_different_account() { - let dispatcher = setup_dispatcher(); - dispatcher.setPublicKey(NEW_PUBKEY, get_accept_ownership_signature()); + let key_pair = KEY_PAIR(); + let (account_address, dispatcher) = setup_dispatcher(key_pair); + + let new_key_pair = KEY_PAIR_2(); + let signature = get_accept_ownership_signature( + account_address, key_pair.public_key, new_key_pair + ); + dispatcher.setPublicKey(new_key_pair.public_key, signature); } // @@ -137,16 +159,12 @@ fn test_setPublicKey_different_account() { // fn is_valid_sig_dispatcher() -> (AccountUpgradeableABIDispatcher, felt252, Array) { - let dispatcher = setup_dispatcher(); - - let data = SIGNED_TX_DATA(); - let hash = data.transaction_hash; - let mut signature = array![data.r, data.s]; - - testing::set_contract_address(dispatcher.contract_address); - dispatcher.set_public_key(data.public_key, get_accept_ownership_signature()); + let key_pair = KEY_PAIR(); + let (_, dispatcher) = setup_dispatcher(key_pair); - (dispatcher, hash, signature) + let data = SIGNED_TX_DATA(key_pair); + let signature = array![data.r, data.s]; + (dispatcher, data.tx_hash, signature) } #[test] @@ -159,44 +177,64 @@ fn test_is_valid_signature() { #[test] fn test_is_valid_signature_bad_sig() { - let (dispatcher, hash, _) = is_valid_sig_dispatcher(); + let (dispatcher, tx_hash, _) = is_valid_sig_dispatcher(); + let bad_signature = array!['BAD', 'SIG']; - let bad_signature = array![0x987, 0x564]; - - let is_valid = dispatcher.is_valid_signature(hash, bad_signature.clone()); + let is_valid = dispatcher.is_valid_signature(tx_hash, bad_signature); assert!(is_valid.is_zero(), "Should reject invalid signature"); } +#[test] +fn test_is_valid_signature_invalid_len_sig() { + let (dispatcher, tx_hash, _) = is_valid_sig_dispatcher(); + let invalid_len_sig = array!['INVALID_LEN']; + + let is_valid = dispatcher.is_valid_signature(tx_hash, invalid_len_sig); + assert!(is_valid.is_zero(), "Should reject signature of invalid length"); +} + #[test] fn test_isValidSignature() { - let (dispatcher, hash, signature) = is_valid_sig_dispatcher(); + let (dispatcher, tx_hash, signature) = is_valid_sig_dispatcher(); - let is_valid = dispatcher.isValidSignature(hash, signature); + let is_valid = dispatcher.isValidSignature(tx_hash, signature); assert_eq!(is_valid, starknet::VALIDATED); } #[test] fn test_isValidSignature_bad_sig() { - let (dispatcher, hash, _) = is_valid_sig_dispatcher(); + let (dispatcher, tx_hash, _) = is_valid_sig_dispatcher(); + let bad_signature = array!['BAD', 'SIG']; - let bad_signature = array![0x987, 0x564]; - - let is_valid = dispatcher.isValidSignature(hash, bad_signature); + let is_valid = dispatcher.isValidSignature(tx_hash, bad_signature); assert!(is_valid.is_zero(), "Should reject invalid signature"); } +#[test] +fn test_isValidSignature_invalid_len_sig() { + let (dispatcher, tx_hash, _) = is_valid_sig_dispatcher(); + let invalid_len_sig = array!['INVALID_LEN']; + + let is_valid = dispatcher.isValidSignature(tx_hash, invalid_len_sig); + assert!(is_valid.is_zero(), "Should reject signature of invalid length"); +} + // // supports_interface // #[test] fn test_supports_interface() { - let dispatcher = setup_dispatcher(); + let key_pair = KEY_PAIR(); + let (_, dispatcher) = setup_dispatcher(key_pair); + let supports_isrc5 = dispatcher.supports_interface(ISRC5_ID); assert!(supports_isrc5); + let supports_isrc6 = dispatcher.supports_interface(ISRC6_ID); assert!(supports_isrc6); - let doesnt_support_0x123 = !dispatcher.supports_interface(0x123); + + let doesnt_support_0x123 = !dispatcher.supports_interface('DUMMY_INTERFACE_ID'); assert!(doesnt_support_0x123); } @@ -206,110 +244,120 @@ fn test_supports_interface() { #[test] fn test_validate_deploy() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); + let key_pair = KEY_PAIR(); + let (account, class_hash) = setup_dispatcher_with_data(key_pair, SIGNED_TX_DATA(key_pair)); // `__validate_deploy__` does not directly use the passed arguments. Their // values are already integrated in the tx hash. The passed arguments in this // testing context are decoupled from the signature and have no effect on the test. - let is_valid = account.__validate_deploy__(CLASS_HASH(), SALT, PUBKEY); + let is_valid = account.__validate_deploy__(class_hash, SALT, key_pair.public_key); assert_eq!(is_valid, starknet::VALIDATED); } #[test] -#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid signature',))] fn test_validate_deploy_invalid_signature_data() { - let mut data = SIGNED_TX_DATA(); - data.transaction_hash += 1; - let account = setup_dispatcher_with_data(Option::Some(@data)); + let key_pair = KEY_PAIR(); + let mut data = SIGNED_TX_DATA(key_pair); + data.tx_hash += 1; + let (account, class_hash) = setup_dispatcher_with_data(key_pair, data); - account.__validate_deploy__(CLASS_HASH(), SALT, PUBKEY); + account.__validate_deploy__(class_hash, SALT, key_pair.public_key); } #[test] -#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid signature',))] fn test_validate_deploy_invalid_signature_length() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); - let mut signature = array![0x1]; + let key_pair = KEY_PAIR(); + let (account, class_hash) = setup_dispatcher_with_data(key_pair, SIGNED_TX_DATA(key_pair)); - testing::set_signature(signature.span()); + let invalid_len_sig = array!['INVALID_LEN']; + cheat_signature_global(invalid_len_sig.span()); - account.__validate_deploy__(CLASS_HASH(), SALT, PUBKEY); + account.__validate_deploy__(class_hash, SALT, key_pair.public_key); } #[test] -#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid signature',))] fn test_validate_deploy_empty_signature() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); + let key_pair = KEY_PAIR(); + let (account, class_hash) = setup_dispatcher_with_data(key_pair, SIGNED_TX_DATA(key_pair)); + let empty_sig = array![]; + cheat_signature_global(empty_sig.span()); - testing::set_signature(empty_sig.span()); - account.__validate_deploy__(CLASS_HASH(), SALT, PUBKEY); + account.__validate_deploy__(class_hash, SALT, key_pair.public_key); } #[test] fn test_validate_declare() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); + let key_pair = KEY_PAIR(); + let (account, class_hash) = setup_dispatcher_with_data(key_pair, SIGNED_TX_DATA(key_pair)); // `__validate_declare__` does not directly use the class_hash argument. Its // value is already integrated in the tx hash. The class_hash argument in this // testing context is decoupled from the signature and has no effect on the test. - let is_valid = account.__validate_declare__(CLASS_HASH()); + let is_valid = account.__validate_declare__(class_hash); assert_eq!(is_valid, starknet::VALIDATED); } #[test] -#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid signature',))] fn test_validate_declare_invalid_signature_data() { - let mut data = SIGNED_TX_DATA(); - data.transaction_hash += 1; - let account = setup_dispatcher_with_data(Option::Some(@data)); + let key_pair = KEY_PAIR(); + let mut data = SIGNED_TX_DATA(key_pair); + data.tx_hash += 1; + let (account, class_hash) = setup_dispatcher_with_data(key_pair, data); - account.__validate_declare__(CLASS_HASH()); + account.__validate_declare__(class_hash); } #[test] -#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid signature',))] fn test_validate_declare_invalid_signature_length() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); - let mut signature = array![0x1]; + let key_pair = KEY_PAIR(); + let (account, class_hash) = setup_dispatcher_with_data(key_pair, SIGNED_TX_DATA(key_pair)); - testing::set_signature(signature.span()); + let invalid_len_sig = array!['INVALID_LEN']; + cheat_signature_global(invalid_len_sig.span()); - account.__validate_declare__(CLASS_HASH()); + account.__validate_declare__(class_hash); } #[test] -#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid signature',))] fn test_validate_declare_empty_signature() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); - let empty_sig = array![]; + let key_pair = KEY_PAIR(); + let (account, class_hash) = setup_dispatcher_with_data(key_pair, SIGNED_TX_DATA(key_pair)); - testing::set_signature(empty_sig.span()); + let empty_sig = array![]; + cheat_signature_global(empty_sig.span()); - account.__validate_declare__(CLASS_HASH()); + account.__validate_declare__(class_hash); } fn test_execute_with_version(version: Option) { - let data = SIGNED_TX_DATA(); - let account = setup_dispatcher_with_data(Option::Some(@data)); + let key_pair = KEY_PAIR(); + let data = SIGNED_TX_DATA(key_pair); + let (account, _) = setup_dispatcher_with_data(key_pair, data); let erc20 = deploy_erc20(account.contract_address, 1000); // Craft call and add to calls array let amount: u256 = 200; + let recipient = RECIPIENT(); let mut calldata = array![]; - calldata.append_serde(RECIPIENT()); + calldata.append_serde(recipient); calldata.append_serde(amount); let call = Call { to: erc20.contract_address, selector: selectors::transfer, calldata: calldata.span() }; - let mut calls = array![]; - calls.append(call); + let calls = array![call]; // Handle version for test - if version.is_some() { - testing::set_version(version.unwrap()); + if let Option::Some(version) = version { + cheat_transaction_version_global(version) } // Execute @@ -317,7 +365,7 @@ fn test_execute_with_version(version: Option) { // Assert that the transfer was successful assert_eq!(erc20.balance_of(account.contract_address), 800, "Should have remainder"); - assert_eq!(erc20.balance_of(RECIPIENT()), amount, "Should have transferred"); + assert_eq!(erc20.balance_of(recipient), amount, "Should have transferred"); // Test return value let mut call_serialized_retval = *ret.at(0); @@ -327,7 +375,7 @@ fn test_execute_with_version(version: Option) { #[test] fn test_execute() { - test_execute_with_version(Option::None(())); + test_execute_with_version(Option::None); } #[test] @@ -341,7 +389,7 @@ fn test_execute_query_version() { } #[test] -#[should_panic(expected: ('Account: invalid tx version', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid tx version',))] fn test_execute_invalid_query_version() { test_execute_with_version(Option::Some(QUERY_OFFSET)); } @@ -352,34 +400,37 @@ fn test_execute_future_query_version() { } #[test] -#[should_panic(expected: ('Account: invalid tx version', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid tx version',))] fn test_execute_invalid_version() { test_execute_with_version(Option::Some(MIN_TRANSACTION_VERSION - 1)); } #[test] fn test_validate() { - let calls = array![]; - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); + let key_pair = KEY_PAIR(); + let (account, _) = setup_dispatcher_with_data(key_pair, SIGNED_TX_DATA(key_pair)); + let calls = array![]; let is_valid = account.__validate__(calls); assert_eq!(is_valid, starknet::VALIDATED); } #[test] -#[should_panic(expected: ('Account: invalid signature', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid signature',))] fn test_validate_invalid() { - let calls = array![]; - let mut data = SIGNED_TX_DATA(); - data.transaction_hash += 1; - let account = setup_dispatcher_with_data(Option::Some(@data)); + let key_pair = KEY_PAIR(); + let mut data = SIGNED_TX_DATA(key_pair); + data.tx_hash += 1; + let (account, _) = setup_dispatcher_with_data(key_pair, data); + let calls = array![]; account.__validate__(calls); } #[test] fn test_multicall() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); + let key_pair = KEY_PAIR(); + let (account, _) = setup_dispatcher_with_data(key_pair, SIGNED_TX_DATA(key_pair)); let erc20 = deploy_erc20(account.contract_address, 1000); let recipient1 = RECIPIENT(); let recipient2 = OTHER(); @@ -403,7 +454,7 @@ fn test_multicall() { to: erc20.contract_address, selector: selectors::transfer, calldata: calldata2.span() }; - // Bundle calls and exeute + // Bundle calls and execute calls.append(call1); calls.append(call2); let ret = account.__execute__(calls); @@ -426,23 +477,23 @@ fn test_multicall() { #[test] fn test_multicall_zero_calls() { - let account = setup_dispatcher_with_data(Option::Some(@SIGNED_TX_DATA())); - let mut calls = array![]; + let key_pair = KEY_PAIR(); + let (account, _) = setup_dispatcher_with_data(key_pair, SIGNED_TX_DATA(key_pair)); + let calls = array![]; let response = account.__execute__(calls); assert!(response.is_empty()); } #[test] -#[should_panic(expected: ('Account: invalid caller', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Account: invalid caller',))] fn test_account_called_from_contract() { - let account = setup_dispatcher(); - let calls = array![]; - - testing::set_contract_address(account.contract_address); - testing::set_caller_address(CALLER()); + let key_pair = KEY_PAIR(); + let (account_address, dispatcher) = setup_dispatcher(key_pair); - account.__execute__(calls); + let calls = array![]; + start_cheat_caller_address(account_address, CALLER()); + dispatcher.__execute__(calls); } // @@ -450,89 +501,72 @@ fn test_account_called_from_contract() { // #[test] -#[should_panic(expected: ('Account: unauthorized', 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ('Account: unauthorized',))] fn test_upgrade_access_control() { - let v1 = setup_dispatcher(); - v1.upgrade(CLASS_HASH_ZERO()); + let key_pair = KEY_PAIR(); + let (_, v1_dispatcher) = setup_dispatcher(key_pair); + + v1_dispatcher.upgrade(CLASS_HASH_ZERO()); } #[test] -#[should_panic(expected: ('Class hash cannot be zero', 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ('Class hash cannot be zero',))] fn test_upgrade_with_class_hash_zero() { - let v1 = setup_dispatcher(); + let key_pair = KEY_PAIR(); + let (account_address, v1_dispatcher) = setup_dispatcher(key_pair); - set_contract_and_caller(v1.contract_address); - v1.upgrade(CLASS_HASH_ZERO()); + start_cheat_caller_address(account_address, account_address); + v1_dispatcher.upgrade(CLASS_HASH_ZERO()); } #[test] fn test_upgraded_event() { - let v1 = setup_dispatcher(); - let v2_class_hash = V2_CLASS_HASH(); + let key_pair = KEY_PAIR(); + let (account_address, v1_dispatcher) = setup_dispatcher(key_pair); + let mut spy = spy_events(); - set_contract_and_caller(v1.contract_address); - v1.upgrade(v2_class_hash); + let v2_class_hash = declare_v2_class(); + start_cheat_caller_address(account_address, account_address); + v1_dispatcher.upgrade(v2_class_hash); - assert_only_event_upgraded(v1.contract_address, v2_class_hash); + spy.assert_only_event_upgraded(account_address, v2_class_hash); } #[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +#[feature("safe_dispatcher")] fn test_v2_missing_camel_selector() { - let v1 = setup_dispatcher(); - let v2_class_hash = V2_CLASS_HASH(); + let key_pair = KEY_PAIR(); + let (account_address, v1_dispatcher) = setup_dispatcher(key_pair); + + let v2_class_hash = declare_v2_class(); + start_cheat_caller_address(account_address, account_address); + v1_dispatcher.upgrade(v2_class_hash); - set_contract_and_caller(v1.contract_address); - v1.upgrade(v2_class_hash); + let safe_dispatcher = AccountUpgradeableABISafeDispatcher { contract_address: account_address }; + let result = safe_dispatcher.getPublicKey(); - let dispatcher = AccountUpgradeableABIDispatcher { contract_address: v1.contract_address }; - dispatcher.getPublicKey(); + utils::assert_entrypoint_not_found_error(result, selector!("getPublicKey"), account_address) } #[test] fn test_state_persists_after_upgrade() { - let v1 = setup_dispatcher(); - let v2_class_hash = V2_CLASS_HASH(); + let key_pair = KEY_PAIR(); + let (account_address, v1_dispatcher) = setup_dispatcher(key_pair); - set_contract_and_caller(v1.contract_address); - let dispatcher = AccountUpgradeableABIDispatcher { contract_address: v1.contract_address }; + let new_key_pair = KEY_PAIR_2(); + let accept_ownership_sig = get_accept_ownership_signature( + account_address, key_pair.public_key, new_key_pair + ); + start_cheat_caller_address(account_address, account_address); + v1_dispatcher.set_public_key(new_key_pair.public_key, accept_ownership_sig); - dispatcher.set_public_key(NEW_PUBKEY, get_accept_ownership_signature()); + let expected_public_key = new_key_pair.public_key; + let camel_public_key = v1_dispatcher.getPublicKey(); + assert_eq!(camel_public_key, expected_public_key); - let camel_public_key = dispatcher.getPublicKey(); - assert_eq!(camel_public_key, NEW_PUBKEY); - - v1.upgrade(v2_class_hash); - let snake_public_key = dispatcher.get_public_key(); - - assert_eq!(snake_public_key, camel_public_key); -} - -// -// Helpers -// + let v2_class_hash = declare_v2_class(); + v1_dispatcher.upgrade(v2_class_hash); + let snake_public_key = v1_dispatcher.get_public_key(); -fn set_contract_and_caller(address: ContractAddress) { - testing::set_contract_address(address); - testing::set_caller_address(address); -} - -fn get_accept_ownership_signature() -> Span { - // 0xecfdac5cd0e60434b672a97ba94520b9acfe629d123a883005e45afa25ccea = - // PoseidonTrait::new() - // .update_with('StarkNet Message') - // .update_with('accept_ownership') - // .update_with(dispatcher.contract_address) - // .update_with(PUBKEY) - // .finalize(); - - // This signature was computed using starknet js sdk from the following values: - // - private_key: '1234' - // - public_key: 0x26da8d11938b76025862be14fdb8b28438827f73e75e86f7bfa38b196951fa7 - // - msg_hash: 0xecfdac5cd0e60434b672a97ba94520b9acfe629d123a883005e45afa25ccea - array![ - 0x379fcc17e39513c19b5d97143e919ec3a5d9f59d4ae80fef83e037bc9275240, - 0x7719ade3541834755ed48a4373a19872c4032937344d832e2743df677b0a43 - ] - .span() + assert_eq!(snake_public_key, expected_public_key); } diff --git a/src/tests/presets/test_erc1155.cairo b/src/tests/presets/test_erc1155.cairo index 57c597612..62ff34fbb 100644 --- a/src/tests/presets/test_erc1155.cairo +++ b/src/tests/presets/test_erc1155.cairo @@ -868,11 +868,9 @@ fn test_v2_missing_camel_selector() { v1.upgrade(v2_class_hash); let safe_dispatcher = IERC1155CamelSafeDispatcher { contract_address: v1.contract_address }; - let panic_data = safe_dispatcher.balanceOf(owner, TOKEN_ID).unwrap_err(); + let result = safe_dispatcher.balanceOf(owner, TOKEN_ID); - utils::assert_entrypoint_not_found_error( - panic_data, selector!("balanceOf"), v1.contract_address - ) + utils::assert_entrypoint_not_found_error(result, selector!("balanceOf"), v1.contract_address) } #[test] diff --git a/src/tests/presets/test_erc20.cairo b/src/tests/presets/test_erc20.cairo index 8d10d7ab8..5630256d9 100644 --- a/src/tests/presets/test_erc20.cairo +++ b/src/tests/presets/test_erc20.cairo @@ -480,11 +480,9 @@ fn test_v2_missing_camel_selector() { let safe_dispatcher = ERC20UpgradeableABISafeDispatcher { contract_address: v1.contract_address }; - let mut panic_data = safe_dispatcher.totalSupply().unwrap_err(); + let result = safe_dispatcher.totalSupply(); - utils::assert_entrypoint_not_found_error( - panic_data, selector!("totalSupply"), v1.contract_address - ) + utils::assert_entrypoint_not_found_error(result, selector!("totalSupply"), v1.contract_address) } #[test] diff --git a/src/tests/presets/test_eth_account.cairo b/src/tests/presets/test_eth_account.cairo index 81ea869ea..c30da1c7e 100644 --- a/src/tests/presets/test_eth_account.cairo +++ b/src/tests/presets/test_eth_account.cairo @@ -499,11 +499,9 @@ fn test_v2_missing_camel_selector() { let safe_dispatcher = EthAccountUpgradeableABISafeDispatcher { contract_address: v1.contract_address }; - let panic_data = safe_dispatcher.getPublicKey().unwrap_err(); + let result = safe_dispatcher.getPublicKey(); - utils::assert_entrypoint_not_found_error( - panic_data, selector!("getPublicKey"), v1.contract_address - ) + utils::assert_entrypoint_not_found_error(result, selector!("getPublicKey"), v1.contract_address) } #[test] diff --git a/src/tests/utils/common.cairo b/src/tests/utils/common.cairo index 853be448b..f453a32fb 100644 --- a/src/tests/utils/common.cairo +++ b/src/tests/utils/common.cairo @@ -1,5 +1,5 @@ use core::to_byte_array::FormatAsByteArray; -use starknet::ContractAddress; +use starknet::{ContractAddress, SyscallResult}; /// Converts panic data into a string (ByteArray). /// @@ -9,7 +9,7 @@ pub fn panic_data_to_byte_array(panic_data: Array) -> ByteArray { let mut panic_data = panic_data.span(); // Remove BYTE_ARRAY_MAGIC from the panic data. - panic_data.pop_front().unwrap(); + panic_data.pop_front().expect('Empty panic data provided'); match Serde::::deserialize(ref panic_data) { Option::Some(string) => string, @@ -36,15 +36,23 @@ pub impl IntoBase16String> of IntoBase16StringTrait { } } -/// Asserts that the panic data is an "Entrypoint not found" error, following the starknet foundry -/// emitted error format. -pub fn assert_entrypoint_not_found_error( - panic_data: Array, selector: felt252, contract_address: ContractAddress +/// Asserts that the syscall result of a call failed with an "Entrypoint not found" error, +/// following the starknet foundry emitted error format. +pub fn assert_entrypoint_not_found_error>( + result: SyscallResult, selector: felt252, contract_address: ContractAddress ) { - let expected_panic_message = format!( - "Entry point selector {} not found in contract {}", - selector.into_base_16_string(), - contract_address.into_base_16_string() - ); - assert!(panic_data_to_byte_array(panic_data) == expected_panic_message); + if let Result::Err(panic_data) = result { + let expected_panic_message = format!( + "Entry point selector {} not found in contract {}", + selector.into_base_16_string(), + contract_address.into_base_16_string() + ); + let actual_panic_message = panic_data_to_byte_array(panic_data); + assert!( + actual_panic_message == expected_panic_message, + "Got unexpected panic message: ${actual_panic_message}" + ); + } else { + panic!("${selector} call was expected to fail, but succeeded"); + } } diff --git a/src/tests/utils/deployment.cairo b/src/tests/utils/deployment.cairo index dae9a2071..2b50ab15f 100644 --- a/src/tests/utils/deployment.cairo +++ b/src/tests/utils/deployment.cairo @@ -1,7 +1,6 @@ use core::starknet::SyscallResultTrait; use openzeppelin::tests::utils::panic_data_to_byte_array; -use snforge_std::{declare, get_class_hash, ContractClass, ContractClassTrait}; -use snforge_std::{start_cheat_caller_address, stop_cheat_caller_address}; +use snforge_std::{ContractClass, ContractClassTrait}; use starknet::ContractAddress; pub fn deploy(contract_class: ContractClass, calldata: Array) -> ContractAddress { @@ -24,23 +23,26 @@ pub fn deploy_at( pub fn deploy_another_at( existing: ContractAddress, target_address: ContractAddress, calldata: Array ) { - let class_hash = get_class_hash(existing); + let class_hash = snforge_std::get_class_hash(existing); let contract_class = ContractClassTrait::new(class_hash); deploy_at(contract_class, target_address, calldata) } pub fn declare_class(contract_name: ByteArray) -> ContractClass { - declare(contract_name).unwrap_syscall() + match snforge_std::declare(contract_name) { + Result::Ok(contract_class) => contract_class, + Result::Err(panic_data) => panic!("{}", panic_data_to_byte_array(panic_data)) + } } pub fn declare_and_deploy(contract_name: ByteArray, calldata: Array) -> ContractAddress { - let contract_class = declare(contract_name).unwrap_syscall(); + let contract_class = declare_class(contract_name); deploy(contract_class, calldata) } pub fn declare_and_deploy_at( contract_name: ByteArray, target_address: ContractAddress, calldata: Array ) { - let contract_class = declare(contract_name).unwrap_syscall(); + let contract_class = declare_class(contract_name); deploy_at(contract_class, target_address, calldata) } From 6601f24d4fe8a7b51c2d166116461e2c8c11e274 Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Wed, 31 Jul 2024 05:13:41 -0400 Subject: [PATCH 19/20] Bump scarb, merge changes from main in snforge migration (#1076) * Remove unnecessary `mut`, minor fixes (#1032) * clean up code * change array syntax * fix fmt * Improve SNIP12 examples (#1036) * docs: improve examples * refactor: remove empty line * Update docs/modules/ROOT/pages/guides/snip12.adoc Co-authored-by: Andrew Fleming --------- Co-authored-by: Andrew Fleming * Release v0.15.0-rc.0 (#1039) * feat: update CHANGELOG * Bump version to 0.15.0-rc.0 * docs: add missing header * feat: apply review suggestions --------- Co-authored-by: ericnordelo * Bump cairo to 2.7.0-rc.1 (#1025) * feat: migrate modules * feat: delete cairo_project.toml * feat: update tests * feat: apply review updates * fix: remove extra line * feat: add CHANGELOG entry * fix: CHANGELOG * Bump Scarb to 2.7.0-rc.2 (#1052) * bump scarb to 2.7.0-rc.2 * add changelog entry * update changelog entry * bump scarb in installation page * Add timelock component (#996) * start timelock comp draft * move timelock to own dir, finish drafting component * tmp: add utility impls * add timelock mock * add test mod for timelock * fix commnet * add constructor to timelock mock * set min_delay in initializer * start tests * fix fmt * fix schedule assertion * add schedule tests * fix fmt * remove unused import * fix errs, _before_call * start execute tests * fix fmt * fix after_call * add execute tests * add abi interface * add reentrancy mock for timelock * add tests for cancel and update_delay * fix hash_op test * add execute with predecessor test * add timelock utils, add operation_state debug impl * fix fmt * improve imports * improve _execute * add basic mock for tests * add tmp call struct * add batch fns to interface * add batch fns * refactor tests to use dedicated mock, add batch tests * fix fmt * remove use clause * add timelock mixin * fix interface name * improve event assertions * fix execute and schedule events * fix fmt * add safe token transfer tests * fix fmt * tidy up code * add descriptions to events * clean up code * inline CallPartialEq fns * start fn descriptions * remove comments * remove comment * fix fmt * add changelog entries * improve spacing * add line break to hash test * clean up tests * clean up tests * fix constants in attacker impl * add initializer helper, register access control support * add _before_call and _after_call tests * fix reentrant batch mock call * add _schedule and _execute tests * add timelock description * fix formatting * fix comments * fix comment * fmt * tidy up tests * remove batch helper fn * remove event from mocks * Apply suggestions from code review Co-authored-by: Eric Nordelo * update spdx * remove token receiver support * add additional cancel tests * Apply suggestions from code review Co-authored-by: Eric Nordelo * initializer: remove mut, use while loop * fix fmt * add assert_only_self fn * fix getter comments re: pending/waiting * add assert_only_role * add specific op errors * make event names consistent * fix test * remove serialization from HashCallImpl * remove unused components from mock * clean up code * update to 2.7.0-rc.1 * fix fmt * import Call from corelib * update spdx * fix fmt * move OperationState, derive debug * fix fmt * fix hash impls * add for loops * fix PartialEq, add tests * fix fmt * simplify mixin fns * switch Poseidon to Pedersen * make admin a req in initializer * update tests with admin * fix fmt * fix comment * undo changes * add no admin initializer test --------- Co-authored-by: Eric Nordelo * bump scarb to 2.7.0-rc.4 (#1064) * bump scarb to 2.7.0-rc.4 * add changelog * bump scarb in installation page * feat: update workflow and fix warning (#1066) * Add actionlint (#1067) * add actionlint for workflows * remove codecov and gitmodules * bump checkout to v4, add double quotes, fmt * bump md lint * re-add cairo version * group redirects * fix link in security * add local actionlint matcher json * re-add changelog entry * add tmp usc install to ci * fix ci --------- Co-authored-by: Eric Nordelo Co-authored-by: ericnordelo Co-authored-by: JChoy --- .codecov.yml | 11 - .github/actionlint-matcher.json | 17 + .github/workflows/actionlint.yml | 15 + .github/workflows/prepare-release.yml | 16 +- .github/workflows/test.yml | 11 +- .gitmodules | 3 - CHANGELOG.md | 21 + README.md | 8 +- SECURITY.md | 2 +- Scarb.lock | 2 +- Scarb.toml | 8 +- docs/antora.yml | 2 +- docs/modules/ROOT/pages/accounts.adoc | 2 - docs/modules/ROOT/pages/api/access.adoc | 6 +- docs/modules/ROOT/pages/api/account.adoc | 10 +- docs/modules/ROOT/pages/api/erc1155.adoc | 12 +- docs/modules/ROOT/pages/api/erc20.adoc | 11 +- docs/modules/ROOT/pages/api/erc721.adoc | 12 +- docs/modules/ROOT/pages/api/governance.adoc | 2 +- .../modules/ROOT/pages/api/introspection.adoc | 4 +- docs/modules/ROOT/pages/api/security.adoc | 6 +- docs/modules/ROOT/pages/api/upgrades.adoc | 4 +- docs/modules/ROOT/pages/api/utilities.adoc | 2 +- docs/modules/ROOT/pages/guides/snip12.adoc | 22 +- docs/modules/ROOT/pages/index.adoc | 10 +- docs/modules/ROOT/pages/presets.adoc | 2 +- docs/modules/ROOT/pages/udc.adoc | 2 +- docs/modules/ROOT/pages/upgrades.adoc | 2 +- .../ROOT/pages/utils/_class_hashes.adoc | 14 +- docs/modules/ROOT/pages/wizard.adoc | 2 +- src/access/accesscontrol/accesscontrol.cairo | 9 +- .../accesscontrol/dual_accesscontrol.cairo | 2 +- src/access/accesscontrol/interface.cairo | 2 +- src/access/ownable/dual_ownable.cairo | 4 +- src/access/ownable/interface.cairo | 2 +- src/access/ownable/ownable.cairo | 2 +- src/account/account.cairo | 6 +- src/account/dual_account.cairo | 2 +- src/account/dual_eth_account.cairo | 3 +- src/account/eth_account.cairo | 8 +- src/account/interface.cairo | 3 +- src/account/utils.cairo | 4 +- src/account/utils/secp256k1.cairo | 16 +- src/account/utils/signature.cairo | 2 +- src/cairo_project.toml | 2 - src/governance.cairo | 1 + src/governance/timelock.cairo | 9 + src/governance/timelock/interface.cairo | 69 + .../timelock/timelock_controller.cairo | 659 ++++++ src/governance/timelock/utils.cairo | 1 + .../timelock/utils/call_impls.cairo | 41 + src/governance/utils/interfaces/votes.cairo | 7 +- src/introspection/interface.cairo | 2 +- src/introspection/src5.cairo | 5 +- src/presets/account.cairo | 5 +- src/presets/erc1155.cairo | 2 +- src/presets/erc20.cairo | 2 +- src/presets/erc721.cairo | 2 +- src/presets/eth_account.cairo | 3 +- src/presets/interfaces/eth_account.cairo | 1 - src/presets/universal_deployer.cairo | 5 +- src/security/initializable.cairo | 2 +- src/security/pausable.cairo | 2 +- src/security/reentrancyguard.cairo | 5 +- src/tests.cairo | 2 + src/tests/access/test_ownable.cairo | 1 - src/tests/access/test_ownable_twostep.cairo | 1 - .../ethereum/test_dual_eth_account.cairo | 4 +- .../account/ethereum/test_eth_account.cairo | 4 +- src/tests/account/test_secp256k1.cairo | 50 +- src/tests/governance.cairo | 2 + src/tests/governance/test_timelock.cairo | 1799 +++++++++++++++++ src/tests/governance/test_utils.cairo | 146 ++ src/tests/mocks.cairo | 1 + src/tests/mocks/erc721_receiver_mocks.cairo | 2 +- src/tests/mocks/eth_account_mocks.cairo | 5 - src/tests/mocks/timelock_mocks.cairo | 151 ++ src/tests/presets/test_eth_account.cairo | 4 +- src/tests/security/test_reentrancyguard.cairo | 1 - src/tests/token/erc1155/test_erc1155.cairo | 1 - src/tests/token/erc20/test_erc20_votes.cairo | 5 +- src/tests/token/erc721/test_erc721.cairo | 1 - src/token/erc1155/dual1155.cairo | 2 +- src/token/erc1155/dual1155_receiver.cairo | 2 +- src/token/erc1155/erc1155.cairo | 53 +- src/token/erc1155/erc1155_receiver.cairo | 2 +- src/token/erc1155/interface.cairo | 2 +- src/token/erc20/dual20.cairo | 4 +- src/token/erc20/erc20.cairo | 17 +- src/token/erc20/extensions/erc20_votes.cairo | 28 +- src/token/erc20/interface.cairo | 2 +- src/token/erc721/dual721.cairo | 2 +- src/token/erc721/dual721_receiver.cairo | 2 +- src/token/erc721/erc721.cairo | 78 +- src/token/erc721/erc721_receiver.cairo | 2 +- src/token/erc721/interface.cairo | 2 +- src/upgrades/interface.cairo | 2 +- src/upgrades/upgradeable.cairo | 4 +- src/utils.cairo | 2 +- src/utils/cryptography/interface.cairo | 2 +- src/utils/cryptography/nonces.cairo | 12 +- src/utils/cryptography/snip12.cairo | 2 +- src/utils/deployments.cairo | 10 +- src/utils/deployments/interface.cairo | 2 +- src/utils/math.cairo | 2 +- src/utils/selectors.cairo | 2 +- src/utils/serde.cairo | 2 +- src/utils/structs/checkpoint.cairo | 16 +- src/utils/structs/storage_array.cairo | 2 +- src/utils/unwrap_and_cast.cairo | 2 +- 110 files changed, 3238 insertions(+), 333 deletions(-) delete mode 100644 .codecov.yml create mode 100644 .github/actionlint-matcher.json create mode 100644 .github/workflows/actionlint.yml delete mode 100644 .gitmodules delete mode 100644 src/cairo_project.toml create mode 100644 src/governance/timelock.cairo create mode 100644 src/governance/timelock/interface.cairo create mode 100644 src/governance/timelock/timelock_controller.cairo create mode 100644 src/governance/timelock/utils.cairo create mode 100644 src/governance/timelock/utils/call_impls.cairo create mode 100644 src/tests/governance.cairo create mode 100644 src/tests/governance/test_timelock.cairo create mode 100644 src/tests/governance/test_utils.cairo create mode 100644 src/tests/mocks/timelock_mocks.cairo diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index 54616a49c..000000000 --- a/.codecov.yml +++ /dev/null @@ -1,11 +0,0 @@ -comment: off -github_checks: - annotations: false -coverage: - status: - patch: - default: - target: 95% - project: - default: - threshold: 1% diff --git a/.github/actionlint-matcher.json b/.github/actionlint-matcher.json new file mode 100644 index 000000000..09211db2f --- /dev/null +++ b/.github/actionlint-matcher.json @@ -0,0 +1,17 @@ +{ + "problemMatcher": [ + { + "owner": "actionlint", + "pattern": [ + { + "regexp": "^(?:\\x1b\\[\\d+m)?(.+?)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*: (?:\\x1b\\[\\d+m)*(.+?)(?:\\x1b\\[\\d+m)* \\[(.+?)\\]$", + "file": 1, + "line": 2, + "column": 3, + "message": 4, + "code": 5 + } + ] + } + ] + } diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml new file mode 100644 index 000000000..667a77682 --- /dev/null +++ b/.github/workflows/actionlint.yml @@ -0,0 +1,15 @@ +name: Lint workflows + +on: + pull_request: + paths: + - '.github/**/*.ya?ml' + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Add problem matchers + run: echo "::add-matcher::.github/actionlint-matcher.json" + - uses: docker://rhysd/actionlint:latest diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 6b0481046..23a2a7f95 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -10,23 +10,27 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Extract current versions run: | CURRENT_VERSION=$(grep '^version = ' Scarb.toml | sed 's/version = "\(.*\)"/\1/') SCARB_VERSION=$(grep 'scarb-version = ' Scarb.toml | sed 's/scarb-version = "\(.*\)"/\1/') - echo "CURRENT_VERSION=$CURRENT_VERSION" >> $GITHUB_ENV - echo "SCARB_VERSION=$SCARB_VERSION" >> $GITHUB_ENV + CAIRO_VERSION=$(grep 'cairo-version = ' Scarb.toml | sed 's/cairo-version = "\(.*\)"/\1/') + { + echo "CURRENT_VERSION=$CURRENT_VERSION" + echo "SCARB_VERSION=$SCARB_VERSION" + echo "CAIRO_VERSION=$CAIRO_VERSION" + } >> "$GITHUB_ENV" - name: Extract new version number - run: echo "NEW_VERSION=${GITHUB_REF#refs/heads/release-v}" >> $GITHUB_ENV + run: echo "NEW_VERSION=${GITHUB_REF#refs/heads/release-v}" >> "$GITHUB_ENV" - name: Replace version in files run: | echo "Current version: $CURRENT_VERSION" echo "New version: $NEW_VERSION" - ESCAPED_CURRENT_VERSION=$(echo $CURRENT_VERSION | sed 's/\./\\./g') + ESCAPED_CURRENT_VERSION="${CURRENT_VERSION//\./\\.}" find . -type f -not -path '*/\.*' -not -path './CHANGELOG.md' -not -path './docs/package-lock.json' \ -not -path './RELEASING.md' -exec sed -i "s/$ESCAPED_CURRENT_VERSION/$NEW_VERSION/g" {} + @@ -43,7 +47,7 @@ jobs: - name: Update presets page run: | - class_hash get --json | sed -e '1,4d' | python3 scripts/get_hashes_page.py $SCARB_VERSION \ + class_hash get --json | sed -e '1,4d' | python3 scripts/get_hashes_page.py "$CAIRO_VERSION" \ > ./docs/modules/ROOT/pages/utils/_class_hashes.adoc - name: Auto-commit changes diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1d37a35d6..7e7d36e0b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,28 +11,31 @@ jobs: name: Lint and test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Extract scarb version run: | SCARB_VERSION=$(grep 'scarb-version = ' Scarb.toml | sed 's/scarb-version = "\(.*\)"/\1/') - echo "SCARB_VERSION=$SCARB_VERSION" >> $GITHUB_ENV + echo "SCARB_VERSION=$SCARB_VERSION" >> "$GITHUB_ENV" - uses: software-mansion/setup-scarb@v1 with: scarb-version: ${{ env.SCARB_VERSION }} - name: Extract foundry version run: | FOUNDRY_VERSION=$(grep 'snforge_std = ' Scarb.toml | sed 's/snforge_std = .\+ tag = "v\(.*\)".*/\1/') - echo "FOUNDRY_VERSION=$FOUNDRY_VERSION" >> $GITHUB_ENV + echo "FOUNDRY_VERSION=$FOUNDRY_VERSION" >> "$GITHUB_ENV" - uses: foundry-rs/setup-snfoundry@v3 with: starknet-foundry-version: ${{ env.FOUNDRY_VERSION }} - name: Markdown lint - uses: DavidAnson/markdownlint-cli2-action@5b7c9f74fec47e6b15667b2cc23c63dff11e449e # v9 + uses: DavidAnson/markdownlint-cli2-action@b4c9feab76d8025d1e83c653fa3990936df0e6c8 # v16 with: globs: | *.md !PULL_REQUEST_TEMPLATE.md - name: Cairo lint run: scarb fmt --check + - name: Temporary USC manually install + run: | + curl -L https://raw.githubusercontent.com/software-mansion/universal-sierra-compiler/master/scripts/install.sh | sh -s -- v2.2.0-rc.1 - name: Cairo test run: snforge test diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 269eb8546..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "cairo"] - path = cairo - url = https://github.com/starkware-libs/cairo.git diff --git a/CHANGELOG.md b/CHANGELOG.md index 562be9606..9589aca28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- TimelockController component (#996) +- HashCall implementation (#996) + +### Changed + +- Bump scarb to v2.7.0-rc.1 (#1025) +- Bump scarb to v2.7.0-rc.2 (#1052) +- Bump scarb to v2.7.0-rc.4 (#1064) + +## 0.15.0-rc.0 (2024-07-8) + +### Changed + +- `Trace`, `Checkpoint`, and `StorageArray` structs made public. + +### Changed (Breaking) + +- Removed `num_checkpoints` and `checkpoints` from `ERC20VotesABI`. + ## 0.14.0 (2024-06-14) ### Changed (Breaking) diff --git a/README.md b/README.md index b5750a91e..beee9ab5a 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Edit `scarb.toml` and add: ```toml [dependencies] -openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.14.0" } +openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.15.0-rc.0" } ``` Build the project to download it: @@ -105,7 +105,7 @@ mod MyToken { ### Unsupported -[`DualCase` dispatchers](https://docs.openzeppelin.com/contracts-cairo/0.14.0/interfaces#dualcase_dispatchers) rely on Sierra's ability to catch a revert to resume execution. Currently, Starknet live chains (testnets and mainnet) don't implement that behavior. Starknet's testing framework does support it. +[`DualCase` dispatchers](https://docs.openzeppelin.com/contracts-cairo/0.15.0-rc.0/interfaces#dualcase_dispatchers) rely on Sierra's ability to catch a revert to resume execution. Currently, Starknet live chains (testnets and mainnet) don't implement that behavior. Starknet's testing framework does support it. ## Learn @@ -155,8 +155,8 @@ git clone git@github.com:OpenZeppelin/cairo-contracts.git $ cd cairo-contracts $ scarb build -Compiling lib(openzeppelin) openzeppelin v0.14.0 (~/cairo-contracts/Scarb.toml) -Compiling starknet-contract(openzeppelin) openzeppelin v0.14.0 (~/cairo-contracts/Scarb.toml) +Compiling lib(openzeppelin) openzeppelin v0.15.0-rc.0 (~/cairo-contracts/Scarb.toml) +Compiling starknet-contract(openzeppelin) openzeppelin v0.15.0-rc.0 (~/cairo-contracts/Scarb.toml) Finished release target(s) in 16 seconds ``` diff --git a/SECURITY.md b/SECURITY.md index 8bd19014b..7084b6069 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -3,4 +3,4 @@ > ⚠️ Warning! ⚠️ > This project is still in a very early and experimental phase. It has never been audited nor thoroughly reviewed for security vulnerabilities. Do not use in production. -Please report any security issues you find to security@openzeppelin.com. +Please report any security issues you find to . diff --git a/Scarb.lock b/Scarb.lock index 524489827..8e9562d62 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -3,7 +3,7 @@ version = 1 [[package]] name = "openzeppelin" -version = "0.14.0" +version = "0.15.0-rc.0" dependencies = [ "snforge_std", ] diff --git a/Scarb.toml b/Scarb.toml index 935224176..0c175288a 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -1,9 +1,9 @@ [package] name = "openzeppelin" -version = "0.14.0" +version = "0.15.0-rc.0" edition = "2023_11" -cairo-version = "2.6.4" -scarb-version = "2.6.5" +cairo-version = "2.7.0-rc.3" +scarb-version = "2.7.0-rc.4" authors = ["OpenZeppelin Community "] description = "OpenZeppelin Contracts written in Cairo for StarkNet, a decentralized ZK Rollup" documentation = "https://docs.openzeppelin.com/contracts-cairo" @@ -13,7 +13,7 @@ license-file = "LICENSE" keywords = ["openzeppelin", "starknet", "cairo", "contracts", "security", "standards"] [dependencies] -starknet = "2.6.4" +starknet = "2.7.0-rc.3" snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.26.0" } [lib] diff --git a/docs/antora.yml b/docs/antora.yml index 1e484920d..625262280 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -1,6 +1,6 @@ name: contracts-cairo title: Contracts for Cairo -version: 0.14.0 +version: 0.15.0-rc.0 nav: - modules/ROOT/nav.adoc asciidoc: diff --git a/docs/modules/ROOT/pages/accounts.adoc b/docs/modules/ROOT/pages/accounts.adoc index b6aaa2e48..bf064a01d 100644 --- a/docs/modules/ROOT/pages/accounts.adoc +++ b/docs/modules/ROOT/pages/accounts.adoc @@ -204,7 +204,6 @@ Here’s an example of a basic contract: mod MyEthAccount { use openzeppelin::account::EthAccountComponent; use openzeppelin::account::interface::EthPublicKey; - use openzeppelin::account::utils::secp256k1::Secp256k1PointSerde; use openzeppelin::introspection::src5::SRC5Component; use starknet::ClassHash; @@ -389,7 +388,6 @@ First, let's take the example account we created before and deploy it: mod MyEthAccount { use openzeppelin::account::EthAccountComponent; use openzeppelin::account::interface::EthPublicKey; - use openzeppelin::account::utils::secp256k1::Secp256k1PointSerde; use openzeppelin::introspection::src5::SRC5Component; component!(path: EthAccountComponent, storage: eth_account, event: EthAccountEvent); diff --git a/docs/modules/ROOT/pages/api/access.adoc b/docs/modules/ROOT/pages/api/access.adoc index 5338692ce..1b362e728 100644 --- a/docs/modules/ROOT/pages/api/access.adoc +++ b/docs/modules/ROOT/pages/api/access.adoc @@ -20,7 +20,7 @@ assigned each to multiple accounts. [.contract] [[OwnableComponent]] -=== `++OwnableComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/access/ownable/ownable.cairo[{github-icon},role=heading-link] +=== `++OwnableComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/access/ownable/ownable.cairo[{github-icon},role=heading-link] ```cairo use openzeppelin::access::ownable::OwnableComponent; @@ -267,7 +267,7 @@ Emitted when the ownership is transferred. [.contract] [[IAccessControl]] -=== `++IAccessControl++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/access/accesscontrol/interface.cairo[{github-icon},role=heading-link] +=== `++IAccessControl++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/access/accesscontrol/interface.cairo[{github-icon},role=heading-link] :grant_role: xref:#IAccessControl-grant_role[grant_role] :revoke_role: xref:#IAccessControl-revoke_role[revoke_role] @@ -400,7 +400,7 @@ Emitted when `account` is revoked `role`. [.contract] [[AccessControlComponent]] -=== `++AccessControlComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/access/accesscontrol/accesscontrol.cairo[{github-icon},role=heading-link] +=== `++AccessControlComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/access/accesscontrol/accesscontrol.cairo[{github-icon},role=heading-link] :assert_only_role: xref:#AccessControlComponent-assert_only_role :grant_role: xref:#AccessControlComponent-grant_role[grant_role] diff --git a/docs/modules/ROOT/pages/api/account.adoc b/docs/modules/ROOT/pages/api/account.adoc index 04cd5e9f4..c5ece578d 100644 --- a/docs/modules/ROOT/pages/api/account.adoc +++ b/docs/modules/ROOT/pages/api/account.adoc @@ -12,7 +12,7 @@ include::../utils/_common.adoc[] [.contract] [[ISRC6]] -=== `++ISRC6++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/account/interface.cairo[{github-icon},role=heading-link] +=== `++ISRC6++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/account/interface.cairo[{github-icon},role=heading-link] ```cairo use openzeppelin::account::interface::ISRC6; @@ -65,7 +65,7 @@ Returns the short string `'VALID'` if valid, otherwise it reverts. [.contract] [[AccountComponent]] -=== `++AccountComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/account/account.cairo[{github-icon},role=heading-link] +=== `++AccountComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/account/account.cairo[{github-icon},role=heading-link] :OwnerAdded: xref:AccountComponent-OwnerAdded[OwnerAdded] :OwnerRemoved: xref:AccountComponent-OwnerRemoved[OwnerRemoved] @@ -315,7 +315,7 @@ Emitted when a `public_key` is removed. [.contract] [[EthAccountComponent]] -=== `++EthAccountComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/account/eth_account.cairo[{github-icon},role=heading-link] +=== `++EthAccountComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/account/eth_account.cairo[{github-icon},role=heading-link] :OwnerAdded: xref:EthAccountComponent-OwnerAdded[OwnerAdded] :OwnerRemoved: xref:EthAccountComponent-OwnerRemoved[OwnerRemoved] @@ -570,7 +570,7 @@ Emitted when a `public_key` is removed. [.contract] [[AccountUpgradeable]] -=== `++AccountUpgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/presets/account.cairo[{github-icon},role=heading-link] +=== `++AccountUpgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/presets/account.cairo[{github-icon},role=heading-link] ```cairo use openzeppelin::presets::AccountUpgradeable; @@ -631,7 +631,7 @@ Requirements: [.contract] [[EthAccountUpgradeable]] -=== `++EthAccountUpgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/presets/eth_account.cairo[{github-icon},role=heading-link] +=== `++EthAccountUpgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/presets/eth_account.cairo[{github-icon},role=heading-link] ```cairo use openzeppelin::presets::EthAccountUpgradeable; diff --git a/docs/modules/ROOT/pages/api/erc1155.adoc b/docs/modules/ROOT/pages/api/erc1155.adoc index 029cf84e3..20846ec46 100644 --- a/docs/modules/ROOT/pages/api/erc1155.adoc +++ b/docs/modules/ROOT/pages/api/erc1155.adoc @@ -16,7 +16,7 @@ TIP: For an overview of ERC1155, read our xref:erc1155.adoc[ERC1155 guide]. [.contract] [[IERC1155]] -=== `++IERC1155++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc1155/interface.cairo[{github-icon},role=heading-link] +=== `++IERC1155++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc1155/interface.cairo[{github-icon},role=heading-link] [.hljs-theme-dark] ```cairo @@ -126,7 +126,7 @@ Emitted when the token URI is updated to `value` for the `id` token. [.contract] [[IERC1155MetadataURI]] -=== `++IERC1155MetadataURI++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc1155/interface.cairo[{github-icon},role=heading-link] +=== `++IERC1155MetadataURI++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc1155/interface.cairo[{github-icon},role=heading-link] [.hljs-theme-dark] ```cairo @@ -156,7 +156,7 @@ Returns the Uniform Resource Identifier (URI) for the `token_id` token. [.contract] [[ERC1155Component]] -=== `++ERC1155Component++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc1155/erc1155.cairo[{github-icon},role=heading-link] +=== `++ERC1155Component++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc1155/erc1155.cairo[{github-icon},role=heading-link] [.hljs-theme-dark] ```cairo @@ -543,7 +543,7 @@ See <>. [.contract] [[IERC1155Receiver]] -=== `++IERC1155Receiver++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc1155/interface.cairo[{github-icon},role=heading-link] +=== `++IERC1155Receiver++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc1155/interface.cairo[{github-icon},role=heading-link] [.hljs-theme-dark] ```cairo @@ -583,7 +583,7 @@ via <> by [.contract] [[ERC1155ReceiverComponent]] -=== `++ERC1155ReceiverComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc1155/erc1155_receiver.cairo[{github-icon},role=heading-link] +=== `++ERC1155ReceiverComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc1155/erc1155_receiver.cairo[{github-icon},role=heading-link] [.hljs-theme-dark] ```cairo @@ -660,7 +660,7 @@ Registers the `IERC1155Receiver` interface ID as supported through introspection [.contract] [[ERC1155Upgradeable]] -=== `++ERC1155Upgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/presets/erc1155.cairo[{github-icon},role=heading-link] +=== `++ERC1155Upgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/presets/erc1155.cairo[{github-icon},role=heading-link] ```cairo use openzeppelin::presets::ERC1155; diff --git a/docs/modules/ROOT/pages/api/erc20.adoc b/docs/modules/ROOT/pages/api/erc20.adoc index 48e2f7349..05fe286fd 100644 --- a/docs/modules/ROOT/pages/api/erc20.adoc +++ b/docs/modules/ROOT/pages/api/erc20.adoc @@ -16,7 +16,7 @@ TIP: For an overview of ERC20, read our {erc20-guide}. [.contract] [[IERC20]] -=== `++IERC20++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc20/interface.cairo[{github-icon},role=heading-link] +=== `++IERC20++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc20/interface.cairo[{github-icon},role=heading-link] [.hljs-theme-dark] ```cairo @@ -114,7 +114,7 @@ Emitted when the allowance of a `spender` for an `owner` is set. [.contract] [[IERC20Metadata]] -=== `++IERC20Metadata++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc20/interface.cairo#L19[{github-icon},role=heading-link] +=== `++IERC20Metadata++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc20/interface.cairo#L19[{github-icon},role=heading-link] [.hljs-theme-dark] ```cairo @@ -162,7 +162,7 @@ NOTE: This information is only used for _display_ purposes: it in no way affects [.contract] [[ERC20Component]] -=== `++ERC20Component++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc20/erc20.cairo[{github-icon},role=heading-link] +=== `++ERC20Component++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc20/erc20.cairo[{github-icon},role=heading-link] [.hljs-theme-dark] ```cairo @@ -460,7 +460,7 @@ See <>. [.contract] [[ERC20VotesComponent]] -=== `++ERC20VotesComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc20/extensions/erc20_votes.cairo[{github-icon},role=heading-link] +=== `++ERC20VotesComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc20/extensions/erc20_votes.cairo[{github-icon},role=heading-link] ```cairo use openzeppelin::token::extensions::ERC20VotesComponent; @@ -506,6 +506,7 @@ By default, token balance does not account for voting power. This makes transfer * xref:#ERC20VotesComponent-_delegate[`++_delegate(self, account, delegatee)++`] * xref:#ERC20VotesComponent-move_delegate_votes[`++move_delegate_votes(self, from, to, amount)++`] * xref:#ERC20VotesComponent-transfer_voting_units[`++transfer_voting_units(self, from, to, amount)++`] +* xref:#ERC20VotesComponent-num_checkpoints[`++num_checkpoints(self, account)++`] * xref:#ERC20VotesComponent-checkpoints[`++checkpoints(self, account, pos)++`] * xref:#ERC20VotesComponent-get_voting_units[`++get_voting_units(self, account)++`] -- @@ -654,7 +655,7 @@ Emitted when `delegate` votes are updated from `previous_votes` to `new_votes`. [.contract] [[ERC20Upgradeable]] -=== `++ERC20Upgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/presets/erc20.cairo[{github-icon},role=heading-link] +=== `++ERC20Upgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/presets/erc20.cairo[{github-icon},role=heading-link] ```cairo use openzeppelin::presets::ERC20Upgradeable; diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index ecf4b7a97..09ab75d29 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -16,7 +16,7 @@ TIP: For an overview of ERC721, read our xref:erc721.adoc[ERC721 guide]. [.contract] [[IERC721]] -=== `++IERC721++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc721/interface.cairo#L13-L31[{github-icon},role=heading-link] +=== `++IERC721++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc721/interface.cairo#L13-L31[{github-icon},role=heading-link] [.hljs-theme-dark] ```cairo @@ -135,7 +135,7 @@ Emitted when `token_id` token is transferred from `from` to `to`. [.contract] [[IERC721Metadata]] -=== `++IERC721Metadata++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc721/interface.cairo#L54-L59[{github-icon},role=heading-link] +=== `++IERC721Metadata++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc721/interface.cairo#L54-L59[{github-icon},role=heading-link] [.hljs-theme-dark] ```cairo @@ -181,7 +181,7 @@ If the URI is not set for `token_id`, the return value will be an empty `ByteArr [.contract] [[ERC721Component]] -=== `++ERC721Component++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc721/erc721.cairo#L7[{github-icon},role=heading-link] +=== `++ERC721Component++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc721/erc721.cairo#L7[{github-icon},role=heading-link] [.hljs-theme-dark] ```cairo @@ -693,7 +693,7 @@ See <>. [.contract] [[IERC721Receiver]] -=== `++IERC721Receiver++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc721/interface.cairo#L70-L79[{github-icon},role=heading-link] +=== `++IERC721Receiver++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc721/interface.cairo#L70-L79[{github-icon},role=heading-link] [.hljs-theme-dark] ```cairo @@ -724,7 +724,7 @@ Whenever an IERC721 `token_id` token is transferred to this non-account contract [.contract] [[ERC721ReceiverComponent]] -=== `++ERC721ReceiverComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/token/erc721/erc721_receiver.cairo[{github-icon},role=heading-link] +=== `++ERC721ReceiverComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/token/erc721/erc721_receiver.cairo[{github-icon},role=heading-link] [.hljs-theme-dark] ```cairo @@ -790,7 +790,7 @@ Registers the `IERC721Receiver` interface ID as supported through introspection. [.contract] [[ERC721Upgradeable]] -=== `++ERC721Upgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/presets/erc721.cairo[{github-icon},role=heading-link] +=== `++ERC721Upgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/presets/erc721.cairo[{github-icon},role=heading-link] ```cairo use openzeppelin::presets::ERC721Upgradeable; diff --git a/docs/modules/ROOT/pages/api/governance.adoc b/docs/modules/ROOT/pages/api/governance.adoc index 1d2451eb2..997815023 100644 --- a/docs/modules/ROOT/pages/api/governance.adoc +++ b/docs/modules/ROOT/pages/api/governance.adoc @@ -8,7 +8,7 @@ Reference of interfaces and utilities related to Governance. [.contract] [[IVotes]] -=== `++IVotes++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/governance/utils/interfaces/votes.cairo[{github-icon},role=heading-link] +=== `++IVotes++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/governance/utils/interfaces/votes.cairo[{github-icon},role=heading-link] [.hljs-theme-dark] ```cairo diff --git a/docs/modules/ROOT/pages/api/introspection.adoc b/docs/modules/ROOT/pages/api/introspection.adoc index c1e35dcba..77fb3b2b9 100644 --- a/docs/modules/ROOT/pages/api/introspection.adoc +++ b/docs/modules/ROOT/pages/api/introspection.adoc @@ -10,7 +10,7 @@ Reference of interfaces and utilities related to https://en.wikipedia.org/wiki/T [.contract] [[ISRC5]] -=== `++ISRC5++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/introspection/interface.cairo#L7[{github-icon},role=heading-link] +=== `++ISRC5++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/introspection/interface.cairo#L7[{github-icon},role=heading-link] ```cairo use openzeppelin::introspection::interface::ISRC5; @@ -44,7 +44,7 @@ on how to compute this ID. [.contract] [[SRC5Component]] -=== `++SRC5Component++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/introspection/src5.cairo[{github-icon},role=heading-link] +=== `++SRC5Component++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/introspection/src5.cairo[{github-icon},role=heading-link] ```cairo use openzeppelin::introspection::src5::SRC5Component; diff --git a/docs/modules/ROOT/pages/api/security.adoc b/docs/modules/ROOT/pages/api/security.adoc index 02b137467..3a6558408 100644 --- a/docs/modules/ROOT/pages/api/security.adoc +++ b/docs/modules/ROOT/pages/api/security.adoc @@ -8,7 +8,7 @@ Reference of components, interfaces and utilities found in the library's `securi [.contract] [[InitializableComponent]] -=== `++InitializableComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/security/initializable.cairo[{github-icon},role=heading-link] +=== `++InitializableComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/security/initializable.cairo[{github-icon},role=heading-link] ```cairo use openzeppelin::security::InitializableComponent; @@ -58,7 +58,7 @@ Requirements: [.contract] [[PausableComponent]] -=== `++PausableComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/security/pausable.cairo[{github-icon},role=heading-link] +=== `++PausableComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/security/pausable.cairo[{github-icon},role=heading-link] :Paused: xref:PausableComponent-Paused[Paused] :Unpaused: xref:PausableComponent-Unpaused[Unpaused] @@ -163,7 +163,7 @@ Emitted when the contract is unpaused by `account`. [.contract] [[ReentrancyGuardComponent]] -=== `++ReentrancyGuardComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/security/reentrancyguard.cairo[{github-icon},role=heading-link] +=== `++ReentrancyGuardComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/security/reentrancyguard.cairo[{github-icon},role=heading-link] ```cairo use openzeppelin::security::ReentrancyGuardComponent; diff --git a/docs/modules/ROOT/pages/api/upgrades.adoc b/docs/modules/ROOT/pages/api/upgrades.adoc index c7673fbf2..08060dd87 100644 --- a/docs/modules/ROOT/pages/api/upgrades.adoc +++ b/docs/modules/ROOT/pages/api/upgrades.adoc @@ -9,7 +9,7 @@ Reference of interfaces and utilities related to upgradeability. [.contract] [[IUpgradeable]] -=== `++IUpgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/upgrades/interface.cairo#L3[{github-icon},role=heading-link] +=== `++IUpgradeable++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/upgrades/interface.cairo#L3[{github-icon},role=heading-link] :Upgraded: xref:UpgradeableComponent-Upgraded[Upgraded] @@ -38,7 +38,7 @@ NOTE: This function is usually protected by an xref:access.adoc[Access Control] [.contract] [[UpgradeableComponent]] -=== `++UpgradeableComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/upgrades/upgradeable.cairo[{github-icon},role=heading-link] +=== `++UpgradeableComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/upgrades/upgradeable.cairo[{github-icon},role=heading-link] ```cairo use openzeppelin::upgrades::upgradeable::UpgradeableComponent; diff --git a/docs/modules/ROOT/pages/api/utilities.adoc b/docs/modules/ROOT/pages/api/utilities.adoc index 3feaa991f..d103d311d 100644 --- a/docs/modules/ROOT/pages/api/utilities.adoc +++ b/docs/modules/ROOT/pages/api/utilities.adoc @@ -261,7 +261,7 @@ NOTE: `T` is a generic value matching different numeric implementations. use openzeppelin::utils::selectors; ``` -:selectors: https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/utils/selectors.cairo[selectors.cairo] +:selectors: https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/utils/selectors.cairo[selectors.cairo] Module containing constants matching multiple selectors used through the library. To see the full list of selectors, see {selectors}. diff --git a/docs/modules/ROOT/pages/guides/snip12.adoc b/docs/modules/ROOT/pages/guides/snip12.adoc index 1ad4c134e..fb919e065 100644 --- a/docs/modules/ROOT/pages/guides/snip12.adoc +++ b/docs/modules/ROOT/pages/guides/snip12.adoc @@ -163,9 +163,25 @@ impl SNIP12MetadataImpl of SNIP12Metadata { } ---- -NOTE: These params could be set in the contract constructor, but then two storage reads would be executed every time -a message hash needs to be generated, and this is unnecessary overhead. When Starknet implements immutable storage -set in constructor, that approach will be more efficient. +In the above example, no storage reads are required which avoids unnecessary extra gas costs, but in +some cases we may need to read from storage to get the domain separator values. This can be accomplished even when +the trait is not bounded to the ContractState, like this: + +[,cairo] +---- +use openzeppelin::utils::snip12::SNIP12Metadata; + +impl SNIP12MetadataImpl of SNIP12Metadata { + fn name() -> felt252 { + let state = unsafe_new_contract_state(); + + // Some logic to get the name from storage + state.erc20.name().at(0).unwrap().into() + } + + fn version() -> felt252 { 'v1' } +} +---- === 5. Generate the hash. diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 77a1f138e..41e20d036 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -20,9 +20,9 @@ before proceeding, and run the following command to check that the installation ---- $ scarb --version -scarb 2.6.5 (d49f54394 2024-06-11) -cairo: 2.6.4 (https://crates.io/crates/cairo-lang-compiler/2.6.4) -sierra: 1.5.0 +scarb 2.7.0-rc.4 (88bf93564 2024-07-19) +cairo: 2.7.0-rc.3 (https://crates.io/crates/cairo-lang-compiler/2.7.0-rc.3) +sierra: 1.6.0 ---- === Set up your project @@ -57,7 +57,7 @@ Install the library by declaring it as a dependency in the project's `Scarb.toml [,text] ---- [dependencies] -openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.14.0" } +openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.15.0-rc.0" } ---- WARNING: Make sure the tag matches the target release. @@ -113,4 +113,4 @@ You can now compile it: [,bash] ---- scarb build ----- \ No newline at end of file +---- diff --git a/docs/modules/ROOT/pages/presets.adoc b/docs/modules/ROOT/pages/presets.adoc index f0a3f8ce3..3f56743f1 100644 --- a/docs/modules/ROOT/pages/presets.adoc +++ b/docs/modules/ROOT/pages/presets.adoc @@ -53,7 +53,7 @@ TIP: {starkli} class-hash command can be used to compute the class hash from a S :setup_project: xref:index.adoc#set_up_your_project[setting up a project] :install_lib: xref:index.adoc#install the_library[installing the Contracts for Cairo library] -:presets_dir: link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/presets[presets directory] +:presets_dir: link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/presets[presets directory] These preset contracts are ready-to-deploy which means they should already be declared on the Sepolia network. Simply deploy the preset class hash and add the appropriate constructor arguments. diff --git a/docs/modules/ROOT/pages/udc.adoc b/docs/modules/ROOT/pages/udc.adoc index d0af0dd0c..fd30ce729 100644 --- a/docs/modules/ROOT/pages/udc.adoc +++ b/docs/modules/ROOT/pages/udc.adoc @@ -56,7 +56,7 @@ fn deploy() -> ContractAddress { >(); let salt = 1234567879; let from_zero = false; - let mut calldata = array![]; + let calldata = array![]; // The UDC returns the deployed contract address dispatcher.deploy_contract(class_hash, salt, from_zero, calldata.span()) diff --git a/docs/modules/ROOT/pages/upgrades.adoc b/docs/modules/ROOT/pages/upgrades.adoc index dff92c758..3f58469e0 100644 --- a/docs/modules/ROOT/pages/upgrades.adoc +++ b/docs/modules/ROOT/pages/upgrades.adoc @@ -1,7 +1,7 @@ :contract_classes: https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/contract-classes/[Contract Classes] :class_hash: https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/class-hash/[class hash] :replace_class_syscall: https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/system-calls-cairo1/#replace_class[replace_class] -:upgradeable: https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.14.0/src/upgrades/upgradeable.cairo[Upgradeable] +:upgradeable: https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.0-rc.0/src/upgrades/upgradeable.cairo[Upgradeable] :ownable: xref:access.adoc#ownership_and_ownable[Ownable] :i_upgradeable: xref:api/upgrades.adoc#IUpgradeable[IUpgradeable] :library_calls: https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/system-calls-cairo1/#library_call[library calls] diff --git a/docs/modules/ROOT/pages/utils/_class_hashes.adoc b/docs/modules/ROOT/pages/utils/_class_hashes.adoc index 1ca009026..8c722bd41 100644 --- a/docs/modules/ROOT/pages/utils/_class_hashes.adoc +++ b/docs/modules/ROOT/pages/utils/_class_hashes.adoc @@ -1,13 +1,13 @@ // Version -:class-hash-cairo-version: https://crates.io/crates/cairo-lang-compiler/2.6.3[cairo 2.6.3] +:class-hash-cairo-version: https://crates.io/crates/cairo-lang-compiler/2.6.4[cairo 2.6.4] // Class Hashes -:ERC20Upgradeable-class-hash: 0x040b9e69e14ddc34a98ec8133c80807c144b818bc6cbf5a119d8f62535258142 -:ERC721Upgradeable-class-hash: 0x00f56255bf8db96498c71884407f3b623f20a5d507cbb7c41deeeb65a1bf8725 -:ERC1155Upgradeable-class-hash: 0x017baa69866decbb1ac5e6d5ef2e69b7d5dd7113111a8cc51a48f01854df571f -:AccountUpgradeable-class-hash: 0x00e2eb8f5672af4e6a4e8a8f1b44989685e668489b0a25437733756c5a34a1d6 -:EthAccountUpgradeable-class-hash: 0x0169e64bc8a60422da86e6cc821c498d2f8cba60888399ce4be86690aaa51e4a -:UniversalDeployer-class-hash: 0x0536e18e3e9820b48e0477e83e0a4d6d923ccddc06deb61585dfd7402dd40733 +:ERC20Upgradeable-class-hash: 0x02718db92cfa9342bd19306fb1d10cb5c61385293b4bdaac23c36577dd7ed882 +:ERC721Upgradeable-class-hash: 0x07e6fe2c473ea7018e3db87712497deae535694870d8265203212ddbdca1642d +:ERC1155Upgradeable-class-hash: 0x06cb57fea4ed34a68db38e3b2ca46b3ad39b123952b31e10e79c5ea3a1eb0a20 +:AccountUpgradeable-class-hash: 0x04343194a4a6082192502e132d9e7834b5d9bfc7a0c1dd990e95b66f85a66d46 +:EthAccountUpgradeable-class-hash: 0x02203673e728fa07de1c2ea60405399ffefaf875f1b7ae54e747659e1e216d94 +:UniversalDeployer-class-hash: 0x03a901e3f8d717544d07df0043dcdd6537c1ae4c046674599793783f73511155 // Presets page :presets-page: xref:presets.adoc[Sierra class hash] diff --git a/docs/modules/ROOT/pages/wizard.adoc b/docs/modules/ROOT/pages/wizard.adoc index c98a6b489..b18c0ddc4 100644 --- a/docs/modules/ROOT/pages/wizard.adoc +++ b/docs/modules/ROOT/pages/wizard.adoc @@ -10,5 +10,5 @@ NOTE: We strongly recommend checking the xref:components.adoc[Components] sectio ++++ - + ++++ diff --git a/src/access/accesscontrol/accesscontrol.cairo b/src/access/accesscontrol/accesscontrol.cairo index 3f320d988..891b3a270 100644 --- a/src/access/accesscontrol/accesscontrol.cairo +++ b/src/access/accesscontrol/accesscontrol.cairo @@ -13,11 +13,12 @@ pub mod AccessControlComponent { use openzeppelin::introspection::src5::SRC5Component; use starknet::ContractAddress; use starknet::get_caller_address; + use starknet::storage::Map; #[storage] struct Storage { - AccessControl_role_admin: LegacyMap, - AccessControl_role_member: LegacyMap<(felt252, ContractAddress), bool>, + AccessControl_role_admin: Map, + AccessControl_role_member: Map<(felt252, ContractAddress), bool>, } #[event] @@ -96,7 +97,7 @@ pub mod AccessControlComponent { fn grant_role( ref self: ComponentState, role: felt252, account: ContractAddress ) { - let admin = AccessControl::get_role_admin(@self, role); + let admin = Self::get_role_admin(@self, role); self.assert_only_role(admin); self._grant_role(role, account); } @@ -111,7 +112,7 @@ pub mod AccessControlComponent { fn revoke_role( ref self: ComponentState, role: felt252, account: ContractAddress ) { - let admin = AccessControl::get_role_admin(@self, role); + let admin = Self::get_role_admin(@self, role); self.assert_only_role(admin); self._revoke_role(role, account); } diff --git a/src/access/accesscontrol/dual_accesscontrol.cairo b/src/access/accesscontrol/dual_accesscontrol.cairo index b9cca51f9..82b4f9256 100644 --- a/src/access/accesscontrol/dual_accesscontrol.cairo +++ b/src/access/accesscontrol/dual_accesscontrol.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (access/accesscontrol/dual_accesscontrol.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (access/accesscontrol/dual_accesscontrol.cairo) use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; diff --git a/src/access/accesscontrol/interface.cairo b/src/access/accesscontrol/interface.cairo index 8487b007f..9af3f87f7 100644 --- a/src/access/accesscontrol/interface.cairo +++ b/src/access/accesscontrol/interface.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (access/accesscontrol/interface.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (access/accesscontrol/interface.cairo) use starknet::ContractAddress; diff --git a/src/access/ownable/dual_ownable.cairo b/src/access/ownable/dual_ownable.cairo index d4ae86aa2..1695e3e2d 100644 --- a/src/access/ownable/dual_ownable.cairo +++ b/src/access/ownable/dual_ownable.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (access/ownable/dual_ownable.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (access/ownable/dual_ownable.cairo) use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; @@ -42,7 +42,7 @@ impl DualCaseOwnableImpl of DualCaseOwnableTrait { } fn renounce_ownership(self: @DualCaseOwnable) { - let mut args = array![]; + let args = array![]; try_selector_with_fallback( *self.contract_address, diff --git a/src/access/ownable/interface.cairo b/src/access/ownable/interface.cairo index 591eb4fa7..2f6b11578 100644 --- a/src/access/ownable/interface.cairo +++ b/src/access/ownable/interface.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (access/ownable/interface.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (access/ownable/interface.cairo) use starknet::ContractAddress; diff --git a/src/access/ownable/ownable.cairo b/src/access/ownable/ownable.cairo index c60b214e1..8443a23ff 100644 --- a/src/access/ownable/ownable.cairo +++ b/src/access/ownable/ownable.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (access/ownable/ownable.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (access/ownable/ownable.cairo) /// # Ownable Component /// diff --git a/src/account/account.cairo b/src/account/account.cairo index c13192540..b63d756d4 100644 --- a/src/account/account.cairo +++ b/src/account/account.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (account/account.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (account/account.cairo) /// # Account Component /// @@ -327,8 +327,8 @@ pub mod AccountComponent { /// Validates that `new_owner` accepted the ownership of the contract. /// - /// WARNING: This function assumes that `current_owner` is the current owner of the contract, and - /// does not validate this assumption. + /// WARNING: This function assumes that `current_owner` is the current owner of the + /// contract, and does not validate this assumption. /// /// Requirements: /// diff --git a/src/account/dual_account.cairo b/src/account/dual_account.cairo index 3c0d33c14..dd9e4be34 100644 --- a/src/account/dual_account.cairo +++ b/src/account/dual_account.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (account/dual_account.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (account/dual_account.cairo) use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; diff --git a/src/account/dual_eth_account.cairo b/src/account/dual_eth_account.cairo index eee086574..22cd39f9e 100644 --- a/src/account/dual_eth_account.cairo +++ b/src/account/dual_eth_account.cairo @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (account/dual_eth_account.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (account/dual_eth_account.cairo) use openzeppelin::account::interface::EthPublicKey; -use openzeppelin::account::utils::secp256k1::Secp256k1PointSerde; use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; diff --git a/src/account/eth_account.cairo b/src/account/eth_account.cairo index cb8082932..61eb038af 100644 --- a/src/account/eth_account.cairo +++ b/src/account/eth_account.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (account/eth_account.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (account/eth_account.cairo) /// # EthAccount Component /// @@ -12,7 +12,7 @@ pub mod EthAccountComponent { use core::starknet::secp256_trait::Secp256PointTrait; use openzeppelin::account::interface::EthPublicKey; use openzeppelin::account::interface; - use openzeppelin::account::utils::secp256k1::{Secp256k1PointSerde, Secp256k1PointStorePacking}; + use openzeppelin::account::utils::secp256k1::Secp256k1PointStorePacking; use openzeppelin::account::utils::{MIN_TRANSACTION_VERSION, QUERY_VERSION, QUERY_OFFSET}; use openzeppelin::account::utils::{execute_calls, is_valid_eth_signature}; use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; @@ -333,8 +333,8 @@ pub mod EthAccountComponent { /// Validates that `new_owner` accepted the ownership of the contract. /// - /// WARNING: This function assumes that `current_owner` is the current owner of the contract, and - /// does not validate this assumption. + /// WARNING: This function assumes that `current_owner` is the current owner of the + /// contract, and does not validate this assumption. /// /// Requirements: /// diff --git a/src/account/interface.cairo b/src/account/interface.cairo index 957a0e5e9..3b7e8f69d 100644 --- a/src/account/interface.cairo +++ b/src/account/interface.cairo @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (account/interface.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (account/interface.cairo) -use openzeppelin::account::utils::secp256k1::Secp256k1PointSerde; use starknet::ContractAddress; use starknet::account::Call; diff --git a/src/account/utils.cairo b/src/account/utils.cairo index 8dfe7b49d..6fed1e68e 100644 --- a/src/account/utils.cairo +++ b/src/account/utils.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (account/utils.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (account/utils.cairo) pub mod secp256k1; pub mod signature; @@ -15,7 +15,7 @@ pub const QUERY_OFFSET: u256 = 0x100000000000000000000000000000000; pub const QUERY_VERSION: u256 = 0x100000000000000000000000000000001; pub fn execute_calls(mut calls: Array) -> Array> { - let mut res = ArrayTrait::new(); + let mut res = array![]; loop { match calls.pop_front() { Option::Some(call) => { diff --git a/src/account/utils/secp256k1.cairo b/src/account/utils/secp256k1.cairo index 084ee6ddc..fe4f00695 100644 --- a/src/account/utils/secp256k1.cairo +++ b/src/account/utils/secp256k1.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (account/utils/secp256k1.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (account/utils/secp256k1.cairo) use core::fmt::{Debug, Formatter, Error}; use starknet::SyscallResultTrait; @@ -11,7 +11,8 @@ use starknet::storage_access::StorePacking; /// /// The packing is done as follows: /// - First felt contains x.low (x being the x-coordinate of the point). -/// - Second felt contains x.high and the parity bit, at the least significant bits (2 * x.high + parity). +/// - Second felt contains x.high and the parity bit, at the least significant bits (2 * x.high + +/// parity). pub impl Secp256k1PointStorePacking of StorePacking { fn pack(value: Secp256k1Point) -> (felt252, felt252) { let (x, y) = value.get_coordinates().unwrap_syscall(); @@ -38,17 +39,6 @@ pub impl Secp256k1PointStorePacking of StorePacking { - fn serialize(self: @Secp256k1Point, ref output: Array) { - let point = (*self).get_coordinates().unwrap_syscall(); - point.serialize(ref output) - } - fn deserialize(ref serialized: Span) -> Option { - let (x, y) = Serde::<(u256, u256)>::deserialize(ref serialized)?; - Secp256Trait::secp256_ec_new_syscall(x, y).unwrap_syscall() - } -} - pub(crate) impl Secp256k1PointPartialEq of PartialEq { #[inline(always)] fn eq(lhs: @Secp256k1Point, rhs: @Secp256k1Point) -> bool { diff --git a/src/account/utils/signature.cairo b/src/account/utils/signature.cairo index 13caa8391..f247df83f 100644 --- a/src/account/utils/signature.cairo +++ b/src/account/utils/signature.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (account/utils/signature.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (account/utils/signature.cairo) use core::ecdsa::check_ecdsa_signature; use openzeppelin::account::interface::EthPublicKey; diff --git a/src/cairo_project.toml b/src/cairo_project.toml deleted file mode 100644 index badca57cd..000000000 --- a/src/cairo_project.toml +++ /dev/null @@ -1,2 +0,0 @@ -[crate_roots] -openzeppelin = "." diff --git a/src/governance.cairo b/src/governance.cairo index b5614dd82..01d740d38 100644 --- a/src/governance.cairo +++ b/src/governance.cairo @@ -1 +1,2 @@ +pub mod timelock; pub mod utils; diff --git a/src/governance/timelock.cairo b/src/governance/timelock.cairo new file mode 100644 index 000000000..0abcf6e3b --- /dev/null +++ b/src/governance/timelock.cairo @@ -0,0 +1,9 @@ +pub mod interface; +pub mod timelock_controller; +pub mod utils; + +pub use timelock_controller::OperationState; +pub use timelock_controller::TimelockControllerComponent::{ + PROPOSER_ROLE, CANCELLER_ROLE, EXECUTOR_ROLE +}; +pub use timelock_controller::TimelockControllerComponent; diff --git a/src/governance/timelock/interface.cairo b/src/governance/timelock/interface.cairo new file mode 100644 index 000000000..0c89d0c20 --- /dev/null +++ b/src/governance/timelock/interface.cairo @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (governance/timelock/interface.cairo) + +use openzeppelin::governance::timelock::OperationState; +use starknet::ContractAddress; +use starknet::account::Call; + +#[starknet::interface] +pub trait ITimelock { + fn is_operation(self: @TState, id: felt252) -> bool; + fn is_operation_pending(self: @TState, id: felt252) -> bool; + fn is_operation_ready(self: @TState, id: felt252) -> bool; + fn is_operation_done(self: @TState, id: felt252) -> bool; + fn get_timestamp(self: @TState, id: felt252) -> u64; + fn get_operation_state(self: @TState, id: felt252) -> OperationState; + fn get_min_delay(self: @TState) -> u64; + fn hash_operation(self: @TState, call: Call, predecessor: felt252, salt: felt252) -> felt252; + fn hash_operation_batch( + self: @TState, calls: Span, predecessor: felt252, salt: felt252 + ) -> felt252; + fn schedule(ref self: TState, call: Call, predecessor: felt252, salt: felt252, delay: u64); + fn schedule_batch( + ref self: TState, calls: Span, predecessor: felt252, salt: felt252, delay: u64 + ); + fn cancel(ref self: TState, id: felt252); + fn execute(ref self: TState, call: Call, predecessor: felt252, salt: felt252); + fn execute_batch(ref self: TState, calls: Span, predecessor: felt252, salt: felt252); + fn update_delay(ref self: TState, new_delay: u64); +} + +#[starknet::interface] +pub trait TimelockABI { + fn is_operation(self: @TState, id: felt252) -> bool; + fn is_operation_pending(self: @TState, id: felt252) -> bool; + fn is_operation_ready(self: @TState, id: felt252) -> bool; + fn is_operation_done(self: @TState, id: felt252) -> bool; + fn get_timestamp(self: @TState, id: felt252) -> u64; + fn get_operation_state(self: @TState, id: felt252) -> OperationState; + fn get_min_delay(self: @TState) -> u64; + fn hash_operation(self: @TState, call: Call, predecessor: felt252, salt: felt252) -> felt252; + fn hash_operation_batch( + self: @TState, calls: Span, predecessor: felt252, salt: felt252 + ) -> felt252; + fn schedule(ref self: TState, call: Call, predecessor: felt252, salt: felt252, delay: u64); + fn schedule_batch( + ref self: TState, calls: Span, predecessor: felt252, salt: felt252, delay: u64 + ); + fn cancel(ref self: TState, id: felt252); + fn execute(ref self: TState, call: Call, predecessor: felt252, salt: felt252); + fn execute_batch(ref self: TState, calls: Span, predecessor: felt252, salt: felt252); + fn update_delay(ref self: TState, new_delay: u64); + + // ISRC5 + fn supports_interface(self: @TState, interface_id: felt252) -> bool; + + // IAccessControl + fn has_role(self: @TState, role: felt252, account: ContractAddress) -> bool; + fn get_role_admin(self: @TState, role: felt252) -> felt252; + fn grant_role(ref self: TState, role: felt252, account: ContractAddress); + fn revoke_role(ref self: TState, role: felt252, account: ContractAddress); + fn renounce_role(ref self: TState, role: felt252, account: ContractAddress); + + // IAccessControlCamel + fn hasRole(self: @TState, role: felt252, account: ContractAddress) -> bool; + fn getRoleAdmin(self: @TState, role: felt252) -> felt252; + fn grantRole(ref self: TState, role: felt252, account: ContractAddress); + fn revokeRole(ref self: TState, role: felt252, account: ContractAddress); + fn renounceRole(ref self: TState, role: felt252, account: ContractAddress); +} diff --git a/src/governance/timelock/timelock_controller.cairo b/src/governance/timelock/timelock_controller.cairo new file mode 100644 index 000000000..494692025 --- /dev/null +++ b/src/governance/timelock/timelock_controller.cairo @@ -0,0 +1,659 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (governance/timelock/timelock_controller.cairo) + +/// # TimelockController Component +/// +/// Component that acts as a timelocked controller. When set as the owner of an `Ownable` smart +/// contract, it enforces a timelock on all `only_owner` maintenance operations. This gives time for +/// users of the controlled contract to exit before a potentially dangerous maintenance operation is +/// applied. +/// +/// By default, this component is self administered, meaning administration tasks have to go through +/// the timelock process. The proposer role is in charge of proposing operations. A common use case +/// is to position the timelock controller as the owner of a smart contract, with a multi-sig +/// or a DAO as the sole proposer. +#[starknet::component] +pub mod TimelockControllerComponent { + use core::hash::{HashStateTrait, HashStateExTrait}; + use core::num::traits::Zero; + use core::pedersen::PedersenTrait; + use openzeppelin::access::accesscontrol::AccessControlComponent::InternalTrait as AccessControlInternalTrait; + use openzeppelin::access::accesscontrol::AccessControlComponent::{ + AccessControlImpl, AccessControlCamelImpl + }; + use openzeppelin::access::accesscontrol::AccessControlComponent; + use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; + use openzeppelin::governance::timelock::interface::{ITimelock, TimelockABI}; + use openzeppelin::governance::timelock::utils::call_impls::{ + HashCallImpl, HashCallsImpl, CallPartialEq + }; + use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; + use openzeppelin::introspection::src5::SRC5Component; + use starknet::ContractAddress; + use starknet::SyscallResultTrait; + use starknet::account::Call; + use starknet::storage::Map; + use super::OperationState; + + // Constants + pub const PROPOSER_ROLE: felt252 = selector!("PROPOSER_ROLE"); + pub const EXECUTOR_ROLE: felt252 = selector!("EXECUTOR_ROLE"); + pub const CANCELLER_ROLE: felt252 = selector!("CANCELLER_ROLE"); + const DONE_TIMESTAMP: u64 = 1; + + #[storage] + struct Storage { + TimelockController_timestamps: Map, + TimelockController_min_delay: u64 + } + + #[event] + #[derive(Drop, PartialEq, starknet::Event)] + pub enum Event { + CallScheduled: CallScheduled, + CallExecuted: CallExecuted, + CallSalt: CallSalt, + CallCancelled: CallCancelled, + MinDelayChanged: MinDelayChanged + } + + /// Emitted when `call` is scheduled as part of operation `id`. + #[derive(Drop, PartialEq, starknet::Event)] + pub struct CallScheduled { + #[key] + pub id: felt252, + #[key] + pub index: felt252, + pub call: Call, + pub predecessor: felt252, + pub delay: u64 + } + + /// Emitted when `call` is performed as part of operation `id`. + #[derive(Drop, PartialEq, starknet::Event)] + pub struct CallExecuted { + #[key] + pub id: felt252, + #[key] + pub index: felt252, + pub call: Call + } + + /// Emitted when a new proposal is scheduled with non-zero salt. + #[derive(Drop, PartialEq, starknet::Event)] + pub struct CallSalt { + #[key] + pub id: felt252, + pub salt: felt252 + } + + /// Emitted when operation `id` is cancelled. + #[derive(Drop, PartialEq, starknet::Event)] + pub struct CallCancelled { + #[key] + pub id: felt252 + } + + /// Emitted when the minimum delay for future operations is modified. + #[derive(Drop, PartialEq, starknet::Event)] + pub struct MinDelayChanged { + pub old_duration: u64, + pub new_duration: u64 + } + + pub mod Errors { + pub const INVALID_OPERATION_LEN: felt252 = 'Timelock: invalid operation len'; + pub const INSUFFICIENT_DELAY: felt252 = 'Timelock: insufficient delay'; + pub const EXPECTED_UNSET_OPERATION: felt252 = 'Timelock: expected Unset op'; + pub const EXPECTED_PENDING_OPERATION: felt252 = 'Timelock: expected Pending op'; + pub const EXPECTED_READY_OPERATION: felt252 = 'Timelock: expected Ready op'; + pub const UNEXECUTED_PREDECESSOR: felt252 = 'Timelock: awaiting predecessor'; + pub const UNAUTHORIZED_CALLER: felt252 = 'Timelock: unauthorized caller'; + } + + #[embeddable_as(TimelockImpl)] + impl Timelock< + TContractState, + +HasComponent, + +SRC5Component::HasComponent, + +AccessControlComponent::HasComponent, + +Drop + > of ITimelock> { + /// Returns whether `id` corresponds to a registered operation. + /// This includes the OperationStates: Waiting, Ready, and Done. + fn is_operation(self: @ComponentState, id: felt252) -> bool { + Self::get_operation_state(self, id) != OperationState::Unset + } + + /// Returns whether the `id` OperationState is pending or not. + /// Note that a pending operation may be either Waiting or Ready. + fn is_operation_pending(self: @ComponentState, id: felt252) -> bool { + let state = Self::get_operation_state(self, id); + state == OperationState::Waiting || state == OperationState::Ready + } + + /// Returns whether the `id` OperationState is Ready or not. + fn is_operation_ready(self: @ComponentState, id: felt252) -> bool { + Self::get_operation_state(self, id) == OperationState::Ready + } + + /// Returns whether the `id` OperationState is Done or not. + fn is_operation_done(self: @ComponentState, id: felt252) -> bool { + Self::get_operation_state(self, id) == OperationState::Done + } + + /// Returns the timestamp at which `id` becomes Ready. + /// + /// NOTE: `0` means the OperationState is Unset and `1` means the OperationState + /// is Done. + fn get_timestamp(self: @ComponentState, id: felt252) -> u64 { + self.TimelockController_timestamps.read(id) + } + + /// Returns the OperationState for `id`. + fn get_operation_state( + self: @ComponentState, id: felt252 + ) -> OperationState { + let timestamp = Self::get_timestamp(self, id); + if (timestamp == 0) { + return OperationState::Unset; + } else if (timestamp == DONE_TIMESTAMP) { + return OperationState::Done; + } else if (timestamp > starknet::get_block_timestamp()) { + return OperationState::Waiting; + } else { + return OperationState::Ready; + } + } + + /// Returns the minimum delay in seconds for an operation to become valid. + /// This value can be changed by executing an operation that calls `update_delay`. + fn get_min_delay(self: @ComponentState) -> u64 { + self.TimelockController_min_delay.read() + } + + /// Returns the identifier of an operation containing a single transaction. + fn hash_operation( + self: @ComponentState, call: Call, predecessor: felt252, salt: felt252 + ) -> felt252 { + PedersenTrait::new(0) + .update_with(call) + .update_with(predecessor) + .update_with(salt) + .finalize() + } + + /// Returns the identifier of an operation containing a batch of transactions. + fn hash_operation_batch( + self: @ComponentState, + calls: Span, + predecessor: felt252, + salt: felt252 + ) -> felt252 { + PedersenTrait::new(0) + .update_with(calls) + .update_with(predecessor) + .update_with(salt) + .finalize() + } + + /// Schedule an operation containing a single transaction. + /// + /// Requirements: + /// + /// - the caller must have the `PROPOSER_ROLE` role. + /// + /// Emits `CallScheduled` event. + /// If `salt` is not zero, emits `CallSalt` event. + fn schedule( + ref self: ComponentState, + call: Call, + predecessor: felt252, + salt: felt252, + delay: u64 + ) { + self.assert_only_role(PROPOSER_ROLE); + + let id = Self::hash_operation(@self, call, predecessor, salt); + self._schedule(id, delay); + self.emit(CallScheduled { id, index: 0, call, predecessor, delay }); + + if salt != 0 { + self.emit(CallSalt { id, salt }); + } + } + + /// Schedule an operation containing a batch of transactions. + /// + /// Requirements: + /// + /// - the caller must have the `PROPOSER_ROLE` role. + /// + /// Emits one `CallScheduled` event for each transaction in the batch. + /// If `salt` is not zero, emits `CallSalt` event. + fn schedule_batch( + ref self: ComponentState, + calls: Span, + predecessor: felt252, + salt: felt252, + delay: u64 + ) { + self.assert_only_role(PROPOSER_ROLE); + + let id = Self::hash_operation_batch(@self, calls, predecessor, salt); + self._schedule(id, delay); + + let mut index = 0; + for call in calls { + self.emit(CallScheduled { id, index, call: *call, predecessor, delay }); + index += 1; + }; + + if salt != 0 { + self.emit(CallSalt { id, salt }); + } + } + + /// Cancel an operation. + /// + /// Requirements: + /// + /// - The caller must have the `CANCELLER_ROLE` role. + /// - `id` must be an operation. + /// + /// Emits a `CallCancelled` event. + fn cancel(ref self: ComponentState, id: felt252) { + self.assert_only_role(CANCELLER_ROLE); + assert(Self::is_operation_pending(@self, id), Errors::EXPECTED_PENDING_OPERATION); + + self.TimelockController_timestamps.write(id, 0); + self.emit(CallCancelled { id }); + } + + /// Execute a (Ready) operation containing a single Call. + /// + /// Requirements: + /// + /// - Caller must have `EXECUTOR_ROLE`. + /// - `id` must be in Ready OperationState. + /// - `predecessor` must either be `0` or in Done OperationState. + /// + /// NOTE: This function can reenter, but it doesn't pose a risk because `_after_call` + /// checks that the proposal is pending, thus any modifications to the operation during + /// reentrancy should be caught. + /// + /// Emits a `CallExecuted` event. + fn execute( + ref self: ComponentState, + call: Call, + predecessor: felt252, + salt: felt252 + ) { + self.assert_only_role_or_open_role(EXECUTOR_ROLE); + + let id = Self::hash_operation(@self, call, predecessor, salt); + self._before_call(id, predecessor); + self._execute(call); + self.emit(CallExecuted { id, index: 0, call }); + self._after_call(id); + } + + /// Execute a (Ready) operation containing a batch of Calls. + /// + /// Requirements: + /// + /// - Caller must have `EXECUTOR_ROLE`. + /// - `id` must be in Ready OperationState. + /// - `predecessor` must either be `0` or in Done OperationState. + /// + /// NOTE: This function can reenter, but it doesn't pose a risk because `_after_call` + /// checks that the proposal is pending, thus any modifications to the operation during + /// reentrancy should be caught. + /// + /// Emits a `CallExecuted` event for each Call. + fn execute_batch( + ref self: ComponentState, + calls: Span, + predecessor: felt252, + salt: felt252 + ) { + self.assert_only_role_or_open_role(EXECUTOR_ROLE); + + let id = Self::hash_operation_batch(@self, calls, predecessor, salt); + self._before_call(id, predecessor); + + let mut index = 0; + for call in calls { + self._execute(*call); + self.emit(CallExecuted { id, index, call: *call }); + index += 1; + }; + + self._after_call(id); + } + + /// Changes the minimum timelock duration for future operations. + /// + /// Requirements: + /// + /// - The caller must be the timelock itself. This can only be achieved by scheduling + /// and later executing an operation where the timelock is the target and the data + /// is the serialized call to this function. + /// + /// Emits a `MinDelayChanged` event. + fn update_delay(ref self: ComponentState, new_delay: u64) { + self.assert_only_self(); + + let min_delay = self.TimelockController_min_delay.read(); + self.emit(MinDelayChanged { old_duration: min_delay, new_duration: new_delay }); + + self.TimelockController_min_delay.write(new_delay); + } + } + + #[embeddable_as(TimelockMixinImpl)] + impl TimelockMixin< + TContractState, + +HasComponent, + impl SRC5: SRC5Component::HasComponent, + impl AccessControl: AccessControlComponent::HasComponent, + +Drop + > of TimelockABI> { + fn is_operation(self: @ComponentState, id: felt252) -> bool { + Timelock::is_operation(self, id) + } + + fn is_operation_pending(self: @ComponentState, id: felt252) -> bool { + Timelock::is_operation_pending(self, id) + } + + fn is_operation_ready(self: @ComponentState, id: felt252) -> bool { + Timelock::is_operation_ready(self, id) + } + + fn is_operation_done(self: @ComponentState, id: felt252) -> bool { + Timelock::is_operation_done(self, id) + } + + fn get_timestamp(self: @ComponentState, id: felt252) -> u64 { + Timelock::get_timestamp(self, id) + } + + fn get_operation_state( + self: @ComponentState, id: felt252 + ) -> OperationState { + Timelock::get_operation_state(self, id) + } + + fn get_min_delay(self: @ComponentState) -> u64 { + Timelock::get_min_delay(self) + } + + fn hash_operation( + self: @ComponentState, call: Call, predecessor: felt252, salt: felt252 + ) -> felt252 { + Timelock::hash_operation(self, call, predecessor, salt) + } + + fn hash_operation_batch( + self: @ComponentState, + calls: Span, + predecessor: felt252, + salt: felt252 + ) -> felt252 { + Timelock::hash_operation_batch(self, calls, predecessor, salt) + } + + fn schedule( + ref self: ComponentState, + call: Call, + predecessor: felt252, + salt: felt252, + delay: u64 + ) { + Timelock::schedule(ref self, call, predecessor, salt, delay); + } + + fn schedule_batch( + ref self: ComponentState, + calls: Span, + predecessor: felt252, + salt: felt252, + delay: u64 + ) { + Timelock::schedule_batch(ref self, calls, predecessor, salt, delay); + } + + fn cancel(ref self: ComponentState, id: felt252) { + Timelock::cancel(ref self, id); + } + + fn execute( + ref self: ComponentState, + call: Call, + predecessor: felt252, + salt: felt252 + ) { + Timelock::execute(ref self, call, predecessor, salt); + } + + fn execute_batch( + ref self: ComponentState, + calls: Span, + predecessor: felt252, + salt: felt252 + ) { + Timelock::execute_batch(ref self, calls, predecessor, salt); + } + + fn update_delay(ref self: ComponentState, new_delay: u64) { + Timelock::update_delay(ref self, new_delay); + } + + // ISRC5 + fn supports_interface( + self: @ComponentState, interface_id: felt252 + ) -> bool { + let src5 = get_dep_component!(self, SRC5); + src5.supports_interface(interface_id) + } + + // IAccessControl + fn has_role( + self: @ComponentState, role: felt252, account: ContractAddress + ) -> bool { + let access_control = get_dep_component!(self, AccessControl); + access_control.has_role(role, account) + } + + fn get_role_admin(self: @ComponentState, role: felt252) -> felt252 { + let access_control = get_dep_component!(self, AccessControl); + access_control.get_role_admin(role) + } + + fn grant_role( + ref self: ComponentState, role: felt252, account: ContractAddress + ) { + let mut access_control = get_dep_component_mut!(ref self, AccessControl); + access_control.grant_role(role, account); + } + + fn revoke_role( + ref self: ComponentState, role: felt252, account: ContractAddress + ) { + let mut access_control = get_dep_component_mut!(ref self, AccessControl); + access_control.revoke_role(role, account); + } + fn renounce_role( + ref self: ComponentState, role: felt252, account: ContractAddress + ) { + let mut access_control = get_dep_component_mut!(ref self, AccessControl); + access_control.renounce_role(role, account); + } + + // IAccessControlCamel + fn hasRole( + self: @ComponentState, role: felt252, account: ContractAddress + ) -> bool { + Self::has_role(self, role, account) + } + + fn getRoleAdmin(self: @ComponentState, role: felt252) -> felt252 { + Self::getRoleAdmin(self, role) + } + + fn grantRole( + ref self: ComponentState, role: felt252, account: ContractAddress + ) { + Self::grant_role(ref self, role, account); + } + + fn revokeRole( + ref self: ComponentState, role: felt252, account: ContractAddress + ) { + Self::revoke_role(ref self, role, account); + } + + fn renounceRole( + ref self: ComponentState, role: felt252, account: ContractAddress + ) { + Self::renounce_role(ref self, role, account); + } + } + + #[generate_trait] + pub impl InternalImpl< + TContractState, + +HasComponent, + impl SRC5: SRC5Component::HasComponent, + impl AccessControl: AccessControlComponent::HasComponent, + +Drop + > of InternalTrait { + /// Initializes the contract by registering support for SRC5 and AccessControl. + /// + /// This function also configures the contract with the following parameters: + /// + /// - `min_delay`: initial minimum delay in seconds for operations. + /// - `proposers`: accounts to be granted proposer and canceller roles. + /// - `executors`: accounts to be granted executor role. + /// - `admin`: optional account to be granted admin role; disable with zero address. + /// + /// WARNING: The optional admin can aid with initial configuration of roles after deployment + /// without being subject to delay, but this role should be subsequently renounced in favor + /// of administration through timelocked proposals. + /// + /// Emits two `RoleGranted` events for each account in `proposers` with `PROPOSER_ROLE` + /// admin `CANCELLER_ROLE` roles. + /// + /// Emits a `RoleGranted` event for each account in `executors` with `EXECUTOR_ROLE` role. + /// + /// May emit a `RoleGranted` event for `admin` with `DEFAULT_ADMIN_ROLE` role (if `admin` is + /// not zero). + /// + /// Emits `MinDelayChanged` event. + fn initializer( + ref self: ComponentState, + min_delay: u64, + proposers: Span, + executors: Span, + admin: ContractAddress + ) { + // Register access control ID and self as default admin + let mut access_component = get_dep_component_mut!(ref self, AccessControl); + access_component.initializer(); + access_component._grant_role(DEFAULT_ADMIN_ROLE, starknet::get_contract_address()); + + // Optional admin + if admin != Zero::zero() { + access_component._grant_role(DEFAULT_ADMIN_ROLE, admin) + }; + + // Register proposers and cancellers + for proposer in proposers { + access_component._grant_role(PROPOSER_ROLE, *proposer); + access_component._grant_role(CANCELLER_ROLE, *proposer); + }; + + // Register executors + for executor in executors { + access_component._grant_role(EXECUTOR_ROLE, *executor); + }; + + // Set minimum delay + self.TimelockController_min_delay.write(min_delay); + self.emit(MinDelayChanged { old_duration: 0, new_duration: min_delay }); + } + + /// Validates that the caller has the given `role`. + /// Otherwise it reverts. + fn assert_only_role(self: @ComponentState, role: felt252) { + let access_component = get_dep_component!(self, AccessControl); + access_component.assert_only_role(role); + } + + /// Validates that the caller has the given `role`. + /// If `role` is granted to the zero address, then this is considered an open role which + /// allows anyone to be the caller. + fn assert_only_role_or_open_role(self: @ComponentState, role: felt252) { + let access_component = get_dep_component!(self, AccessControl); + let is_role_open = access_component.has_role(role, Zero::zero()); + if !is_role_open { + access_component.assert_only_role(role); + } + } + + /// Validates that the caller is the timelock contract itself. + /// Otherwise it reverts. + fn assert_only_self(self: @ComponentState) { + let this = starknet::get_contract_address(); + let caller = starknet::get_caller_address(); + assert(caller == this, Errors::UNAUTHORIZED_CALLER); + } + + /// Private function that checks before execution of an operation's calls. + /// + /// Requirements: + /// + /// - `id` must be in the Ready OperationState. + /// - `predecessor` must either be zero or be in the Done OperationState. + fn _before_call(self: @ComponentState, id: felt252, predecessor: felt252) { + assert(Timelock::is_operation_ready(self, id), Errors::EXPECTED_READY_OPERATION); + assert( + predecessor == 0 || Timelock::is_operation_done(self, predecessor), + Errors::UNEXECUTED_PREDECESSOR + ); + } + + /// Private functions that checks after execution of an operation's calls. + /// + /// Requirements: + /// + /// - `id` must be in the Ready OperationState. + fn _after_call(ref self: ComponentState, id: felt252) { + assert(Timelock::is_operation_ready(@self, id), Errors::EXPECTED_READY_OPERATION); + self.TimelockController_timestamps.write(id, DONE_TIMESTAMP); + } + + /// Private function that schedules an operation that is to become valid after a given + /// `delay`. + fn _schedule(ref self: ComponentState, id: felt252, delay: u64) { + assert(!Timelock::is_operation(@self, id), Errors::EXPECTED_UNSET_OPERATION); + assert(Timelock::get_min_delay(@self) <= delay, Errors::INSUFFICIENT_DELAY); + self.TimelockController_timestamps.write(id, starknet::get_block_timestamp() + delay); + } + + /// Private function that executes an operation's calls. + fn _execute(ref self: ComponentState, call: Call) { + let Call { to, selector, calldata } = call; + starknet::syscalls::call_contract_syscall(to, selector, calldata).unwrap_syscall(); + } + } +} + +#[derive(Drop, Serde, PartialEq, Debug)] +pub enum OperationState { + Unset, + Waiting, + Ready, + Done +} diff --git a/src/governance/timelock/utils.cairo b/src/governance/timelock/utils.cairo new file mode 100644 index 000000000..0b626634e --- /dev/null +++ b/src/governance/timelock/utils.cairo @@ -0,0 +1 @@ +pub mod call_impls; diff --git a/src/governance/timelock/utils/call_impls.cairo b/src/governance/timelock/utils/call_impls.cairo new file mode 100644 index 000000000..428259e57 --- /dev/null +++ b/src/governance/timelock/utils/call_impls.cairo @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (governance/timelock/utils/call_impls.cairo) + +use core::hash::{HashStateTrait, HashStateExTrait, Hash}; +use starknet::account::Call; + +pub(crate) impl HashCallImpl, +Drop> of Hash { + fn update_state(mut state: S, value: Call) -> S { + let Call { to, selector, calldata } = value; + state = state.update_with(to).update_with(selector).update_with(calldata.len()); + for elem in calldata { + state = state.update_with(*elem); + }; + + state + } +} + +pub(crate) impl HashCallsImpl, +Drop> of Hash, S> { + fn update_state(mut state: S, value: Span) -> S { + state = state.update_with(value.len()); + for call in value { + state = state.update_with(*call); + }; + + state + } +} + +pub(crate) impl CallPartialEq of PartialEq { + #[inline(always)] + fn eq(lhs: @Call, rhs: @Call) -> bool { + let Call { to: l_to, selector: l_selector, calldata: l_calldata } = lhs; + let Call { to: r_to, selector: r_selector, calldata: r_calldata } = rhs; + l_to == r_to && l_selector == r_selector && l_calldata == r_calldata + } + #[inline(always)] + fn ne(lhs: @Call, rhs: @Call) -> bool { + !(lhs == rhs) + } +} diff --git a/src/governance/utils/interfaces/votes.cairo b/src/governance/utils/interfaces/votes.cairo index 44188130a..d55c481c2 100644 --- a/src/governance/utils/interfaces/votes.cairo +++ b/src/governance/utils/interfaces/votes.cairo @@ -11,9 +11,10 @@ pub trait IVotes { /// Returns the total supply of votes available at a specific moment in the past. /// - /// NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. - /// Votes that have not been delegated are still part of total supply, even though they would not participate in a - /// vote. + /// NOTE: This value is the sum of all available votes, which is not necessarily the sum of all + /// delegated votes. + /// Votes that have not been delegated are still part of total supply, even though they would + /// not participate in a vote. fn get_past_total_supply(self: @TState, timepoint: u64) -> u256; /// Returns the delegate that `account` has chosen. diff --git a/src/introspection/interface.cairo b/src/introspection/interface.cairo index bb0d6c9cf..0de388130 100644 --- a/src/introspection/interface.cairo +++ b/src/introspection/interface.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (introspection/interface.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (introspection/interface.cairo) pub const ISRC5_ID: felt252 = 0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055; diff --git a/src/introspection/src5.cairo b/src/introspection/src5.cairo index 4ac0d4b7e..530fb73b2 100644 --- a/src/introspection/src5.cairo +++ b/src/introspection/src5.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (introspection/src5.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (introspection/src5.cairo) /// # SRC5 Component /// @@ -7,10 +7,11 @@ #[starknet::component] pub mod SRC5Component { use openzeppelin::introspection::interface; + use starknet::storage::Map; #[storage] struct Storage { - SRC5_supported_interfaces: LegacyMap + SRC5_supported_interfaces: Map } pub mod Errors { diff --git a/src/presets/account.cairo b/src/presets/account.cairo index e9805bf3e..93cb2b5ef 100644 --- a/src/presets/account.cairo +++ b/src/presets/account.cairo @@ -1,9 +1,10 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (presets/account.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (presets/account.cairo) /// # Account Preset /// -/// OpenZeppelin's upgradeable account which can change its public key and declare, deploy, or call contracts. +/// 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; diff --git a/src/presets/erc1155.cairo b/src/presets/erc1155.cairo index 2552e7323..b24bdf100 100644 --- a/src/presets/erc1155.cairo +++ b/src/presets/erc1155.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (presets/erc1155.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (presets/erc1155.cairo) /// # ERC1155Upgradeable Preset /// diff --git a/src/presets/erc20.cairo b/src/presets/erc20.cairo index a12f43d2b..94506ba3e 100644 --- a/src/presets/erc20.cairo +++ b/src/presets/erc20.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (presets/erc20.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (presets/erc20.cairo) /// # ERC20 Preset /// diff --git a/src/presets/erc721.cairo b/src/presets/erc721.cairo index 91a469a6f..1359736f7 100644 --- a/src/presets/erc721.cairo +++ b/src/presets/erc721.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (presets/erc721.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (presets/erc721.cairo) /// # ERC721 Preset /// diff --git a/src/presets/eth_account.cairo b/src/presets/eth_account.cairo index f641904be..97bdbc8b4 100644 --- a/src/presets/eth_account.cairo +++ b/src/presets/eth_account.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (presets/eth_account.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (presets/eth_account.cairo) /// # EthAccount Preset /// @@ -9,7 +9,6 @@ pub(crate) mod EthAccountUpgradeable { use openzeppelin::account::EthAccountComponent; use openzeppelin::account::interface::EthPublicKey; - use openzeppelin::account::utils::secp256k1::Secp256k1PointSerde; use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::upgrades::UpgradeableComponent; use openzeppelin::upgrades::interface::IUpgradeable; diff --git a/src/presets/interfaces/eth_account.cairo b/src/presets/interfaces/eth_account.cairo index 9ee9ad22c..ab9af2f69 100644 --- a/src/presets/interfaces/eth_account.cairo +++ b/src/presets/interfaces/eth_account.cairo @@ -1,5 +1,4 @@ use openzeppelin::account::interface::EthPublicKey; -use openzeppelin::account::utils::secp256k1::Secp256k1PointSerde; use starknet::account::Call; use starknet::{ContractAddress, ClassHash}; diff --git a/src/presets/universal_deployer.cairo b/src/presets/universal_deployer.cairo index 2d4f84070..2777a6107 100644 --- a/src/presets/universal_deployer.cairo +++ b/src/presets/universal_deployer.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (presets/universal_deployer.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (presets/universal_deployer.cairo) /// # UniversalDeployerContract Preset /// @@ -45,8 +45,7 @@ pub(crate) mod UniversalDeployer { let deployer: ContractAddress = get_caller_address(); let mut _salt: felt252 = salt; if !from_zero { - let mut hash_state = PoseidonTrait::new(); - _salt = hash_state.update_with(deployer).update_with(salt).finalize(); + _salt = PoseidonTrait::new().update_with(deployer).update_with(salt).finalize() } let (address, _) = starknet::syscalls::deploy_syscall( diff --git a/src/security/initializable.cairo b/src/security/initializable.cairo index b265bbc8a..11e7bdabb 100644 --- a/src/security/initializable.cairo +++ b/src/security/initializable.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (security/initializable.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (security/initializable.cairo) /// # Initializable Component /// diff --git a/src/security/pausable.cairo b/src/security/pausable.cairo index d10a201a5..0be0d3a43 100644 --- a/src/security/pausable.cairo +++ b/src/security/pausable.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (security/pausable.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (security/pausable.cairo) /// # Pausable Component /// diff --git a/src/security/reentrancyguard.cairo b/src/security/reentrancyguard.cairo index de43d6c0d..994e71762 100644 --- a/src/security/reentrancyguard.cairo +++ b/src/security/reentrancyguard.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (security/reentrancyguard.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (security/reentrancyguard.cairo) /// # ReentrancyGuard Component /// @@ -22,7 +22,8 @@ pub mod ReentrancyGuardComponent { pub impl InternalImpl< TContractState, +HasComponent > of InternalTrait { - /// Prevents a contract's function from calling itself or another protected function, directly or indirectly. + /// Prevents a contract's function from calling itself or another protected function, + /// directly or indirectly. fn start(ref self: ComponentState) { assert(!self.ReentrancyGuard_entered.read(), Errors::REENTRANT_CALL); self.ReentrancyGuard_entered.write(true); diff --git a/src/tests.cairo b/src/tests.cairo index fab03b247..6a1d8a06a 100644 --- a/src/tests.cairo +++ b/src/tests.cairo @@ -5,6 +5,8 @@ mod account; #[cfg(test)] mod cryptography; #[cfg(test)] +mod governance; +#[cfg(test)] mod introspection; mod mocks; #[cfg(test)] diff --git a/src/tests/access/test_ownable.cairo b/src/tests/access/test_ownable.cairo index 463406916..8a2d8ac7d 100644 --- a/src/tests/access/test_ownable.cairo +++ b/src/tests/access/test_ownable.cairo @@ -5,7 +5,6 @@ use openzeppelin::access::ownable::interface::{IOwnable, IOwnableCamelOnly}; use openzeppelin::tests::mocks::ownable_mocks::DualCaseOwnableMock; use openzeppelin::tests::utils::constants::{ZERO, OTHER, OWNER}; use snforge_std::{spy_events, test_address, start_cheat_caller_address}; -use starknet::storage::StorageMemberAccessTrait; use super::common::OwnableSpyHelpers; diff --git a/src/tests/access/test_ownable_twostep.cairo b/src/tests/access/test_ownable_twostep.cairo index 55b58b09a..60db61cc4 100644 --- a/src/tests/access/test_ownable_twostep.cairo +++ b/src/tests/access/test_ownable_twostep.cairo @@ -8,7 +8,6 @@ use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::utils::serde::SerializedAppend; use snforge_std::{EventSpy, spy_events, start_cheat_caller_address, test_address}; use starknet::ContractAddress; -use starknet::storage::StorageMemberAccessTrait; use super::common::OwnableSpyHelpers; diff --git a/src/tests/account/ethereum/test_dual_eth_account.cairo b/src/tests/account/ethereum/test_dual_eth_account.cairo index bad86716a..bde5c53f5 100644 --- a/src/tests/account/ethereum/test_dual_eth_account.cairo +++ b/src/tests/account/ethereum/test_dual_eth_account.cairo @@ -1,8 +1,6 @@ use openzeppelin::account::dual_eth_account::{DualCaseEthAccountABI, DualCaseEthAccount}; use openzeppelin::account::interface::{EthAccountABIDispatcherTrait, EthAccountABIDispatcher}; -use openzeppelin::account::utils::secp256k1::{ - DebugSecp256k1Point, Secp256k1PointPartialEq, Secp256k1PointSerde -}; +use openzeppelin::account::utils::secp256k1::{DebugSecp256k1Point, Secp256k1PointPartialEq}; use openzeppelin::account::utils::signature::EthSignature; use openzeppelin::introspection::interface::ISRC5_ID; use openzeppelin::tests::utils::constants::secp256k1::KEY_PAIR; diff --git a/src/tests/account/ethereum/test_eth_account.cairo b/src/tests/account/ethereum/test_eth_account.cairo index 1f26956e0..4c1b50bff 100644 --- a/src/tests/account/ethereum/test_eth_account.cairo +++ b/src/tests/account/ethereum/test_eth_account.cairo @@ -4,9 +4,7 @@ use openzeppelin::account::EthAccountComponent::{PublicKeyCamelImpl, PublicKeyIm use openzeppelin::account::EthAccountComponent; use openzeppelin::account::interface::{EthAccountABIDispatcherTrait, EthAccountABIDispatcher}; use openzeppelin::account::interface::{ISRC6, ISRC6_ID}; -use openzeppelin::account::utils::secp256k1::{ - DebugSecp256k1Point, Secp256k1PointPartialEq, Secp256k1PointSerde -}; +use openzeppelin::account::utils::secp256k1::{DebugSecp256k1Point, Secp256k1PointPartialEq}; use openzeppelin::account::utils::signature::EthSignature; use openzeppelin::introspection::interface::{ISRC5, ISRC5_ID}; use openzeppelin::tests::mocks::eth_account_mocks::DualCaseEthAccountMock; diff --git a/src/tests/account/test_secp256k1.cairo b/src/tests/account/test_secp256k1.cairo index b41160a3d..0fb43803c 100644 --- a/src/tests/account/test_secp256k1.cairo +++ b/src/tests/account/test_secp256k1.cairo @@ -1,6 +1,5 @@ use openzeppelin::account::utils::secp256k1::{ - DebugSecp256k1Point, Secp256k1PointSerde, Secp256k1PointPartialEq, - Secp256k1PointStorePacking as StorePacking + DebugSecp256k1Point, Secp256k1PointPartialEq, Secp256k1PointStorePacking as StorePacking }; use starknet::SyscallResultTrait; use starknet::secp256_trait::{Secp256Trait, Secp256PointTrait}; @@ -63,53 +62,6 @@ fn test_unpack_big_secp256k1_points() { assert_eq!(y, expected_y); } -#[test] -fn test_secp256k1_serialization() { - let (big_point_1, big_point_2) = get_points(); - - let mut serialized_point = array![]; - let mut expected_serialization = array![]; - - // Check point 1 - - big_point_1.serialize(ref serialized_point); - big_point_1.get_coordinates().unwrap_syscall().serialize(ref expected_serialization); - - assert!(serialized_point == expected_serialization); - - // Check point 2 - - big_point_2.serialize(ref serialized_point); - big_point_2.get_coordinates().unwrap_syscall().serialize(ref expected_serialization); - - assert!(serialized_point == expected_serialization); -} - -#[test] -fn test_secp256k1_deserialization() { - let (big_point_1, big_point_2) = get_points(); - - // Check point 1 - - let mut expected_serialization = array![]; - - big_point_1.get_coordinates().unwrap_syscall().serialize(ref expected_serialization); - let mut expected_serialization = expected_serialization.span(); - let deserialized_point = Secp256k1PointSerde::deserialize(ref expected_serialization).unwrap(); - - assert_eq!(big_point_1, deserialized_point); - - // Check point 2 - - let mut expected_serialization = array![]; - - big_point_2.get_coordinates().unwrap_syscall().serialize(ref expected_serialization); - let mut expected_serialization = expected_serialization.span(); - let deserialized_point = Secp256k1PointSerde::deserialize(ref expected_serialization).unwrap(); - - assert_eq!(big_point_2, deserialized_point); -} - #[test] fn test_partial_eq() { let (big_point_1, big_point_2) = get_points(); diff --git a/src/tests/governance.cairo b/src/tests/governance.cairo new file mode 100644 index 000000000..1d40503c0 --- /dev/null +++ b/src/tests/governance.cairo @@ -0,0 +1,2 @@ +//mod test_timelock; +mod test_utils; diff --git a/src/tests/governance/test_timelock.cairo b/src/tests/governance/test_timelock.cairo new file mode 100644 index 000000000..c654c2082 --- /dev/null +++ b/src/tests/governance/test_timelock.cairo @@ -0,0 +1,1799 @@ +use core::hash::{HashStateTrait, HashStateExTrait}; +use core::num::traits::Zero; +use core::pedersen::PedersenTrait; +use openzeppelin::access::accesscontrol::AccessControlComponent::{ + AccessControlImpl, InternalImpl as AccessControlInternalImpl +}; +use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; +use openzeppelin::access::accesscontrol::interface::IACCESSCONTROL_ID; +use openzeppelin::access::accesscontrol::interface::IAccessControl; +use openzeppelin::governance::timelock::OperationState; +use openzeppelin::governance::timelock::TimelockControllerComponent::{ + CallScheduled, CallExecuted, CallSalt, CallCancelled, MinDelayChanged +}; +use openzeppelin::governance::timelock::TimelockControllerComponent::{ + TimelockImpl, InternalImpl as TimelockInternalImpl +}; +use openzeppelin::governance::timelock::TimelockControllerComponent; +use openzeppelin::governance::timelock::interface::{ + TimelockABIDispatcher, TimelockABIDispatcherTrait +}; +use openzeppelin::governance::timelock::{PROPOSER_ROLE, EXECUTOR_ROLE, CANCELLER_ROLE}; +use openzeppelin::introspection::interface::ISRC5_ID; +use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; +use openzeppelin::tests::mocks::timelock_mocks::MockContract; +use openzeppelin::tests::mocks::timelock_mocks::{ + IMockContractDispatcher, IMockContractDispatcherTrait +}; +use openzeppelin::tests::mocks::timelock_mocks::{ + ITimelockAttackerDispatcher, ITimelockAttackerDispatcherTrait +}; +use openzeppelin::tests::mocks::timelock_mocks::{TimelockControllerMock, TimelockAttackerMock}; +use openzeppelin::tests::utils::constants::{ADMIN, ZERO, OTHER, SALT}; +use openzeppelin::tests::utils; +use openzeppelin::utils::selectors; +use openzeppelin::utils::serde::SerializedAppend; +use starknet::ContractAddress; +use starknet::account::Call; +use starknet::contract_address_const; +use starknet::testing; + +type ComponentState = + TimelockControllerComponent::ComponentState; + +fn CONTRACT_STATE() -> TimelockControllerMock::ContractState { + TimelockControllerMock::contract_state_for_testing() +} + +fn COMPONENT_STATE() -> ComponentState { + TimelockControllerComponent::component_state_for_testing() +} + +// +// Constants +// + +const MIN_DELAY: u64 = 1000; +const NEW_DELAY: u64 = 2000; +const VALUE: felt252 = 'VALUE'; +const NO_PREDECESSOR: felt252 = 0; + +// +// Addresses +// + +fn PROPOSER() -> ContractAddress { + contract_address_const::<'PROPOSER'>() +} + +fn EXECUTOR() -> ContractAddress { + contract_address_const::<'EXECUTOR'>() +} + +fn get_proposers() -> (ContractAddress, ContractAddress, ContractAddress) { + let p1 = contract_address_const::<'PROPOSER_1'>(); + let p2 = contract_address_const::<'PROPOSER_2'>(); + let p3 = contract_address_const::<'PROPOSER_3'>(); + (p1, p2, p3) +} + +fn get_executors() -> (ContractAddress, ContractAddress, ContractAddress) { + let e1 = contract_address_const::<'EXECUTOR_1'>(); + let e2 = contract_address_const::<'EXECUTOR_2'>(); + let e3 = contract_address_const::<'EXECUTOR_3'>(); + (e1, e2, e3) +} + +// +// Operations +// + +fn single_operation(target: ContractAddress) -> Call { + let mut calldata = array![]; + calldata.append_serde(VALUE); + + Call { to: target, selector: selector!("set_number"), calldata: calldata.span() } +} + +fn batched_operations(target: ContractAddress) -> Span { + let mut calls = array![]; + let call = single_operation(target); + calls.append(call); + calls.append(call); + calls.append(call); + + calls.span() +} + +fn failing_operation(target: ContractAddress) -> Call { + let mut calldata = array![]; + + Call { to: target, selector: selector!("failing_function"), calldata: calldata.span() } +} + +fn operation_with_bad_selector(target: ContractAddress) -> Call { + let mut calldata = array![]; + + Call { to: target, selector: selector!("bad_selector"), calldata: calldata.span() } +} + +// +// Dispatchers +// + +fn deploy_timelock() -> TimelockABIDispatcher { + let mut calldata = array![]; + + let proposers = array![PROPOSER()].span(); + let executors = array![EXECUTOR()].span(); + let admin = ADMIN(); + + calldata.append_serde(MIN_DELAY); + calldata.append_serde(proposers); + calldata.append_serde(executors); + calldata.append_serde(admin); + + let address = utils::deploy(TimelockControllerMock::TEST_CLASS_HASH, calldata); + // Events dropped: + // - 5 RoleGranted: self, proposer, canceller, executor, admin + // - MinDelayChanged + utils::drop_events(address, 6); + TimelockABIDispatcher { contract_address: address } +} + +fn deploy_mock_target() -> IMockContractDispatcher { + let mut calldata = array![]; + + let address = utils::deploy(MockContract::TEST_CLASS_HASH, calldata); + IMockContractDispatcher { contract_address: address } +} + +fn setup_dispatchers() -> (TimelockABIDispatcher, IMockContractDispatcher) { + let timelock = deploy_timelock(); + let target = deploy_mock_target(); + + (timelock, target) +} + +fn deploy_attacker() -> ITimelockAttackerDispatcher { + let mut calldata = array![]; + + let address = utils::deploy(TimelockAttackerMock::TEST_CLASS_HASH, calldata); + ITimelockAttackerDispatcher { contract_address: address } +} + +// +// hash_operation +// + +#[test] +fn test_hash_operation() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = 123; + let salt = SALT; + + // Set up call + let mut calldata = array![]; + calldata.append_serde(VALUE); + let mut call = Call { + to: target.contract_address, selector: selector!("set_number"), calldata: calldata.span() + }; + + // Hash operation + let hashed_operation = timelock.hash_operation(call, predecessor, salt); + + // Manually set hash elements + let mut expected_hash = PedersenTrait::new(0) + .update_with(target.contract_address) // call::to + .update_with(selector!("set_number")) // call::selector + .update_with(1) // call::calldata.len + .update_with(VALUE) // call::calldata::number + .update_with(predecessor) // predecessor + .update_with(salt) // salt + .finalize(); + + assert_eq!(hashed_operation, expected_hash); +} + +#[test] +fn test_hash_operation_batch() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = 123; + let salt = SALT; + + // Set up calls + let mut calldata = array![]; + calldata.append_serde(VALUE); + let mut call = Call { + to: target.contract_address, selector: selector!("set_number"), calldata: calldata.span() + }; + let calls = array![call, call, call].span(); + + // Hash operation + let hashed_operation = timelock.hash_operation_batch(calls, predecessor, salt); + + // Manually set hash elements + let mut expected_hash = PedersenTrait::new(0) + .update_with(3) // total number of Calls + .update_with(target.contract_address) // call::to + .update_with(selector!("set_number")) // call::selector + .update_with(1) // call::calldata.len + .update_with(VALUE) // call::calldata::number + .update_with(target.contract_address) // call::to + .update_with(selector!("set_number")) // call::selector + .update_with(1) // call::calldata.len + .update_with(VALUE) // call::calldata::number + .update_with(target.contract_address) // call::to + .update_with(selector!("set_number")) // call::selector + .update_with(1) // call::calldata.len + .update_with(VALUE) // call::calldata::number + .update_with(predecessor) // predecessor + .update_with(salt) // salt + .finalize(); + + assert_eq!(hashed_operation, expected_hash); +} + +// +// schedule +// + +fn schedule_from_proposer(salt: felt252) { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let delay = MIN_DELAY; + let mut salt = salt; + + // Set up call + let call = single_operation(target.contract_address); + let target_id = timelock.hash_operation(call, predecessor, salt); + assert_operation_state(timelock, OperationState::Unset, target_id); + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule(call, predecessor, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id); + + // Check timestamp + let operation_ts = timelock.get_timestamp(target_id); + let expected_ts = starknet::get_block_timestamp() + delay; + assert_eq!(operation_ts, expected_ts); + + // Check event(s) + let event_index = 0; + if salt != 0 { + assert_event_schedule( + timelock.contract_address, target_id, event_index, call, predecessor, delay + ); + assert_only_event_call_salt(timelock.contract_address, target_id, salt); + } else { + assert_only_event_schedule( + timelock.contract_address, target_id, event_index, call, predecessor, delay + ); + } +} + +#[test] +fn test_schedule_from_proposer_with_salt() { + let salt = SALT; + schedule_from_proposer(salt); +} + +#[test] +fn test_schedule_from_proposer_no_salt() { + let salt = 0; + schedule_from_proposer(salt); +} + +#[test] +#[should_panic(expected: ('Timelock: expected Unset op', 'ENTRYPOINT_FAILED'))] +fn test_schedule_overwrite() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = SALT; + let delay = MIN_DELAY; + + let call = single_operation(target.contract_address); + + testing::set_contract_address(PROPOSER()); + timelock.schedule(call, predecessor, salt, delay); + timelock.schedule(call, predecessor, salt, delay); +} + +#[test] +#[should_panic(expected: ('Caller is missing role', 'ENTRYPOINT_FAILED'))] +fn test_schedule_unauthorized() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = SALT; + let delay = MIN_DELAY; + + let call = single_operation(target.contract_address); + + testing::set_contract_address(OTHER()); + timelock.schedule(call, predecessor, salt, delay); +} + +#[test] +#[should_panic(expected: ('Timelock: insufficient delay', 'ENTRYPOINT_FAILED'))] +fn test_schedule_bad_min_delay() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = SALT; + let bad_delay = MIN_DELAY - 1; + + let call = single_operation(target.contract_address); + + testing::set_contract_address(PROPOSER()); + timelock.schedule(call, predecessor, salt, bad_delay); +} + +// +// schedule_batch +// + +fn schedule_batch_from_proposer(salt: felt252) { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let delay = MIN_DELAY; + let mut salt = salt; + + // Set up calls + let calls = batched_operations(target.contract_address); + let target_id = timelock.hash_operation_batch(calls, predecessor, salt); + assert_operation_state(timelock, OperationState::Unset, target_id); + + // Schedule batch + testing::set_contract_address(PROPOSER()); + timelock.schedule_batch(calls, predecessor, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id); + + // Check timestamp + let operation_ts = timelock.get_timestamp(target_id); + let expected_ts = starknet::get_block_timestamp() + delay; + assert_eq!(operation_ts, expected_ts); + + // Check events + if salt != 0 { + assert_events_schedule_batch( + timelock.contract_address, target_id, calls, predecessor, delay + ); + assert_only_event_call_salt(timelock.contract_address, target_id, salt); + } else { + assert_only_events_schedule_batch( + timelock.contract_address, target_id, calls, predecessor, delay + ); + } +} + +#[test] +fn test_schedule_batch_from_proposer_with_salt() { + let salt = SALT; + schedule_batch_from_proposer(salt); +} + +#[test] +fn test_schedule_batch_from_proposer_no_salt() { + let no_salt = 0; + schedule_batch_from_proposer(no_salt); +} + +#[test] +#[should_panic(expected: ('Timelock: expected Unset op', 'ENTRYPOINT_FAILED'))] +fn test_schedule_batch_overwrite() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = SALT; + let delay = MIN_DELAY; + + let calls = batched_operations(target.contract_address); + + testing::set_contract_address(PROPOSER()); + timelock.schedule_batch(calls, predecessor, salt, delay); + timelock.schedule_batch(calls, predecessor, salt, delay); +} + +#[test] +#[should_panic(expected: ('Caller is missing role', 'ENTRYPOINT_FAILED'))] +fn test_schedule_batch_unauthorized() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = SALT; + let delay = MIN_DELAY; + + let calls = batched_operations(target.contract_address); + + testing::set_contract_address(OTHER()); + timelock.schedule_batch(calls, predecessor, salt, delay); +} + +#[test] +#[should_panic(expected: ('Timelock: insufficient delay', 'ENTRYPOINT_FAILED'))] +fn test_schedule_batch_bad_min_delay() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = SALT; + let bad_delay = MIN_DELAY - 1; + + let calls = batched_operations(target.contract_address); + + testing::set_contract_address(PROPOSER()); + timelock.schedule_batch(calls, predecessor, salt, bad_delay); +} + +// +// execute +// + +#[test] +#[should_panic(expected: ('Timelock: expected Ready op', 'ENTRYPOINT_FAILED'))] +fn test_execute_when_not_scheduled() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + + let call = single_operation(target.contract_address); + + testing::set_contract_address(EXECUTOR()); + timelock.execute(call, predecessor, salt); +} + +#[test] +fn test_execute_when_scheduled() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + let event_index = 0; + + // Set up call + let call = single_operation(target.contract_address); + let target_id = timelock.hash_operation(call, predecessor, salt); + assert_operation_state(timelock, OperationState::Unset, target_id); + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule(call, predecessor, salt, delay); + assert_only_event_schedule( + timelock.contract_address, target_id, event_index, call, predecessor, delay + ); + assert_operation_state(timelock, OperationState::Waiting, target_id); + + // Fast-forward + testing::set_block_timestamp(delay); + assert_operation_state(timelock, OperationState::Ready, target_id); + + // Check initial target state + let check_target = target.get_number(); + assert_eq!(check_target, 0); + + // Execute + testing::set_contract_address(EXECUTOR()); + timelock.execute(call, predecessor, salt); + + assert_operation_state(timelock, OperationState::Done, target_id); + assert_only_event_execute(timelock.contract_address, target_id, event_index, call); + + // Check target state updates + let check_target = target.get_number(); + assert_eq!(check_target, VALUE); +} + +#[test] +#[should_panic(expected: ('Timelock: expected Ready op', 'ENTRYPOINT_FAILED'))] +fn test_execute_early() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + + let call = single_operation(target.contract_address); + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule(call, predecessor, salt, delay); + + // Fast-forward + let early_time = delay - 1; + testing::set_block_timestamp(early_time); + + // Execute + testing::set_contract_address(EXECUTOR()); + timelock.execute(call, predecessor, salt); +} + +#[test] +#[should_panic(expected: ('Caller is missing role', 'ENTRYPOINT_FAILED'))] +fn test_execute_unauthorized() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + + let call = single_operation(target.contract_address); + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule(call, predecessor, salt, delay); + + // Fast-forward + testing::set_block_timestamp(delay); + + // Execute + testing::set_contract_address(OTHER()); + timelock.execute(call, predecessor, salt); +} + +#[test] +#[should_panic(expected: ('Expected failure', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED'))] +fn test_execute_failing_tx() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + + // Set up call + let call = failing_operation(target.contract_address); + let target_id = timelock.hash_operation(call, predecessor, salt); + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule(call, predecessor, salt, delay); + + // Fast-forward + testing::set_block_timestamp(delay); + assert_operation_state(timelock, OperationState::Ready, target_id); + + // Execute + testing::set_contract_address(EXECUTOR()); + timelock.execute(call, predecessor, salt); +} + +#[test] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', 'ENTRYPOINT_FAILED'))] +fn test_execute_bad_selector() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + + // Set up call + let call = operation_with_bad_selector(target.contract_address); + let target_id = timelock.hash_operation(call, predecessor, salt); + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule(call, predecessor, salt, delay); + + // Fast-forward + testing::set_block_timestamp(delay); + assert_operation_state(timelock, OperationState::Ready, target_id); + + // Execute + testing::set_contract_address(EXECUTOR()); + timelock.execute(call, predecessor, salt); +} + +#[test] +#[should_panic( + expected: ( + 'Timelock: expected Ready op', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED' + ) +)] +fn test_execute_reentrant_call() { + let mut timelock = deploy_timelock(); + let mut attacker = deploy_attacker(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + + let reentrant_call = Call { + to: attacker.contract_address, selector: selector!("reenter"), calldata: array![].span() + }; + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule(reentrant_call, predecessor, salt, delay); + + // Fast-forward + testing::set_block_timestamp(delay); + + // Grant executor role to attacker + testing::set_contract_address(ADMIN()); + timelock.grant_role(EXECUTOR_ROLE, attacker.contract_address); + + // Attempt reentrant call + testing::set_contract_address(EXECUTOR()); + timelock.execute(reentrant_call, predecessor, salt); +} + +#[test] +#[should_panic(expected: ('Timelock: awaiting predecessor', 'ENTRYPOINT_FAILED'))] +fn test_execute_before_dependency() { + let (mut timelock, mut target) = setup_dispatchers(); + let salt = 0; + let delay = MIN_DELAY; + + // Call 1 + let call_1 = single_operation(target.contract_address); + let predecessor_1 = NO_PREDECESSOR; + let target_id_1 = timelock.hash_operation(call_1, predecessor_1, salt); + + // Call 2 + let call_2 = single_operation(target.contract_address); + let predecessor_2 = target_id_1; + let target_id_2 = timelock.hash_operation(call_2, predecessor_2, salt); + + // Schedule call 1 + testing::set_contract_address(PROPOSER()); + timelock.schedule(call_1, predecessor_1, salt, delay); + + // Schedule call 2 + timelock.schedule(call_2, predecessor_2, salt, delay); + + // Fast-forward + testing::set_block_timestamp(delay); + assert_operation_state(timelock, OperationState::Ready, target_id_1); + assert_operation_state(timelock, OperationState::Ready, target_id_2); + + // Execute + testing::set_contract_address(EXECUTOR()); + timelock.execute(call_2, predecessor_2, salt); +} + +#[test] +fn test_execute_after_dependency() { + let (mut timelock, mut target) = setup_dispatchers(); + let salt = 0; + let delay = MIN_DELAY; + let event_index = 0; + + // Call 1 + let call_1 = single_operation(target.contract_address); + let predecessor_1 = NO_PREDECESSOR; + let target_id_1 = timelock.hash_operation(call_1, predecessor_1, salt); + assert_operation_state(timelock, OperationState::Unset, target_id_1); + + // Call 2 + let call_2 = single_operation(target.contract_address); + let predecessor_2 = target_id_1; + let target_id_2 = timelock.hash_operation(call_2, predecessor_2, salt); + assert_operation_state(timelock, OperationState::Unset, target_id_2); + + // Schedule call 1 + testing::set_contract_address(PROPOSER()); + timelock.schedule(call_1, predecessor_1, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id_1); + assert_only_event_schedule( + timelock.contract_address, target_id_1, event_index, call_1, predecessor_1, delay + ); + + // Schedule call 2 + timelock.schedule(call_2, predecessor_2, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id_2); + assert_only_event_schedule( + timelock.contract_address, target_id_2, event_index, call_2, predecessor_2, delay + ); + + // Fast-forward + testing::set_block_timestamp(delay); + assert_operation_state(timelock, OperationState::Ready, target_id_1); + assert_operation_state(timelock, OperationState::Ready, target_id_2); + + // Execute call 1 + testing::set_contract_address(EXECUTOR()); + timelock.execute(call_1, predecessor_1, salt); + assert_operation_state(timelock, OperationState::Done, target_id_1); + assert_event_execute(timelock.contract_address, target_id_1, event_index, call_1); + + // Execute call 2 + timelock.execute(call_2, predecessor_2, salt); + assert_operation_state(timelock, OperationState::Done, target_id_2); + assert_only_event_execute(timelock.contract_address, target_id_2, event_index, call_2); +} + +// +// execute_batch +// + +#[test] +#[should_panic(expected: ('Timelock: expected Ready op', 'ENTRYPOINT_FAILED'))] +fn test_execute_batch_when_not_scheduled() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + + let calls = batched_operations(target.contract_address); + + testing::set_contract_address(EXECUTOR()); + timelock.execute_batch(calls, predecessor, salt); +} + +#[test] +fn test_execute_batch_when_scheduled() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + + // Set up call + let calls = batched_operations(target.contract_address); + let target_id = timelock.hash_operation_batch(calls, predecessor, salt); + assert_operation_state(timelock, OperationState::Unset, target_id); + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule_batch(calls, predecessor, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id); + assert_only_events_schedule_batch( + timelock.contract_address, target_id, calls, predecessor, delay + ); + + // Fast-forward + testing::set_block_timestamp(delay); + assert_operation_state(timelock, OperationState::Ready, target_id); + + // Check initial target state + let check_target = target.get_number(); + assert_eq!(check_target, 0); + + // Execute + testing::set_contract_address(EXECUTOR()); + timelock.execute_batch(calls, predecessor, salt); + assert_operation_state(timelock, OperationState::Done, target_id); + assert_only_events_execute_batch(timelock.contract_address, target_id, calls); + + // Check target state updates + let check_target = target.get_number(); + assert_eq!(check_target, VALUE); +} + +#[test] +#[should_panic(expected: ('Timelock: expected Ready op', 'ENTRYPOINT_FAILED'))] +fn test_execute_batch_early() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + + let calls = batched_operations(target.contract_address); + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule_batch(calls, predecessor, salt, delay); + + // Fast-forward + let early_time = delay - 1; + testing::set_block_timestamp(early_time); + + // Execute + testing::set_contract_address(EXECUTOR()); + timelock.execute_batch(calls, predecessor, salt); +} + +#[test] +#[should_panic(expected: ('Caller is missing role', 'ENTRYPOINT_FAILED'))] +fn test_execute_batch_unauthorized() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + + let calls = batched_operations(target.contract_address); + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule_batch(calls, predecessor, salt, delay); + + // Fast-forward + testing::set_block_timestamp(delay); + + // Execute + testing::set_contract_address(OTHER()); + timelock.execute_batch(calls, predecessor, salt); +} + +#[test] +#[should_panic( + expected: ( + 'Timelock: expected Ready op', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED' + ) +)] +fn test_execute_batch_reentrant_call() { + let mut timelock = deploy_timelock(); + let mut attacker = deploy_attacker(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + + let reentrant_call = Call { + to: attacker.contract_address, + selector: selector!("reenter_batch"), + calldata: array![].span() + }; + let calls = array![reentrant_call].span(); + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule_batch(calls, predecessor, salt, delay); + + // Fast-forward + testing::set_block_timestamp(delay); + + // Grant executor role to attacker + testing::set_contract_address(ADMIN()); + timelock.grant_role(EXECUTOR_ROLE, attacker.contract_address); + + // Attempt reentrant call + testing::set_contract_address(EXECUTOR()); + timelock.execute_batch(calls, predecessor, salt); +} + +#[test] +#[should_panic(expected: ('Expected failure', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED'))] +fn test_execute_batch_partial_execution() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + + let good_call = single_operation(target.contract_address); + let bad_call = failing_operation(target.contract_address); + let calls = array![good_call, bad_call].span(); + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule_batch(calls, predecessor, salt, delay); + + // Fast-forward + testing::set_block_timestamp(delay); + + // Execute + testing::set_contract_address(EXECUTOR()); + timelock.execute_batch(calls, predecessor, salt); +} + +#[test] +#[should_panic(expected: ('Timelock: awaiting predecessor', 'ENTRYPOINT_FAILED'))] +fn test_execute_batch_before_dependency() { + let (mut timelock, mut target) = setup_dispatchers(); + let salt = 0; + let delay = MIN_DELAY; + + // Calls 1 + let calls_1 = batched_operations(target.contract_address); + let predecessor_1 = NO_PREDECESSOR; + let target_id_1 = timelock.hash_operation_batch(calls_1, predecessor_1, salt); + + // Calls 2 + let calls_2 = batched_operations(target.contract_address); + let predecessor_2 = target_id_1; + + // Schedule calls 1 + testing::set_contract_address(PROPOSER()); + timelock.schedule_batch(calls_1, predecessor_1, salt, delay); + + // Schedule calls 2 + timelock.schedule_batch(calls_2, predecessor_2, salt, delay); + + // Fast-forward + testing::set_block_timestamp(delay); + + // Execute + testing::set_contract_address(EXECUTOR()); + timelock.execute_batch(calls_2, predecessor_2, salt); +} + +#[test] +fn test_execute_batch_after_dependency() { + let (mut timelock, mut target) = setup_dispatchers(); + let salt = 0; + let delay = MIN_DELAY; + + // Calls 1 + let calls_1 = batched_operations(target.contract_address); + let predecessor_1 = NO_PREDECESSOR; + let target_id_1 = timelock.hash_operation_batch(calls_1, predecessor_1, salt); + assert_operation_state(timelock, OperationState::Unset, target_id_1); + + // Calls 2 + let calls_2 = batched_operations(target.contract_address); + let predecessor_2 = target_id_1; + let target_id_2 = timelock.hash_operation_batch(calls_2, predecessor_2, salt); + assert_operation_state(timelock, OperationState::Unset, target_id_2); + + // Schedule calls 1 + testing::set_contract_address(PROPOSER()); + timelock.schedule_batch(calls_1, predecessor_1, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id_1); + assert_only_events_schedule_batch( + timelock.contract_address, target_id_1, calls_1, predecessor_1, delay + ); + + // Schedule calls 2 + timelock.schedule_batch(calls_2, predecessor_2, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id_2); + assert_only_events_schedule_batch( + timelock.contract_address, target_id_2, calls_2, predecessor_2, delay + ); + + // Fast-forward + testing::set_block_timestamp(delay); + assert_operation_state(timelock, OperationState::Ready, target_id_1); + assert_operation_state(timelock, OperationState::Ready, target_id_2); + + // Execute calls 1 + testing::set_contract_address(EXECUTOR()); + timelock.execute_batch(calls_1, predecessor_1, salt); + assert_only_events_execute_batch(timelock.contract_address, target_id_1, calls_1); + assert_operation_state(timelock, OperationState::Done, target_id_1); + + // Execute calls 2 + timelock.execute_batch(calls_2, predecessor_2, salt); + assert_operation_state(timelock, OperationState::Done, target_id_2); + assert_only_events_execute_batch(timelock.contract_address, target_id_2, calls_2); +} + +// +// cancel +// + +fn cancel_from_canceller(operation_state: OperationState) { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + let event_index = 0; + + let call = single_operation(target.contract_address); + let target_id = timelock.hash_operation(call, predecessor, salt); + assert_operation_state(timelock, OperationState::Unset, target_id); + + // Schedule + testing::set_contract_address(PROPOSER()); // PROPOSER is also CANCELLER + timelock.schedule(call, predecessor, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id); + assert_only_event_schedule( + timelock.contract_address, target_id, event_index, call, predecessor, delay + ); + + if operation_state == OperationState::Ready { + // Fast-forward + testing::set_block_timestamp(delay); + assert_operation_state(timelock, OperationState::Ready, target_id); + } + + // Cancel + timelock.cancel(target_id); + assert_only_event_cancel(timelock.contract_address, target_id); + assert_operation_state(timelock, OperationState::Unset, target_id); +} + +#[test] +fn test_cancel_when_waiting() { + let waiting = OperationState::Waiting; + cancel_from_canceller(waiting); +} + +#[test] +fn test_cancel_when_ready() { + let ready = OperationState::Waiting; + cancel_from_canceller(ready); +} + +#[test] +#[should_panic(expected: ('Timelock: expected Pending op', 'ENTRYPOINT_FAILED'))] +fn test_cancel_when_done() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + + let call = single_operation(target.contract_address); + let target_id = timelock.hash_operation(call, predecessor, salt); + assert_operation_state(timelock, OperationState::Unset, target_id); + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule(call, predecessor, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id); + + // Fast-forward + testing::set_block_timestamp(delay); + assert_operation_state(timelock, OperationState::Ready, target_id); + + // Execute + testing::set_contract_address(EXECUTOR()); + timelock.execute(call, predecessor, salt); + assert_operation_state(timelock, OperationState::Done, target_id); + + // Attempt cancel + testing::set_contract_address(PROPOSER()); // PROPOSER is also CANCELLER + timelock.cancel(target_id); +} + +#[test] +#[should_panic(expected: ('Timelock: expected Pending op', 'ENTRYPOINT_FAILED'))] +fn test_cancel_when_unset() { + let (mut timelock, _) = setup_dispatchers(); + let invalid_id = 0; + + // PROPOSER is also CANCELLER + testing::set_contract_address(PROPOSER()); + timelock.cancel(invalid_id); +} + +#[test] +#[should_panic(expected: ('Caller is missing role', 'ENTRYPOINT_FAILED'))] +fn test_cancel_unauthorized() { + let (mut timelock, mut target) = setup_dispatchers(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + + let call = single_operation(target.contract_address); + let target_id = timelock.hash_operation(call, predecessor, salt); + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule(call, predecessor, salt, delay); + + // Cancel + testing::set_contract_address(OTHER()); + timelock.cancel(target_id); +} + +// +// update_delay +// + +#[test] +#[should_panic(expected: ('Timelock: unauthorized caller', 'ENTRYPOINT_FAILED'))] +fn test_update_delay_unauthorized() { + let mut timelock = deploy_timelock(); + + timelock.update_delay(NEW_DELAY); +} + +#[test] +fn test_update_delay_scheduled() { + let mut timelock = deploy_timelock(); + let predecessor = NO_PREDECESSOR; + let salt = 0; + let delay = MIN_DELAY; + let event_index = 0; + + let call = Call { + to: timelock.contract_address, + selector: selector!("update_delay"), + calldata: array![NEW_DELAY.into()].span() + }; + let target_id = timelock.hash_operation(call, predecessor, salt); + + // Schedule + testing::set_contract_address(PROPOSER()); + timelock.schedule(call, predecessor, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id); + assert_only_event_schedule( + timelock.contract_address, target_id, event_index, call, predecessor, delay + ); + + // Fast-forward + testing::set_block_timestamp(delay); + + // Execute + testing::set_contract_address(EXECUTOR()); + timelock.execute(call, predecessor, salt); + assert_operation_state(timelock, OperationState::Done, target_id); + assert_event_delay(timelock.contract_address, MIN_DELAY, NEW_DELAY); + assert_only_event_execute(timelock.contract_address, target_id, event_index, call); + + // Check new minimum delay + let get_new_delay = timelock.get_min_delay(); + assert_eq!(get_new_delay, NEW_DELAY); +} + +// +// Internal +// + +// +// initializer +// + +#[test] +fn test_initializer_single_role_and_admin() { + let mut state = COMPONENT_STATE(); + let contract_state = CONTRACT_STATE(); + let min_delay = MIN_DELAY; + + let proposers = array![PROPOSER()].span(); + let executors = array![EXECUTOR()].span(); + let admin = ADMIN(); + + state.initializer(min_delay, proposers, executors, admin); + assert!(contract_state.has_role(PROPOSER_ROLE, *proposers.at(0))); + assert!(contract_state.has_role(CANCELLER_ROLE, *proposers.at(0))); + assert!(contract_state.has_role(EXECUTOR_ROLE, *executors.at(0))); + assert!(contract_state.has_role(DEFAULT_ADMIN_ROLE, admin)); +} + +#[test] +fn test_initializer_multiple_roles_and_admin() { + let mut state = COMPONENT_STATE(); + let contract_state = CONTRACT_STATE(); + let min_delay = MIN_DELAY; + + let (p1, p2, p3) = get_proposers(); + let mut proposers = array![p1, p2, p3].span(); + + let (e1, e2, e3) = get_executors(); + let mut executors = array![e1, e2, e3].span(); + + let admin = ADMIN(); + + state.initializer(min_delay, proposers, executors, admin); + + // Check assigned roles + assert!(contract_state.has_role(DEFAULT_ADMIN_ROLE, admin)); + + let mut index = 0; + loop { + if index == proposers.len() { + break; + } + + assert!(contract_state.has_role(PROPOSER_ROLE, *proposers.at(index))); + assert!(contract_state.has_role(CANCELLER_ROLE, *proposers.at(index))); + assert!(contract_state.has_role(EXECUTOR_ROLE, *executors.at(index))); + index += 1; + }; +} + +#[test] +fn test_initializer_no_admin() { + let mut state = COMPONENT_STATE(); + let contract_state = CONTRACT_STATE(); + let min_delay = MIN_DELAY; + + let proposers = array![PROPOSER()].span(); + let executors = array![EXECUTOR()].span(); + let admin_zero = ZERO(); + + // The initializer grants the timelock contract address the `DEFAULT_ADMIN_ROLE` + // therefore, we need to set the address since it's not deployed in this context + testing::set_contract_address(contract_address_const::<'TIMELOCK_ADDRESS'>()); + state.initializer(min_delay, proposers, executors, admin_zero); + + let admin_does_not_have_role = !contract_state.has_role(DEFAULT_ADMIN_ROLE, admin_zero); + assert!(admin_does_not_have_role); +} + +#[test] +fn test_initializer_supported_interfaces() { + let mut state = COMPONENT_STATE(); + let contract_state = CONTRACT_STATE(); + let min_delay = MIN_DELAY; + + let proposers = array![PROPOSER()].span(); + let executors = array![EXECUTOR()].span(); + let admin = ADMIN(); + + state.initializer(min_delay, proposers, executors, admin); + + // Check interface support + let supports_isrc5 = contract_state.src5.supports_interface(ISRC5_ID); + assert!(supports_isrc5); + + let supports_access_control = contract_state.src5.supports_interface(IACCESSCONTROL_ID); + assert!(supports_access_control); +} + +#[test] +fn test_initializer_min_delay() { + let mut state = COMPONENT_STATE(); + let min_delay = MIN_DELAY; + + let proposers = array![PROPOSER()].span(); + let executors = array![EXECUTOR()].span(); + let admin_zero = ZERO(); + + state.initializer(min_delay, proposers, executors, admin_zero); + + // Check minimum delay is set + let delay = state.get_min_delay(); + assert_eq!(delay, MIN_DELAY); + + // The initializer emits 4 `RoleGranted` events prior to `MinDelayChanged`: + // - Self administration + // - 1 proposer + // - 1 canceller + // - 1 executor + utils::drop_events(ZERO(), 4); + assert_only_event_delay_change(ZERO(), 0, MIN_DELAY); +} + +// +// assert_only_role_or_open_role +// + +#[test] +fn test_assert_only_role_or_open_role_when_has_role() { + let mut state = COMPONENT_STATE(); + let min_delay = MIN_DELAY; + + let proposers = array![PROPOSER()].span(); + let executors = array![EXECUTOR()].span(); + let admin = ADMIN(); + + state.initializer(min_delay, proposers, executors, admin); + + testing::set_caller_address(PROPOSER()); + state.assert_only_role_or_open_role(PROPOSER_ROLE); + + // PROPOSER == CANCELLER + testing::set_caller_address(PROPOSER()); + state.assert_only_role_or_open_role(CANCELLER_ROLE); + + testing::set_caller_address(EXECUTOR()); + state.assert_only_role_or_open_role(EXECUTOR_ROLE); +} + +#[test] +#[should_panic(expected: ('Caller is missing role',))] +fn test_assert_only_role_or_open_role_unauthorized() { + let mut state = COMPONENT_STATE(); + let min_delay = MIN_DELAY; + + let proposers = array![PROPOSER()].span(); + let executors = array![EXECUTOR()].span(); + let admin = ADMIN(); + + state.initializer(min_delay, proposers, executors, admin); + + testing::set_caller_address(OTHER()); + state.assert_only_role_or_open_role(PROPOSER_ROLE); +} + +#[test] +fn test_assert_only_role_or_open_role_with_open_role() { + let mut state = COMPONENT_STATE(); + let contract_state = CONTRACT_STATE(); + let min_delay = MIN_DELAY; + let open_role = ZERO(); + + let proposers = array![PROPOSER()].span(); + let executors = array![open_role].span(); + let admin = ADMIN(); + + state.initializer(min_delay, proposers, executors, admin); + + let is_open_role = contract_state.has_role(EXECUTOR_ROLE, open_role); + assert!(is_open_role); + + testing::set_caller_address(OTHER()); + state.assert_only_role_or_open_role(EXECUTOR_ROLE); +} + +// +// _before_call +// + +#[test] +fn test__before_call() { + let mut state = COMPONENT_STATE(); + let predecessor = NO_PREDECESSOR; + + // Mock targets + let target_id = 'TARGET_ID'; + let target_time = MIN_DELAY + starknet::get_block_timestamp(); + + // Set targets in storage + state.TimelockController_timestamps.write(target_id, target_time); + + // Fast-forward + testing::set_block_timestamp(target_time); + + state._before_call(target_id, predecessor); +} + +#[test] +#[should_panic(expected: ('Timelock: expected Ready op',))] +fn test__before_call_nonexistent_operation() { + let mut state = COMPONENT_STATE(); + let predecessor = NO_PREDECESSOR; + + // Mock targets + let target_id = 'TARGET_ID'; + let not_scheduled = 0; + + // Set targets in storage + state.TimelockController_timestamps.write(target_id, not_scheduled); + + state._before_call(target_id, predecessor); +} + +#[test] +#[should_panic(expected: ('Timelock: expected Ready op',))] +fn test__before_call_insufficient_time() { + let mut state = COMPONENT_STATE(); + let predecessor = NO_PREDECESSOR; + + // Mock targets + let target_id = 'TARGET_ID'; + let target_time = MIN_DELAY + starknet::get_block_timestamp(); + + // Set targets in storage + state.TimelockController_timestamps.write(target_id, target_time); + + // Fast-forward + testing::set_block_timestamp(target_time - 1); + + state._before_call(target_id, predecessor); +} + +#[test] +#[should_panic(expected: ('Timelock: expected Ready op',))] +fn test__before_call_when_already_done() { + let mut state = COMPONENT_STATE(); + let predecessor = NO_PREDECESSOR; + + // Mock targets + let target_id = 'TARGET_ID'; + let done_time = 1; + + // Set targets in storage + state.TimelockController_timestamps.write(target_id, done_time); + + // Fast-forward + testing::set_block_timestamp(done_time); + + state._before_call(target_id, predecessor); +} + +#[test] +fn test__before_call_with_predecessor_done() { + let mut state = COMPONENT_STATE(); + + // Mock `Done` predecessor + let predecessor_id = 'DONE'; + let done_time = 1; + + // Mock targets + let target_id = 'TARGET_ID'; + let target_time = MIN_DELAY + starknet::get_block_timestamp(); + + // Set targets in storage + state.TimelockController_timestamps.write(predecessor_id, done_time); + state.TimelockController_timestamps.write(target_id, target_time); + + // Fast-forward + testing::set_block_timestamp(target_time); + + state._before_call(target_id, predecessor_id); +} + +#[test] +#[should_panic(expected: ('Timelock: awaiting predecessor',))] +fn test__before_call_with_predecessor_not_done() { + let mut state = COMPONENT_STATE(); + + // Mock awaiting predecessor + let predecessor_id = 'DONE'; + let not_done_time = 2; + + // Mock targets + let target_id = 'TARGET_ID'; + let target_time = MIN_DELAY + starknet::get_block_timestamp(); + + // Set targets in storage + state.TimelockController_timestamps.write(predecessor_id, not_done_time); + state.TimelockController_timestamps.write(target_id, target_time); + + // Fast-forward + testing::set_block_timestamp(target_time); + + state._before_call(target_id, predecessor_id); +} + +// +// _after_call +// + +#[test] +fn test__after_call() { + let mut state = COMPONENT_STATE(); + + // Mock targets + let target_id = 'TARGET_ID'; + let target_time = MIN_DELAY + starknet::get_block_timestamp(); + + // Set targets in storage + state.TimelockController_timestamps.write(target_id, target_time); + + // Fast-forward + testing::set_block_timestamp(target_time); + + state._after_call(target_id); + + // Check timestamp is set to done (1) + let done_ts = 1; + let is_done = state.TimelockController_timestamps.read(target_id); + assert_eq!(is_done, done_ts); +} + +#[test] +#[should_panic(expected: ('Timelock: expected Ready op',))] +fn test__after_call_nonexistent_operation() { + let mut state = COMPONENT_STATE(); + + // Mock targets + let target_id = 'TARGET_ID'; + let not_scheduled = 0; + + // Set targets in storage + state.TimelockController_timestamps.write(target_id, not_scheduled); + + state._after_call(target_id); +} + +#[test] +#[should_panic(expected: ('Timelock: expected Ready op',))] +fn test__after_call_insufficient_time() { + let mut state = COMPONENT_STATE(); + + // Mock targets + let target_id = 'TARGET_ID'; + let target_time = MIN_DELAY + starknet::get_block_timestamp(); + + // Set targets in storage + state.TimelockController_timestamps.write(target_id, target_time); + + // Fast-forward + testing::set_block_timestamp(target_time - 1); + + state._after_call(target_id); +} + +#[test] +#[should_panic(expected: ('Timelock: expected Ready op',))] +fn test__after_call_already_done() { + let mut state = COMPONENT_STATE(); + + // Mock targets + let target_id = 'TARGET_ID'; + let done_time = 1; + + // Set targets in storage + state.TimelockController_timestamps.write(target_id, done_time); + + // Fast-forward + testing::set_block_timestamp(done_time); + + state._after_call(target_id); +} + +// +// _schedule +// + +#[test] +fn test__schedule() { + let mut state = COMPONENT_STATE(); + let mut target = deploy_mock_target(); + let predecessor = NO_PREDECESSOR; + let delay = MIN_DELAY; + let mut salt = 0; + + // Set up call + let call = single_operation(target.contract_address); + let target_id = state.hash_operation(call, predecessor, salt); + + // Schedule + state._schedule(target_id, delay); + + let actual_ts = state.TimelockController_timestamps.read(target_id); + let expected_ts = starknet::get_block_timestamp() + delay; + assert_eq!(actual_ts, expected_ts); +} + +#[test] +#[should_panic(expected: ('Timelock: expected Unset op',))] +fn test__schedule_overwrite() { + let mut state = COMPONENT_STATE(); + let mut target = deploy_mock_target(); + let predecessor = NO_PREDECESSOR; + let delay = MIN_DELAY; + let mut salt = 0; + + // Set up call + let call = single_operation(target.contract_address); + let target_id = state.hash_operation(call, predecessor, salt); + + // Schedule and overwrite + state._schedule(target_id, delay); + state._schedule(target_id, delay); +} + +#[test] +#[should_panic(expected: ('Timelock: insufficient delay',))] +fn test__schedule_bad_delay() { + let mut state = COMPONENT_STATE(); + let mut target = deploy_mock_target(); + let predecessor = NO_PREDECESSOR; + let mut salt = 0; + let delay = MIN_DELAY; + + // Set up call + let call = single_operation(target.contract_address); + let target_id = state.hash_operation(call, predecessor, salt); + + // Set min delay + state.TimelockController_min_delay.write(delay); + + // Schedule with bad delay + state._schedule(target_id, delay - 1); +} + +// +// _execute +// + +#[test] +fn test__execute() { + let mut state = COMPONENT_STATE(); + let mut target = deploy_mock_target(); + + // Set up call + let call = single_operation(target.contract_address); + + let storage_num = target.get_number(); + let expected_num = 0; + assert_eq!(storage_num, expected_num); + + // Execute + state._execute(call); + + let storage_num = target.get_number(); + let expected_num = VALUE; + assert_eq!(storage_num, expected_num); +} + +#[test] +#[should_panic(expected: ('Expected failure', 'ENTRYPOINT_FAILED',))] +fn test__execute_with_failing_tx() { + let mut state = COMPONENT_STATE(); + let mut target = deploy_mock_target(); + + // Set up call + let call = failing_operation(target.contract_address); + + // Execute failing tx + state._execute(call); +} + +#[test] +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] +fn test__execute_with_bad_selector() { + let mut state = COMPONENT_STATE(); + let mut target = deploy_mock_target(); + + // Set up call + let bad_selector_call = operation_with_bad_selector(target.contract_address); + + // Execute call with bad selector + state._execute(bad_selector_call); +} + +// +// Helpers +// + +fn assert_operation_state(timelock: TimelockABIDispatcher, exp_state: OperationState, id: felt252) { + let operation_state = timelock.get_operation_state(id); + assert_eq!(operation_state, exp_state); + + let is_operation = timelock.is_operation(id); + let is_pending = timelock.is_operation_pending(id); + let is_ready = timelock.is_operation_ready(id); + let is_done = timelock.is_operation_done(id); + + match exp_state { + OperationState::Unset => { + assert!(!is_operation); + assert!(!is_pending); + assert!(!is_ready); + assert!(!is_done); + }, + OperationState::Waiting => { + assert!(is_operation); + assert!(is_pending); + assert!(!is_ready); + assert!(!is_done); + }, + OperationState::Ready => { + assert!(is_operation); + assert!(is_pending); + assert!(is_ready); + assert!(!is_done); + }, + OperationState::Done => { + assert!(is_operation); + assert!(!is_pending); + assert!(!is_ready); + assert!(is_done); + } + }; +} + +// +// Event helpers +// + +// +// MinDelayChanged +// + +fn assert_event_delay_change(contract: ContractAddress, old_duration: u64, new_duration: u64) { + let event = utils::pop_log::(contract).unwrap(); + let expected = TimelockControllerComponent::Event::MinDelayChanged( + MinDelayChanged { old_duration, new_duration } + ); + assert!(event == expected); +} + +fn assert_only_event_delay_change(contract: ContractAddress, old_duration: u64, new_duration: u64) { + assert_event_delay_change(contract, old_duration, new_duration); + utils::assert_no_events_left(contract); +} + +// +// CallScheduled +// + +fn assert_event_schedule( + contract: ContractAddress, + id: felt252, + index: felt252, + call: Call, + predecessor: felt252, + delay: u64 +) { + let event = utils::pop_log::(contract).unwrap(); + let expected = TimelockControllerComponent::Event::CallScheduled( + CallScheduled { id, index, call, predecessor, delay } + ); + assert!(event == expected); + + // Check indexed keys + let mut indexed_keys = array![]; + indexed_keys.append_serde(selector!("CallScheduled")); + indexed_keys.append_serde(id); + indexed_keys.append_serde(index); + utils::assert_indexed_keys(event, indexed_keys.span()); +} + +fn assert_only_event_schedule( + contract: ContractAddress, + id: felt252, + index: felt252, + call: Call, + predecessor: felt252, + delay: u64 +) { + assert_event_schedule(contract, id, index, call, predecessor, delay); + utils::assert_no_events_left(contract); +} + +fn assert_events_schedule_batch( + contract: ContractAddress, id: felt252, calls: Span, predecessor: felt252, delay: u64 +) { + let mut i = 0; + loop { + if i == calls.len() { + break; + } + assert_event_schedule(contract, id, i.into(), *calls.at(i), predecessor, delay); + i += 1; + } +} + +fn assert_only_events_schedule_batch( + contract: ContractAddress, id: felt252, calls: Span, predecessor: felt252, delay: u64 +) { + assert_events_schedule_batch(contract, id, calls, predecessor, delay); + utils::assert_no_events_left(contract); +} + +// +// CallSalt +// + +fn assert_event_call_salt(contract: ContractAddress, id: felt252, salt: felt252) { + let event = utils::pop_log::(contract).unwrap(); + let expected = TimelockControllerComponent::Event::CallSalt(CallSalt { id, salt }); + assert!(event == expected); +} + +fn assert_only_event_call_salt(contract: ContractAddress, id: felt252, salt: felt252) { + assert_event_call_salt(contract, id, salt); + utils::assert_no_events_left(contract); +} + +// +// CallExecuted +// + +fn assert_event_execute(contract: ContractAddress, id: felt252, index: felt252, call: Call) { + let event = utils::pop_log::(contract).unwrap(); + let expected = TimelockControllerComponent::Event::CallExecuted( + CallExecuted { id, index, call } + ); + assert!(event == expected); + + // Check indexed keys + let mut indexed_keys = array![]; + indexed_keys.append_serde(selector!("CallExecuted")); + indexed_keys.append_serde(id); + indexed_keys.append_serde(index); + utils::assert_indexed_keys(event, indexed_keys.span()); +} + +fn assert_only_event_execute(contract: ContractAddress, id: felt252, index: felt252, call: Call) { + assert_event_execute(contract, id, index, call); + utils::assert_no_events_left(contract); +} + +fn assert_events_execute_batch(contract: ContractAddress, id: felt252, calls: Span) { + let mut i = 0; + loop { + if i == calls.len() { + break; + } + assert_event_execute(contract, id, i.into(), *calls.at(i)); + i += 1; + } +} + +fn assert_only_events_execute_batch(contract: ContractAddress, id: felt252, calls: Span) { + assert_events_execute_batch(contract, id, calls); + utils::assert_no_events_left(contract); +} + +// +// Cancelled +// + +fn assert_event_cancel(contract: ContractAddress, id: felt252) { + let event = utils::pop_log::(contract).unwrap(); + let expected = TimelockControllerComponent::Event::CallCancelled(CallCancelled { id }); + assert!(event == expected); + + // Check indexed keys + let mut indexed_keys = array![]; + indexed_keys.append_serde(selector!("CallCancelled")); + indexed_keys.append_serde(id); + utils::assert_indexed_keys(event, indexed_keys.span()); +} + +fn assert_only_event_cancel(contract: ContractAddress, id: felt252) { + assert_event_cancel(contract, id); + utils::assert_no_events_left(contract); +} + +// +// MinDelayChanged +// + +fn assert_event_delay(contract: ContractAddress, old_duration: u64, new_duration: u64) { + let event = utils::pop_log::(contract).unwrap(); + let expected = TimelockControllerComponent::Event::MinDelayChanged( + MinDelayChanged { old_duration, new_duration } + ); + assert!(event == expected); +} + +fn assert_only_event_delay(contract: ContractAddress, old_duration: u64, new_duration: u64) { + assert_event_delay(contract, old_duration, new_duration); + utils::assert_no_events_left(contract); +} diff --git a/src/tests/governance/test_utils.cairo b/src/tests/governance/test_utils.cairo new file mode 100644 index 000000000..874fe2615 --- /dev/null +++ b/src/tests/governance/test_utils.cairo @@ -0,0 +1,146 @@ +use openzeppelin::governance::timelock::utils::call_impls::CallPartialEq; +use starknet::account::Call; +use starknet::contract_address_const; + +// +// eq +// + +#[test] +fn test_eq_calls_no_calldata() { + let call_1 = Call { to: contract_address_const::<1>(), selector: 1, calldata: array![].span() }; + let call_2 = Call { to: contract_address_const::<1>(), selector: 1, calldata: array![].span() }; + assert_eq!(call_1, call_2); +} + +#[test] +fn test_eq_calls_with_calldata() { + let call_1 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![1, 2, 3].span() + }; + let call_2 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![1, 2, 3].span() + }; + assert_eq!(call_1, call_2); +} + +#[test] +#[should_panic] +fn test_eq_calls_ne_to() { + let call_1 = Call { to: contract_address_const::<1>(), selector: 1, calldata: array![].span() }; + let call_2 = Call { to: contract_address_const::<2>(), selector: 1, calldata: array![].span() }; + assert_eq!(call_1, call_2); +} + +#[test] +#[should_panic] +fn test_eq_calls_ne_selector() { + let call_1 = Call { to: contract_address_const::<1>(), selector: 1, calldata: array![].span() }; + let call_2 = Call { to: contract_address_const::<1>(), selector: 2, calldata: array![].span() }; + assert_eq!(call_1, call_2); +} + +#[test] +#[should_panic] +fn test_eq_calls_gt_calldata() { + let call_1 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![1].span() + }; + let call_2 = Call { to: contract_address_const::<1>(), selector: 2, calldata: array![].span() }; + assert_eq!(call_1, call_2); +} + +#[test] +#[should_panic] +fn test_eq_calls_lt_calldata() { + let call_1 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![1].span() + }; + let call_2 = Call { + to: contract_address_const::<1>(), selector: 2, calldata: array![1, 2].span() + }; + assert_eq!(call_1, call_2); +} + +#[test] +#[should_panic] +fn test_eq_calls_ne_calldata() { + let call_1 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![1, 2].span() + }; + let call_2 = Call { + to: contract_address_const::<1>(), selector: 2, calldata: array![2, 1].span() + }; + assert_eq!(call_1, call_2); +} + +// +// ne +// + +#[test] +fn test_ne_calls_to() { + let call_1 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![1, 2, 3].span() + }; + let call_2 = Call { + to: contract_address_const::<2>(), selector: 1, calldata: array![1, 2, 3].span() + }; + assert_ne!(call_1, call_2); +} + +#[test] +fn test_ne_calls_selector() { + let call_1 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![1, 2, 3].span() + }; + let call_2 = Call { + to: contract_address_const::<1>(), selector: 2, calldata: array![1, 2, 3].span() + }; + assert_ne!(call_1, call_2); +} + +#[test] +fn test_ne_calls_gt_calldata() { + let call_1 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![1, 2, 3].span() + }; + let call_2 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![1, 2].span() + }; + assert_ne!(call_1, call_2); +} + +#[test] +fn test_ne_calls_lt_calldata() { + let call_1 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![1, 2].span() + }; + let call_2 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![1, 2, 3].span() + }; + assert_ne!(call_1, call_2); +} + +#[test] +fn test_ne_calls_eq_len_calldata() { + let call_1 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![1, 2, 3].span() + }; + let call_2 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![3, 2, 1].span() + }; + assert_ne!(call_1, call_2); +} + +#[test] +#[should_panic] +fn test_ne_calls_when_eq() { + let call_1 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![1, 2, 3].span() + }; + let call_2 = Call { + to: contract_address_const::<1>(), selector: 1, calldata: array![1, 2, 3].span() + }; + assert_ne!(call_1, call_2); +} diff --git a/src/tests/mocks.cairo b/src/tests/mocks.cairo index 6e6e38156..daaf65baf 100644 --- a/src/tests/mocks.cairo +++ b/src/tests/mocks.cairo @@ -14,4 +14,5 @@ pub(crate) mod ownable_mocks; pub(crate) mod pausable_mocks; pub(crate) mod reentrancy_mocks; pub(crate) mod src5_mocks; +pub(crate) mod timelock_mocks; pub(crate) mod upgrades_mocks; diff --git a/src/tests/mocks/erc721_receiver_mocks.cairo b/src/tests/mocks/erc721_receiver_mocks.cairo index f496d0a9c..55ca3aebf 100644 --- a/src/tests/mocks/erc721_receiver_mocks.cairo +++ b/src/tests/mocks/erc721_receiver_mocks.cairo @@ -65,7 +65,7 @@ pub(crate) mod DualCaseERC721ReceiverMock { tokenId: u256, data: Span ) -> felt252 { - ExternalTrait::on_erc721_received(self, operator, from, tokenId, data) + Self::on_erc721_received(self, operator, from, tokenId, data) } } } diff --git a/src/tests/mocks/eth_account_mocks.cairo b/src/tests/mocks/eth_account_mocks.cairo index 687e5d68f..24161d3ba 100644 --- a/src/tests/mocks/eth_account_mocks.cairo +++ b/src/tests/mocks/eth_account_mocks.cairo @@ -2,7 +2,6 @@ pub(crate) mod DualCaseEthAccountMock { use openzeppelin::account::EthAccountComponent; use openzeppelin::account::interface::EthPublicKey; - use openzeppelin::account::utils::secp256k1::Secp256k1PointSerde; use openzeppelin::introspection::src5::SRC5Component; component!(path: EthAccountComponent, storage: eth_account, event: EthAccountEvent); @@ -47,7 +46,6 @@ pub(crate) mod DualCaseEthAccountMock { pub(crate) mod SnakeEthAccountMock { use openzeppelin::account::EthAccountComponent; use openzeppelin::account::interface::EthPublicKey; - use openzeppelin::account::utils::secp256k1::Secp256k1PointSerde; use openzeppelin::introspection::src5::SRC5Component; component!(path: EthAccountComponent, storage: eth_account, event: EthAccountEvent); @@ -88,7 +86,6 @@ pub(crate) mod SnakeEthAccountMock { pub(crate) mod CamelEthAccountMock { use openzeppelin::account::EthAccountComponent; use openzeppelin::account::interface::EthPublicKey; - use openzeppelin::account::utils::secp256k1::Secp256k1PointSerde; use openzeppelin::introspection::src5::SRC5Component; use starknet::account::Call; @@ -151,7 +148,6 @@ pub(crate) mod CamelEthAccountMock { #[starknet::contract] pub(crate) mod SnakeEthAccountPanicMock { use openzeppelin::account::interface::EthPublicKey; - use openzeppelin::account::utils::secp256k1::Secp256k1PointSerde; use starknet::SyscallResultTrait; use starknet::secp256_trait::Secp256Trait; @@ -193,7 +189,6 @@ pub(crate) mod SnakeEthAccountPanicMock { #[starknet::contract] pub(crate) mod CamelEthAccountPanicMock { use openzeppelin::account::interface::EthPublicKey; - use openzeppelin::account::utils::secp256k1::Secp256k1PointSerde; use starknet::SyscallResultTrait; use starknet::secp256_trait::Secp256Trait; diff --git a/src/tests/mocks/timelock_mocks.cairo b/src/tests/mocks/timelock_mocks.cairo new file mode 100644 index 000000000..a60a9da19 --- /dev/null +++ b/src/tests/mocks/timelock_mocks.cairo @@ -0,0 +1,151 @@ +#[starknet::contract] +pub(crate) mod TimelockControllerMock { + use openzeppelin::access::accesscontrol::AccessControlComponent; + use openzeppelin::governance::timelock::TimelockControllerComponent; + use openzeppelin::introspection::src5::SRC5Component; + use starknet::ContractAddress; + + component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: TimelockControllerComponent, storage: timelock, event: TimelockEvent); + + // Timelock Mixin + #[abi(embed_v0)] + impl TimelockMixinImpl = + TimelockControllerComponent::TimelockMixinImpl; + impl TimelockInternalImpl = TimelockControllerComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + access_control: AccessControlComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage, + #[substorage(v0)] + timelock: TimelockControllerComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccessControlEvent: AccessControlComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + #[flat] + TimelockEvent: TimelockControllerComponent::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + min_delay: u64, + proposers: Span, + executors: Span, + admin: ContractAddress + ) { + self.timelock.initializer(min_delay, proposers, executors, admin); + } +} + +#[starknet::interface] +pub(crate) trait IMockContract { + fn set_number(ref self: TState, new_number: felt252); + fn get_number(self: @TState) -> felt252; + fn failing_function(self: @TState); +} + +#[starknet::contract] +pub(crate) mod MockContract { + use super::IMockContract; + + #[storage] + struct Storage { + number: felt252, + } + + #[abi(embed_v0)] + impl MockContractImpl of IMockContract { + fn set_number(ref self: ContractState, new_number: felt252) { + self.number.write(new_number); + } + + fn get_number(self: @ContractState) -> felt252 { + self.number.read() + } + + fn failing_function(self: @ContractState) { + core::panic_with_felt252('Expected failure'); + } + } +} + +#[starknet::interface] +pub(crate) trait ITimelockAttacker { + fn reenter(ref self: TState); + fn reenter_batch(ref self: TState); +} + +#[starknet::contract] +pub(crate) mod TimelockAttackerMock { + use openzeppelin::governance::timelock::interface::{ + ITimelockDispatcher, ITimelockDispatcherTrait + }; + use starknet::ContractAddress; + use starknet::account::Call; + use super::ITimelockAttacker; + + const NO_PREDECESSOR: felt252 = 0; + const NO_SALT: felt252 = 0; + + #[storage] + struct Storage { + balance: felt252, + count: felt252 + } + + #[abi(embed_v0)] + impl TimelockAttackerImpl of ITimelockAttacker { + fn reenter(ref self: ContractState) { + let new_balance = self.balance.read() + 1; + self.balance.write(new_balance); + + let sender = starknet::get_caller_address(); + let this = starknet::get_contract_address(); + + let current_count = self.count.read(); + if current_count != 2 { + self.count.write(current_count + 1); + + let reentrant_call = Call { + to: this, selector: selector!("reenter"), calldata: array![].span() + }; + + let timelock = ITimelockDispatcher { contract_address: sender }; + timelock.execute(reentrant_call, NO_PREDECESSOR, NO_SALT); + } + } + + fn reenter_batch(ref self: ContractState) { + let new_balance = self.balance.read() + 1; + self.balance.write(new_balance); + + let sender = starknet::get_caller_address(); + let this = starknet::get_contract_address(); + + let current_count = self.count.read(); + if current_count != 2 { + self.count.write(current_count + 1); + + let reentrant_call = Call { + to: this, selector: selector!("reenter_batch"), calldata: array![].span() + }; + + let calls = array![reentrant_call].span(); + + let timelock = ITimelockDispatcher { contract_address: sender }; + timelock.execute_batch(calls, NO_PREDECESSOR, NO_SALT); + } + } + } +} diff --git a/src/tests/presets/test_eth_account.cairo b/src/tests/presets/test_eth_account.cairo index c30da1c7e..231e26c5b 100644 --- a/src/tests/presets/test_eth_account.cairo +++ b/src/tests/presets/test_eth_account.cairo @@ -1,8 +1,6 @@ use core::num::traits::Zero; use openzeppelin::account::interface::ISRC6_ID; -use openzeppelin::account::utils::secp256k1::{ - DebugSecp256k1Point, Secp256k1PointSerde, Secp256k1PointPartialEq -}; +use openzeppelin::account::utils::secp256k1::{DebugSecp256k1Point, Secp256k1PointPartialEq}; use openzeppelin::introspection::interface::ISRC5_ID; use openzeppelin::presets::EthAccountUpgradeable; use openzeppelin::presets::interfaces::eth_account::{ diff --git a/src/tests/security/test_reentrancyguard.cairo b/src/tests/security/test_reentrancyguard.cairo index 4a12bc918..d6de45ef0 100644 --- a/src/tests/security/test_reentrancyguard.cairo +++ b/src/tests/security/test_reentrancyguard.cairo @@ -4,7 +4,6 @@ use openzeppelin::tests::mocks::reentrancy_mocks::{ ReentrancyMock, IReentrancyMockDispatcher, IReentrancyMockDispatcherTrait }; use openzeppelin::tests::utils; -use starknet::storage::StorageMemberAccessTrait; type ComponentState = ReentrancyGuardComponent::ComponentState; diff --git a/src/tests/token/erc1155/test_erc1155.cairo b/src/tests/token/erc1155/test_erc1155.cairo index 951de7b70..110d93b68 100644 --- a/src/tests/token/erc1155/test_erc1155.cairo +++ b/src/tests/token/erc1155/test_erc1155.cairo @@ -1,5 +1,4 @@ use core::num::traits::Zero; -use core::starknet::storage::StorageMemberAccessTrait; use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; use openzeppelin::introspection; use openzeppelin::tests::mocks::erc1155_mocks::DualCaseERC1155Mock; diff --git a/src/tests/token/erc20/test_erc20_votes.cairo b/src/tests/token/erc20/test_erc20_votes.cairo index 269ec1d44..21f0d8ba2 100644 --- a/src/tests/token/erc20/test_erc20_votes.cairo +++ b/src/tests/token/erc20/test_erc20_votes.cairo @@ -24,7 +24,6 @@ use snforge_std::{ use snforge_std::{EventSpy, EventSpyAssertionsTrait}; use starknet::ContractAddress; use starknet::contract_address_const; -use starknet::storage::{StorageMapMemberAccessTrait, StorageMemberAccessTrait}; // // Setup @@ -304,7 +303,7 @@ fn test_delegate_by_sig_invalid_signature() { #[test] fn test_num_checkpoints() { - let state = setup(); + let state = @setup(); let mut trace = state.ERC20Votes_delegate_checkpoints.read(OWNER()); trace.push('ts1', 0x111); @@ -322,7 +321,7 @@ fn test_num_checkpoints() { #[test] fn test_checkpoints() { - let state = setup(); + let state = @setup(); let mut trace = state.ERC20Votes_delegate_checkpoints.read(OWNER()); trace.push('ts1', 0x111); diff --git a/src/tests/token/erc721/test_erc721.cairo b/src/tests/token/erc721/test_erc721.cairo index 8055d9c17..b7fb9c996 100644 --- a/src/tests/token/erc721/test_erc721.cairo +++ b/src/tests/token/erc721/test_erc721.cairo @@ -14,7 +14,6 @@ use openzeppelin::token::erc721::ERC721Component; use openzeppelin::token::erc721; use snforge_std::{spy_events, test_address, start_cheat_caller_address}; use starknet::ContractAddress; -use starknet::storage::StorageMapMemberAccessTrait; use super::common::ERC721SpyHelpers; diff --git a/src/token/erc1155/dual1155.cairo b/src/token/erc1155/dual1155.cairo index b0aac8f8b..a6644df67 100644 --- a/src/token/erc1155/dual1155.cairo +++ b/src/token/erc1155/dual1155.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc1155/dual1155.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc1155/dual1155.cairo) use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; diff --git a/src/token/erc1155/dual1155_receiver.cairo b/src/token/erc1155/dual1155_receiver.cairo index 24d35834f..e53df5d3c 100644 --- a/src/token/erc1155/dual1155_receiver.cairo +++ b/src/token/erc1155/dual1155_receiver.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc1155/dual1155_receiver.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc1155/dual1155_receiver.cairo) use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; diff --git a/src/token/erc1155/erc1155.cairo b/src/token/erc1155/erc1155.cairo index 493207c40..f852af2a0 100644 --- a/src/token/erc1155/erc1155.cairo +++ b/src/token/erc1155/erc1155.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc1155/erc1155.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc1155/erc1155.cairo) use starknet::ContractAddress; @@ -21,11 +21,12 @@ pub mod ERC1155Component { use openzeppelin::token::erc1155::interface; use starknet::ContractAddress; use starknet::get_caller_address; + use starknet::storage::Map; #[storage] struct Storage { - ERC1155_balances: LegacyMap<(u256, ContractAddress), u256>, - ERC1155_operator_approvals: LegacyMap<(ContractAddress, ContractAddress), bool>, + ERC1155_balances: Map<(u256, ContractAddress), u256>, + ERC1155_operator_approvals: Map<(ContractAddress, ContractAddress), bool>, ERC1155_uri: ByteArray, } @@ -75,10 +76,11 @@ pub mod ERC1155Component { pub approved: bool } - /// Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. + /// Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic + /// URI. /// - /// If an `URI` event was emitted for `id`, the standard guarantees that `value` will equal the value - /// returned by `IERC1155MetadataURI::uri`. + /// If an `URI` event was emitted for `id`, the standard guarantees that `value` will equal the + /// value returned by `IERC1155MetadataURI::uri`. /// https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions #[derive(Drop, PartialEq, starknet::Event)] pub struct URI { @@ -158,19 +160,20 @@ pub mod ERC1155Component { break; } batch_balances - .append(ERC1155::balance_of(self, *accounts.at(index), *token_ids.at(index))); + .append(Self::balance_of(self, *accounts.at(index), *token_ids.at(index))); index += 1; }; batch_balances.span() } - /// Transfers ownership of `value` amount of `token_id` from `from` if `to` is either an account or `IERC1155Receiver`. + /// Transfers ownership of `value` amount of `token_id` from `from` if `to` is either an + /// account or `IERC1155Receiver`. /// /// `data` is additional data, it has no specified format and it is passed to `to`. /// - /// WARNING: This function can potentially allow a reentrancy attack when transferring tokens - /// to an untrusted contract, when invoking `on_ERC1155_received` on the receiver. + /// WARNING: This function can potentially allow a reentrancy attack when transferring + /// tokens to an untrusted contract, when invoking `on_ERC1155_received` on the receiver. /// Ensure to follow the checks-effects-interactions pattern and consider employing /// reentrancy guards when interacting with untrusted contracts. /// @@ -179,7 +182,8 @@ pub mod ERC1155Component { /// - Caller is either approved or the `token_id` owner. /// - `from` is not the zero address. /// - `to` is not the zero address. - /// - If `to` refers to a non-account contract, it must implement `IERC1155Receiver::on_ERC1155_received` + /// - If `to` refers to a non-account contract, it must implement + /// `IERC1155Receiver::on_ERC1155_received` /// and return the required magic value. /// /// Emits a `TransferSingle` event. @@ -193,13 +197,14 @@ pub mod ERC1155Component { ) { let token_ids = array![token_id].span(); let values = array![value].span(); - ERC1155::safe_batch_transfer_from(ref self, from, to, token_ids, values, data) + Self::safe_batch_transfer_from(ref self, from, to, token_ids, values, data) } /// Batched version of `safe_transfer_from`. /// - /// WARNING: This function can potentially allow a reentrancy attack when transferring tokens - /// to an untrusted contract, when invoking `on_ERC1155_batch_received` on the receiver. + /// WARNING: This function can potentially allow a reentrancy attack when transferring + /// tokens to an untrusted contract, when invoking `on_ERC1155_batch_received` on the + /// receiver. /// Ensure to follow the checks-effects-interactions pattern and consider employing /// reentrancy guards when interacting with untrusted contracts. /// @@ -209,10 +214,12 @@ pub mod ERC1155Component { /// - `from` is not the zero address. /// - `to` is not the zero address. /// - `token_ids` and `values` must have the same length. - /// - If `to` refers to a non-account contract, it must implement `IERC1155Receiver::on_ERC1155_batch_received` + /// - If `to` refers to a non-account contract, it must implement + /// `IERC1155Receiver::on_ERC1155_batch_received` /// and return the acceptance magic value. /// - /// Emits either a `TransferSingle` or a `TransferBatch` event, depending on the length of the array arguments. + /// Emits either a `TransferSingle` or a `TransferBatch` event, depending on the length of + /// the array arguments. fn safe_batch_transfer_from( ref self: ComponentState, from: starknet::ContractAddress, @@ -226,7 +233,7 @@ pub mod ERC1155Component { let operator = get_caller_address(); if from != operator { - assert(ERC1155::is_approved_for_all(@self, from, operator), Errors::UNAUTHORIZED); + assert(Self::is_approved_for_all(@self, from, operator), Errors::UNAUTHORIZED); } self.update_with_acceptance_check(from, to, token_ids, values, data); @@ -483,7 +490,8 @@ pub mod ERC1155Component { /// Requirements: /// /// - `to` cannot be the zero address. - /// - If `to` refers to a smart contract, it must implement `IERC1155Receiver::on_ERC1155_received` + /// - If `to` refers to a smart contract, it must implement + /// `IERC1155Receiver::on_ERC1155_received` /// and return the acceptance magic value. /// /// Emits a `TransferSingle` event. @@ -507,7 +515,8 @@ pub mod ERC1155Component { /// /// - `to` cannot be the zero address. /// - `token_ids` and `values` must have the same length. - /// - If `to` refers to a smart contract, it must implement `IERC1155Receiver::on_ERC1155_batch_received` + /// - If `to` refers to a smart contract, it must implement + /// `IERC1155Receiver::on_ERC1155_batch_received` /// and return the acceptance magic value. /// /// Emits a `TransferBatch` event. @@ -571,7 +580,8 @@ pub mod ERC1155Component { /// - `to` is either an account contract or supports the `IERC1155Receiver` interface. /// - `token_ids` and `values` must have the same length. /// - /// Emits a `TransferSingle` event if the arrays contain one element, and `TransferBatch` otherwise. + /// Emits a `TransferSingle` event if the arrays contain one element, and `TransferBatch` + /// otherwise. fn update_with_acceptance_check( ref self: ComponentState, from: ContractAddress, @@ -596,7 +606,8 @@ pub mod ERC1155Component { /// /// - `token_ids` and `values` must have the same length. /// - /// Emits a `TransferSingle` event if the arrays contain one element, and `TransferBatch` otherwise. + /// Emits a `TransferSingle` event if the arrays contain one element, and `TransferBatch` + /// otherwise. /// /// NOTE: This function can be extended using the `ERC1155HooksTrait`, to add /// functionality before and/or after the transfer, mint, or burn. diff --git a/src/token/erc1155/erc1155_receiver.cairo b/src/token/erc1155/erc1155_receiver.cairo index 48e7e7361..f9ac00b80 100644 --- a/src/token/erc1155/erc1155_receiver.cairo +++ b/src/token/erc1155/erc1155_receiver.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc1155/erc1155_receiver.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc1155/erc1155_receiver.cairo) /// # ERC1155Receiver Component /// diff --git a/src/token/erc1155/interface.cairo b/src/token/erc1155/interface.cairo index db7be1e27..6fb5bd109 100644 --- a/src/token/erc1155/interface.cairo +++ b/src/token/erc1155/interface.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc1155/interface.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc1155/interface.cairo) use starknet::ContractAddress; diff --git a/src/token/erc20/dual20.cairo b/src/token/erc20/dual20.cairo index 54b42697e..9165574d0 100644 --- a/src/token/erc20/dual20.cairo +++ b/src/token/erc20/dual20.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc20/dual20.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc20/dual20.cairo) use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; @@ -48,7 +48,7 @@ impl DualCaseERC20Impl of DualCaseERC20Trait { } fn total_supply(self: @DualCaseERC20) -> u256 { - let mut args = array![]; + let args = array![]; try_selector_with_fallback( *self.contract_address, selectors::total_supply, selectors::totalSupply, args.span() ) diff --git a/src/token/erc20/erc20.cairo b/src/token/erc20/erc20.cairo index 4cd568375..6d8e0f3cf 100644 --- a/src/token/erc20/erc20.cairo +++ b/src/token/erc20/erc20.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc20/erc20.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc20/erc20.cairo) use starknet::ContractAddress; @@ -9,7 +9,8 @@ use starknet::ContractAddress; /// non-standard implementations that can be used to create an ERC20 contract. This /// component is agnostic regarding how tokens are created, which means that developers /// must create their own token distribution mechanism. -/// See [the documentation](https://docs.openzeppelin.com/contracts-cairo/0.14.0/guides/erc20-supply) +/// See [the documentation] +/// (https://docs.openzeppelin.com/contracts-cairo/0.15.0-rc.0/guides/erc20-supply) /// for examples. #[starknet::component] pub mod ERC20Component { @@ -18,14 +19,15 @@ pub mod ERC20Component { use openzeppelin::token::erc20::interface; use starknet::ContractAddress; use starknet::get_caller_address; + use starknet::storage::Map; #[storage] struct Storage { ERC20_name: ByteArray, ERC20_symbol: ByteArray, ERC20_total_supply: u256, - ERC20_balances: LegacyMap, - ERC20_allowances: LegacyMap<(ContractAddress, ContractAddress), u256>, + ERC20_balances: Map, + ERC20_allowances: Map<(ContractAddress, ContractAddress), u256>, } #[event] @@ -294,7 +296,8 @@ pub mod ERC20Component { TContractState, +HasComponent, impl Hooks: ERC20HooksTrait > of InternalTrait { /// Initializes the contract by setting the token name and symbol. - /// To prevent reinitialization, this should only be used inside of a contract's constructor. + /// To prevent reinitialization, this should only be used inside of a contract's + /// constructor. fn initializer( ref self: ComponentState, name: ByteArray, symbol: ByteArray ) { @@ -330,8 +333,8 @@ pub mod ERC20Component { } - /// Transfers an `amount` of tokens from `from` to `to`, or alternatively mints (or burns) if `from` (or `to`) is - /// the zero address. + /// Transfers an `amount` of tokens from `from` to `to`, or alternatively mints (or burns) + /// if `from` (or `to`) is the zero address. /// /// NOTE: This function can be extended using the `ERC20HooksTrait`, to add /// functionality before and/or after the transfer, mint, or burn. diff --git a/src/token/erc20/extensions/erc20_votes.cairo b/src/token/erc20/extensions/erc20_votes.cairo index 069fd626c..0a9d2f50b 100644 --- a/src/token/erc20/extensions/erc20_votes.cairo +++ b/src/token/erc20/extensions/erc20_votes.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc20/extensions/erc20_votes.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc20/extensions/erc20_votes.cairo) use core::hash::{Hash, HashStateTrait, HashStateExTrait}; use core::poseidon::PoseidonTrait; @@ -8,11 +8,12 @@ use starknet::ContractAddress; /// # ERC20Votes Component /// -/// The ERC20Votes component tracks voting units from ERC20 balances, which are a measure of voting power that can be -/// transferred, and provides a system of vote delegation, where an account can delegate its voting units to a sort of -/// "representative" that will pool delegated voting units from different accounts and can then use it to vote in -/// decisions. In fact, voting units MUST be delegated in order to count as actual votes, and an account has to -/// delegate those votes to itself if it wishes to participate in decisions and does not have a trusted representative. +/// The ERC20Votes component tracks voting units from ERC20 balances, which are a measure of voting +/// power that can be transferred, and provides a system of vote delegation, where an account can +/// delegate its voting units to a sort of "representative" that will pool delegated voting units +/// from different accounts and can then use it to vote in decisions. In fact, voting units MUST be +/// delegated in order to count as actual votes, and an account has to delegate those votes to +/// itself if it wishes to participate in decisions and does not have a trusted representative. #[starknet::component] pub mod ERC20VotesComponent { use core::num::traits::Zero; @@ -24,12 +25,13 @@ pub mod ERC20VotesComponent { use openzeppelin::utils::nonces::NoncesComponent; use openzeppelin::utils::structs::checkpoint::{Checkpoint, Trace, TraceTrait}; use starknet::ContractAddress; + use starknet::storage::Map; use super::{Delegation, OffchainMessageHash, SNIP12Metadata}; #[storage] struct Storage { - ERC20Votes_delegatee: LegacyMap, - ERC20Votes_delegate_checkpoints: LegacyMap, + ERC20Votes_delegatee: Map, + ERC20Votes_delegate_checkpoints: Map, ERC20Votes_total_checkpoints: Trace } @@ -101,9 +103,10 @@ pub mod ERC20VotesComponent { /// /// - `timepoint` must be in the past. /// - /// NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. - /// Votes that have not been delegated are still part of total supply, even though they would not participate in a - /// vote. + /// NOTE: This value is the sum of all available votes, which is not necessarily the sum of + /// all delegated votes. + /// Votes that have not been delegated are still part of total supply, even though they + /// would not participate in a vote. fn get_past_total_supply(self: @ComponentState, timepoint: u64) -> u256 { let current_timepoint = starknet::get_block_timestamp(); assert(timepoint < current_timepoint, Errors::FUTURE_LOOKUP); @@ -127,7 +130,8 @@ pub mod ERC20VotesComponent { self._delegate(sender, delegatee); } - /// Delegates votes from the sender to `delegatee` through a SNIP12 message signature validation. + /// Delegates votes from the sender to `delegatee` through a SNIP12 message signature + /// validation. /// /// Requirements: /// diff --git a/src/token/erc20/interface.cairo b/src/token/erc20/interface.cairo index 44c61694f..74ed4c817 100644 --- a/src/token/erc20/interface.cairo +++ b/src/token/erc20/interface.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc20/interface.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc20/interface.cairo) use openzeppelin::utils::structs::checkpoint::Checkpoint; use starknet::ContractAddress; diff --git a/src/token/erc721/dual721.cairo b/src/token/erc721/dual721.cairo index 4c710f588..7b57c787c 100644 --- a/src/token/erc721/dual721.cairo +++ b/src/token/erc721/dual721.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc721/dual721.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc721/dual721.cairo) use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; diff --git a/src/token/erc721/dual721_receiver.cairo b/src/token/erc721/dual721_receiver.cairo index a41f9699b..bc6e74242 100644 --- a/src/token/erc721/dual721_receiver.cairo +++ b/src/token/erc721/dual721_receiver.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc721/dual721_receiver.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc721/dual721_receiver.cairo) use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 6f838205a..476ac76ee 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc721/erc721.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc721/erc721.cairo) use starknet::ContractAddress; @@ -21,15 +21,16 @@ pub mod ERC721Component { use openzeppelin::token::erc721::interface; use starknet::ContractAddress; use starknet::get_caller_address; + use starknet::storage::Map; #[storage] struct Storage { ERC721_name: ByteArray, ERC721_symbol: ByteArray, - ERC721_owners: LegacyMap, - ERC721_balances: LegacyMap, - ERC721_token_approvals: LegacyMap, - ERC721_operator_approvals: LegacyMap<(ContractAddress, ContractAddress), bool>, + ERC721_owners: Map, + ERC721_balances: Map, + ERC721_token_approvals: Map, + ERC721_operator_approvals: Map<(ContractAddress, ContractAddress), bool>, ERC721_base_uri: ByteArray } @@ -133,11 +134,13 @@ pub mod ERC721Component { self._require_owned(token_id) } - /// Transfers ownership of `token_id` from `from` if `to` is either an account or `IERC721Receiver`. + /// Transfers ownership of `token_id` from `from` if `to` is either an account or + /// `IERC721Receiver`. /// /// `data` is additional data, it has no specified format and it is sent in call to `to`. /// - /// WARNING: This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. + /// WARNING: This method makes an external call to the recipient contract, which can lead to + /// reentrancy vulnerabilities. /// /// Requirements: /// @@ -155,7 +158,7 @@ pub mod ERC721Component { token_id: u256, data: Span ) { - ERC721::transfer_from(ref self, from, to, token_id); + Self::transfer_from(ref self, from, to, token_id); assert( _check_on_erc721_received(from, to, token_id, data), Errors::SAFE_TRANSFER_FAILED ); @@ -163,7 +166,8 @@ pub mod ERC721Component { /// Transfers ownership of `token_id` from `from` to `to`. /// - /// WARNING: This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. + /// WARNING: This method may lead to the loss of tokens if `to` is not aware of the ERC721 + /// protocol. /// /// Requirements: /// @@ -181,8 +185,9 @@ pub mod ERC721Component { ) { assert(!to.is_zero(), Errors::INVALID_RECEIVER); - // Setting an "auth" arguments enables the `_is_authorized` check which verifies that the token exists - // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here. + // Setting an "auth" arguments enables the `_is_authorized` check which verifies that + // the token exists (from != 0). Therefore, it is not needed to verify that the return + // value is not 0 here. let previous_owner = self.update(to, token_id, get_caller_address()); assert(from == previous_owner, Errors::INVALID_SENDER); @@ -499,7 +504,8 @@ pub mod ERC721Component { /// /// Internal function without access restriction. /// - /// WARNING: This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. + /// WARNING: This method may lead to the loss of tokens if `to` is not aware of the ERC721 + /// protocol. /// /// Requirements: /// @@ -525,7 +531,8 @@ pub mod ERC721Component { /// Mints `token_id` and transfers it to `to`. /// Internal function without access restriction. /// - /// WARNING: This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. + /// WARNING: This method may lead to the loss of tokens if `to` is not aware of the ERC721 + /// protocol. /// /// Requirements: /// @@ -541,11 +548,13 @@ pub mod ERC721Component { assert(previous_owner.is_zero(), Errors::ALREADY_MINTED); } - /// Transfers ownership of `token_id` from `from` if `to` is either an account or `IERC721Receiver`. + /// Transfers ownership of `token_id` from `from` if `to` is either an account or + /// `IERC721Receiver`. /// /// `data` is additional data, it has no specified format and it is sent in call to `to`. /// - /// WARNING: This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. + /// WARNING: This method makes an external call to the recipient contract, which can lead to + /// reentrancy vulnerabilities. /// /// Requirements: /// @@ -572,7 +581,8 @@ pub mod ERC721Component { /// /// `data` is additional data, it has no specified format and it is sent in call to `to`. /// - /// WARNING: This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. + /// WARNING: This method makes an external call to the recipient contract, which can lead to + /// reentrancy vulnerabilities. /// /// Requirements: /// @@ -608,11 +618,13 @@ pub mod ERC721Component { assert(!previous_owner.is_zero(), Errors::INVALID_TOKEN_ID); } - /// Transfers `token_id` from its current owner to `to`, or alternatively mints (or burns) if the current owner - /// (or `to`) is the zero address. Returns the owner of the `token_id` before the update. + /// Transfers `token_id` from its current owner to `to`, or alternatively mints (or burns) + /// if the current owner (or `to`) is the zero address. Returns the owner of the `token_id` + /// before the update. /// - /// The `auth` argument is optional. If the value passed is non-zero, then this function will check that - /// `auth` is either the owner of the token, or approved to operate on the token (by the owner). + /// The `auth` argument is optional. If the value passed is non-zero, then this function + /// will check that `auth` is either the owner of the token, or approved to operate on the + /// token (by the owner). /// /// Emits a `Transfer` event. /// @@ -671,8 +683,9 @@ pub mod ERC721Component { /// Approve `to` to operate on `token_id` /// - /// The `auth` argument is optional. If the value passed is non-zero, then this function will check that `auth` is - /// either the owner of the token, or approved to operate on all tokens held by this owner. + /// The `auth` argument is optional. If the value passed is non-zero, then this function + /// will check that `auth` is either the owner of the token, or approved to operate on all + /// tokens held by this owner. /// /// Emits an `Approval` event. fn _approve( @@ -684,10 +697,11 @@ pub mod ERC721Component { self._approve_with_optional_event(to, token_id, auth, true); } - /// Variant of `_approve` with an optional flag to enable or disable the `Approval` event. The event is not - /// emitted in the context of transfers. + /// Variant of `_approve` with an optional flag to enable or disable the `Approval` event. + /// The event is not emitted in the context of transfers. /// - /// WARNING: If `auth` is zero and `emit_event` is false, this function will not check that the token exists. + /// WARNING: If `auth` is zero and `emit_event` is false, this function will not check that + /// the token exists. /// /// Requirements: /// @@ -744,7 +758,8 @@ pub mod ERC721Component { /// Base URI for computing `token_uri`. /// - /// If set, the resulting URI for each token will be the concatenation of the base URI and the token ID. + /// If set, the resulting URI for each token will be the concatenation of the base URI and + /// the token ID. /// Returns an empty `ByteArray` if not set. fn _base_uri(self: @ComponentState) -> ByteArray { self.ERC721_base_uri.read() @@ -753,8 +768,8 @@ pub mod ERC721Component { /// Returns whether `spender` is allowed to manage `owner`'s tokens, or `token_id` in /// particular (ignoring whether it is owned by `owner`). /// - /// WARNING: This function assumes that `owner` is the actual owner of `token_id` and does not verify this - /// assumption. + /// WARNING: This function assumes that `owner` is the actual owner of `token_id` and does + /// not verify this assumption. fn _is_authorized( self: @ComponentState, owner: ContractAddress, @@ -769,7 +784,8 @@ pub mod ERC721Component { || spender == ERC721::get_approved(self, token_id)) } - /// Checks if `spender` can operate on `token_id`, assuming the provided `owner` is the actual owner. + /// Checks if `spender` can operate on `token_id`, assuming the provided `owner` is the + /// actual owner. /// /// Requirements: /// @@ -777,8 +793,8 @@ pub mod ERC721Component { /// - `spender` cannot be the zero address. /// - `spender` must be the owner of `token_id` or be approved to operate on it. /// - /// WARNING: This function assumes that `owner` is the actual owner of `token_id` and does not verify this - /// assumption. + /// WARNING: This function assumes that `owner` is the actual owner of `token_id` and does + /// not verify this assumption. fn _check_authorized( self: @ComponentState, owner: ContractAddress, diff --git a/src/token/erc721/erc721_receiver.cairo b/src/token/erc721/erc721_receiver.cairo index bbc8db4ff..693f4d6fa 100644 --- a/src/token/erc721/erc721_receiver.cairo +++ b/src/token/erc721/erc721_receiver.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc721/erc721_receiver.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc721/erc721_receiver.cairo) /// # ERC721Receiver Component /// diff --git a/src/token/erc721/interface.cairo b/src/token/erc721/interface.cairo index c821677d5..26f4cecac 100644 --- a/src/token/erc721/interface.cairo +++ b/src/token/erc721/interface.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc721/interface.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (token/erc721/interface.cairo) use starknet::ContractAddress; diff --git a/src/upgrades/interface.cairo b/src/upgrades/interface.cairo index 43233f751..cc011515e 100644 --- a/src/upgrades/interface.cairo +++ b/src/upgrades/interface.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (upgrades/interface.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (upgrades/interface.cairo) use starknet::ClassHash; diff --git a/src/upgrades/upgradeable.cairo b/src/upgrades/upgradeable.cairo index 443182d29..c78d25476 100644 --- a/src/upgrades/upgradeable.cairo +++ b/src/upgrades/upgradeable.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (upgrades/upgradeable.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (upgrades/upgradeable.cairo) /// # Upgradeable Component /// @@ -25,7 +25,7 @@ pub mod UpgradeableComponent { pub class_hash: ClassHash } - mod Errors { + pub mod Errors { pub const INVALID_CLASS: felt252 = 'Class hash cannot be zero'; } diff --git a/src/utils.cairo b/src/utils.cairo index 666ec68dc..530d909b3 100644 --- a/src/utils.cairo +++ b/src/utils.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (utils.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (utils.cairo) pub mod cryptography; pub mod deployments; diff --git a/src/utils/cryptography/interface.cairo b/src/utils/cryptography/interface.cairo index 54d22f4cb..a80966140 100644 --- a/src/utils/cryptography/interface.cairo +++ b/src/utils/cryptography/interface.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (utils/cryptography/interface.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (utils/cryptography/interface.cairo) use starknet::ContractAddress; diff --git a/src/utils/cryptography/nonces.cairo b/src/utils/cryptography/nonces.cairo index 6358cefe6..40089d76a 100644 --- a/src/utils/cryptography/nonces.cairo +++ b/src/utils/cryptography/nonces.cairo @@ -1,17 +1,18 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (utils/cryptography/nonces.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (utils/cryptography/nonces.cairo) #[starknet::component] pub mod NoncesComponent { use openzeppelin::utils::interfaces::INonces; use starknet::ContractAddress; + use starknet::storage::Map; #[storage] struct Storage { - Nonces_nonces: LegacyMap + Nonces_nonces: Map } - mod Errors { + pub mod Errors { pub const INVALID_NONCE: felt252 = 'Nonces: invalid nonce'; } @@ -31,8 +32,9 @@ pub mod NoncesComponent { > of InternalTrait { /// Consumes a nonce, returns the current value, and increments nonce. fn use_nonce(ref self: ComponentState, owner: ContractAddress) -> felt252 { - // For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be - // decremented or reset. This guarantees that the nonce never overflows. + // For each account, the nonce has an initial value of 0, can only be incremented by + // one, and cannot be decremented or reset. This guarantees that the nonce never + // overflows. let nonce = self.Nonces_nonces.read(owner); self.Nonces_nonces.write(owner, nonce + 1); nonce diff --git a/src/utils/cryptography/snip12.cairo b/src/utils/cryptography/snip12.cairo index 9aa7dc7fe..0f09a51e2 100644 --- a/src/utils/cryptography/snip12.cairo +++ b/src/utils/cryptography/snip12.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (utils/cryptography/snip12.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (utils/cryptography/snip12.cairo) use core::hash::{Hash, HashStateTrait, HashStateExTrait}; use core::poseidon::PoseidonTrait; diff --git a/src/utils/deployments.cairo b/src/utils/deployments.cairo index 16a1f0519..0ca55135c 100644 --- a/src/utils/deployments.cairo +++ b/src/utils/deployments.cairo @@ -15,8 +15,11 @@ const L2_ADDRESS_UPPER_BOUND: felt252 = const CONTRACT_ADDRESS_PREFIX: felt252 = 'STARKNET_CONTRACT_ADDRESS'; /// Returns the contract address from a `deploy_syscall`. -/// `deployer_address` should be the zero address if the deployment is origin-independent (deployed from zero). -/// For more information, see https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/contract-address/ +/// `deployer_address` should be the zero address if the deployment is origin-independent (deployed +/// from zero). +/// For more information, see +/// +/// https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/contract-address/ pub fn calculate_contract_address_from_deploy_syscall( salt: felt252, class_hash: ClassHash, @@ -65,7 +68,8 @@ pub struct DeployerInfo { } /// Returns the calculated contract address for contracts deployed through the UDC. -/// Origin-independent deployments (deployed from zero) should pass `Option::None` as `deployer_info`. +/// Origin-independent deployments (deployed from zero) should pass `Option::None` as +/// `deployer_info`. pub fn calculate_contract_address_from_udc( salt: felt252, class_hash: ClassHash, diff --git a/src/utils/deployments/interface.cairo b/src/utils/deployments/interface.cairo index c80f10e4f..10231a5a9 100644 --- a/src/utils/deployments/interface.cairo +++ b/src/utils/deployments/interface.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (utils/universal_deployer/interface.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (utils/universal_deployer/interface.cairo) use starknet::ClassHash; use starknet::ContractAddress; diff --git a/src/utils/math.cairo b/src/utils/math.cairo index cc9960aef..7d80a075c 100644 --- a/src/utils/math.cairo +++ b/src/utils/math.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (utils/math.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (utils/math.cairo) use core::traits::Into; diff --git a/src/utils/selectors.cairo b/src/utils/selectors.cairo index 61e9758e0..2fafd30e8 100644 --- a/src/utils/selectors.cairo +++ b/src/utils/selectors.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (utils/selectors.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (utils/selectors.cairo) // // AccessControl diff --git a/src/utils/serde.cairo b/src/utils/serde.cairo index e08fe03d8..dfe6efcf3 100644 --- a/src/utils/serde.cairo +++ b/src/utils/serde.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (utils/serde.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (utils/serde.cairo) pub trait SerializedAppend { fn append_serde(ref self: Array, value: T); diff --git a/src/utils/structs/checkpoint.cairo b/src/utils/structs/checkpoint.cairo index 0a55edf9a..ec2ec0ab4 100644 --- a/src/utils/structs/checkpoint.cairo +++ b/src/utils/structs/checkpoint.cairo @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (utils/structs/checkpoint.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (utils/structs/checkpoint.cairo) -use core::integer::u32_sqrt; +use core::num::traits::Sqrt; use openzeppelin::utils::math; use starknet::storage_access::StorePacking; use super::storage_array::{StorageArray, StorageArrayTrait}; @@ -55,7 +55,7 @@ pub impl TraceImpl of TraceTrait { let mut high = len; if (len > 5) { - let mid = len - u32_sqrt(len).into(); + let mid = len - len.sqrt().into(); if (key < checkpoints.read_at(mid).key) { high = mid; } else { @@ -112,8 +112,8 @@ pub impl TraceImpl of TraceTrait { #[generate_trait] impl CheckpointImpl of CheckpointTrait { - /// Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint, - /// or by updating the last one. + /// Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a + /// new checkpoint, or by updating the last one. fn _insert(ref self: StorageArray, key: u64, value: u256) -> (u256, u256) { let pos = self.len(); @@ -138,9 +138,9 @@ impl CheckpointImpl of CheckpointTrait { } } - /// Returns the index of the last (most recent) checkpoint with the key lower than or equal to the search key, - /// or `high` if there is none. `low` and `high` define a section where to do the search, with - /// inclusive `low` and exclusive `high`. + /// Returns the index of the last (most recent) checkpoint with the key lower than or equal to + /// the search key, or `high` if there is none. `low` and `high` define a section where to do + /// the search, with inclusive `low` and exclusive `high`. fn _upper_binary_lookup(self: @StorageArray, key: u64, low: u32, high: u32) -> u32 { let mut _low = low; let mut _high = high; diff --git a/src/utils/structs/storage_array.cairo b/src/utils/structs/storage_array.cairo index 7b890cf37..5a35dfd5e 100644 --- a/src/utils/structs/storage_array.cairo +++ b/src/utils/structs/storage_array.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (utils/structs/storage_array.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (utils/structs/storage_array.cairo) use core::hash::{HashStateExTrait, HashStateTrait}; use core::poseidon::PoseidonTrait; diff --git a/src/utils/unwrap_and_cast.cairo b/src/utils/unwrap_and_cast.cairo index 775385cb1..0ab73c8f1 100644 --- a/src/utils/unwrap_and_cast.cairo +++ b/src/utils/unwrap_and_cast.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.14.0 (utils/unwrap_and_cast.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0-rc.0 (utils/unwrap_and_cast.cairo) use starknet::SyscallResult; use starknet::SyscallResultTrait; From e09af84c35a08295a6579d116288279a3000b68c Mon Sep 17 00:00:00 2001 From: Andrew Fleming Date: Wed, 31 Jul 2024 22:56:55 -0400 Subject: [PATCH 20/20] Migrate timelock tests (#1061) * Remove unnecessary `mut`, minor fixes (#1032) * clean up code * change array syntax * fix fmt * Improve SNIP12 examples (#1036) * docs: improve examples * refactor: remove empty line * Update docs/modules/ROOT/pages/guides/snip12.adoc Co-authored-by: Andrew Fleming --------- Co-authored-by: Andrew Fleming * Release v0.15.0-rc.0 (#1039) * feat: update CHANGELOG * Bump version to 0.15.0-rc.0 * docs: add missing header * feat: apply review suggestions --------- Co-authored-by: ericnordelo * Bump cairo to 2.7.0-rc.1 (#1025) * feat: migrate modules * feat: delete cairo_project.toml * feat: update tests * feat: apply review updates * fix: remove extra line * feat: add CHANGELOG entry * fix: CHANGELOG * Bump Scarb to 2.7.0-rc.2 (#1052) * bump scarb to 2.7.0-rc.2 * add changelog entry * update changelog entry * bump scarb in installation page * Add timelock component (#996) * start timelock comp draft * move timelock to own dir, finish drafting component * tmp: add utility impls * add timelock mock * add test mod for timelock * fix commnet * add constructor to timelock mock * set min_delay in initializer * start tests * fix fmt * fix schedule assertion * add schedule tests * fix fmt * remove unused import * fix errs, _before_call * start execute tests * fix fmt * fix after_call * add execute tests * add abi interface * add reentrancy mock for timelock * add tests for cancel and update_delay * fix hash_op test * add execute with predecessor test * add timelock utils, add operation_state debug impl * fix fmt * improve imports * improve _execute * add basic mock for tests * add tmp call struct * add batch fns to interface * add batch fns * refactor tests to use dedicated mock, add batch tests * fix fmt * remove use clause * add timelock mixin * fix interface name * improve event assertions * fix execute and schedule events * fix fmt * add safe token transfer tests * fix fmt * tidy up code * add descriptions to events * clean up code * inline CallPartialEq fns * start fn descriptions * remove comments * remove comment * fix fmt * add changelog entries * improve spacing * add line break to hash test * clean up tests * clean up tests * fix constants in attacker impl * add initializer helper, register access control support * add _before_call and _after_call tests * fix reentrant batch mock call * add _schedule and _execute tests * add timelock description * fix formatting * fix comments * fix comment * fmt * tidy up tests * remove batch helper fn * remove event from mocks * Apply suggestions from code review Co-authored-by: Eric Nordelo * update spdx * remove token receiver support * add additional cancel tests * Apply suggestions from code review Co-authored-by: Eric Nordelo * initializer: remove mut, use while loop * fix fmt * add assert_only_self fn * fix getter comments re: pending/waiting * add assert_only_role * add specific op errors * make event names consistent * fix test * remove serialization from HashCallImpl * remove unused components from mock * clean up code * update to 2.7.0-rc.1 * fix fmt * import Call from corelib * update spdx * fix fmt * move OperationState, derive debug * fix fmt * fix hash impls * add for loops * fix PartialEq, add tests * fix fmt * simplify mixin fns * switch Poseidon to Pedersen * make admin a req in initializer * update tests with admin * fix fmt * fix comment * undo changes * add no admin initializer test --------- Co-authored-by: Eric Nordelo * fix fmt * bump scarb to 2.7.0-rc.4 (#1064) * bump scarb to 2.7.0-rc.4 * add changelog * bump scarb in installation page * fix fmt * feat: update workflow and fix warning (#1066) * bump scarb * add tmp usc install in ci * fix changelog --------- Co-authored-by: Eric Nordelo Co-authored-by: ericnordelo Co-authored-by: JChoy --- src/tests/governance.cairo | 2 +- src/tests/governance/test_timelock.cairo | 695 ++++++++++++----------- 2 files changed, 365 insertions(+), 332 deletions(-) diff --git a/src/tests/governance.cairo b/src/tests/governance.cairo index 1d40503c0..3aa8297b3 100644 --- a/src/tests/governance.cairo +++ b/src/tests/governance.cairo @@ -1,2 +1,2 @@ -//mod test_timelock; +mod test_timelock; mod test_utils; diff --git a/src/tests/governance/test_timelock.cairo b/src/tests/governance/test_timelock.cairo index c654c2082..d3838ae21 100644 --- a/src/tests/governance/test_timelock.cairo +++ b/src/tests/governance/test_timelock.cairo @@ -29,14 +29,18 @@ use openzeppelin::tests::mocks::timelock_mocks::{ ITimelockAttackerDispatcher, ITimelockAttackerDispatcherTrait }; use openzeppelin::tests::mocks::timelock_mocks::{TimelockControllerMock, TimelockAttackerMock}; -use openzeppelin::tests::utils::constants::{ADMIN, ZERO, OTHER, SALT}; +use openzeppelin::tests::utils::constants::{ADMIN, ZERO, OTHER, SALT, FELT_VALUE as VALUE}; +use openzeppelin::tests::utils::events::EventSpyExt; use openzeppelin::tests::utils; -use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; +use snforge_std::EventSpy; +use snforge_std::{ + spy_events, test_address, start_cheat_caller_address, cheat_caller_address, + cheat_block_timestamp_global, CheatSpan +}; use starknet::ContractAddress; use starknet::account::Call; use starknet::contract_address_const; -use starknet::testing; type ComponentState = TimelockControllerComponent::ComponentState; @@ -55,7 +59,6 @@ fn COMPONENT_STATE() -> ComponentState { const MIN_DELAY: u64 = 1000; const NEW_DELAY: u64 = 2000; -const VALUE: felt252 = 'VALUE'; const NO_PREDECESSOR: felt252 = 0; // @@ -133,18 +136,14 @@ fn deploy_timelock() -> TimelockABIDispatcher { calldata.append_serde(executors); calldata.append_serde(admin); - let address = utils::deploy(TimelockControllerMock::TEST_CLASS_HASH, calldata); - // Events dropped: - // - 5 RoleGranted: self, proposer, canceller, executor, admin - // - MinDelayChanged - utils::drop_events(address, 6); + let address = utils::declare_and_deploy("TimelockControllerMock", calldata); TimelockABIDispatcher { contract_address: address } } fn deploy_mock_target() -> IMockContractDispatcher { let mut calldata = array![]; - let address = utils::deploy(MockContract::TEST_CLASS_HASH, calldata); + let address = utils::declare_and_deploy("MockContract", calldata); IMockContractDispatcher { contract_address: address } } @@ -158,7 +157,7 @@ fn setup_dispatchers() -> (TimelockABIDispatcher, IMockContractDispatcher) { fn deploy_attacker() -> ITimelockAttackerDispatcher { let mut calldata = array![]; - let address = utils::deploy(TimelockAttackerMock::TEST_CLASS_HASH, calldata); + let address = utils::declare_and_deploy("TimelockAttackerMock", calldata); ITimelockAttackerDispatcher { contract_address: address } } @@ -250,8 +249,10 @@ fn schedule_from_proposer(salt: felt252) { assert_operation_state(timelock, OperationState::Unset, target_id); // Schedule - testing::set_contract_address(PROPOSER()); + let mut spy = spy_events(); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule(call, predecessor, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id); // Check timestamp @@ -262,14 +263,16 @@ fn schedule_from_proposer(salt: felt252) { // Check event(s) let event_index = 0; if salt != 0 { - assert_event_schedule( - timelock.contract_address, target_id, event_index, call, predecessor, delay - ); - assert_only_event_call_salt(timelock.contract_address, target_id, salt); + spy + .assert_event_call_scheduled( + timelock.contract_address, target_id, event_index, call, predecessor, delay + ); + spy.assert_only_event_call_salt(timelock.contract_address, target_id, salt); } else { - assert_only_event_schedule( - timelock.contract_address, target_id, event_index, call, predecessor, delay - ); + spy + .assert_only_event_call_scheduled( + timelock.contract_address, target_id, event_index, call, predecessor, delay + ); } } @@ -286,7 +289,7 @@ fn test_schedule_from_proposer_no_salt() { } #[test] -#[should_panic(expected: ('Timelock: expected Unset op', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Timelock: expected Unset op',))] fn test_schedule_overwrite() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; @@ -295,13 +298,14 @@ fn test_schedule_overwrite() { let call = single_operation(target.contract_address); - testing::set_contract_address(PROPOSER()); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); + timelock.schedule(call, predecessor, salt, delay); timelock.schedule(call, predecessor, salt, delay); } #[test] -#[should_panic(expected: ('Caller is missing role', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is missing role',))] fn test_schedule_unauthorized() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; @@ -310,21 +314,21 @@ fn test_schedule_unauthorized() { let call = single_operation(target.contract_address); - testing::set_contract_address(OTHER()); + start_cheat_caller_address(timelock.contract_address, OTHER()); timelock.schedule(call, predecessor, salt, delay); } #[test] -#[should_panic(expected: ('Timelock: insufficient delay', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Timelock: insufficient delay',))] fn test_schedule_bad_min_delay() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; let salt = SALT; let bad_delay = MIN_DELAY - 1; - let call = single_operation(target.contract_address); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); - testing::set_contract_address(PROPOSER()); + let call = single_operation(target.contract_address); timelock.schedule(call, predecessor, salt, bad_delay); } @@ -344,8 +348,10 @@ fn schedule_batch_from_proposer(salt: felt252) { assert_operation_state(timelock, OperationState::Unset, target_id); // Schedule batch - testing::set_contract_address(PROPOSER()); + let mut spy = spy_events(); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule_batch(calls, predecessor, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id); // Check timestamp @@ -355,14 +361,16 @@ fn schedule_batch_from_proposer(salt: felt252) { // Check events if salt != 0 { - assert_events_schedule_batch( - timelock.contract_address, target_id, calls, predecessor, delay - ); - assert_only_event_call_salt(timelock.contract_address, target_id, salt); + spy + .assert_events_call_scheduled_batch( + timelock.contract_address, target_id, calls, predecessor, delay + ); + spy.assert_only_event_call_salt(timelock.contract_address, target_id, salt); } else { - assert_only_events_schedule_batch( - timelock.contract_address, target_id, calls, predecessor, delay - ); + spy + .assert_only_events_call_scheduled_batch( + timelock.contract_address, target_id, calls, predecessor, delay + ); } } @@ -379,7 +387,7 @@ fn test_schedule_batch_from_proposer_no_salt() { } #[test] -#[should_panic(expected: ('Timelock: expected Unset op', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Timelock: expected Unset op',))] fn test_schedule_batch_overwrite() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; @@ -388,36 +396,37 @@ fn test_schedule_batch_overwrite() { let calls = batched_operations(target.contract_address); - testing::set_contract_address(PROPOSER()); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); + timelock.schedule_batch(calls, predecessor, salt, delay); timelock.schedule_batch(calls, predecessor, salt, delay); } #[test] -#[should_panic(expected: ('Caller is missing role', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is missing role',))] fn test_schedule_batch_unauthorized() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; let salt = SALT; let delay = MIN_DELAY; - let calls = batched_operations(target.contract_address); + start_cheat_caller_address(timelock.contract_address, OTHER()); - testing::set_contract_address(OTHER()); + let calls = batched_operations(target.contract_address); timelock.schedule_batch(calls, predecessor, salt, delay); } #[test] -#[should_panic(expected: ('Timelock: insufficient delay', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Timelock: insufficient delay',))] fn test_schedule_batch_bad_min_delay() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; let salt = SALT; let bad_delay = MIN_DELAY - 1; - let calls = batched_operations(target.contract_address); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); - testing::set_contract_address(PROPOSER()); + let calls = batched_operations(target.contract_address); timelock.schedule_batch(calls, predecessor, salt, bad_delay); } @@ -426,15 +435,15 @@ fn test_schedule_batch_bad_min_delay() { // #[test] -#[should_panic(expected: ('Timelock: expected Ready op', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Timelock: expected Ready op',))] fn test_execute_when_not_scheduled() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; let salt = 0; - let call = single_operation(target.contract_address); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); - testing::set_contract_address(EXECUTOR()); + let call = single_operation(target.contract_address); timelock.execute(call, predecessor, salt); } @@ -444,7 +453,6 @@ fn test_execute_when_scheduled() { let predecessor = NO_PREDECESSOR; let salt = 0; let delay = MIN_DELAY; - let event_index = 0; // Set up call let call = single_operation(target.contract_address); @@ -452,15 +460,19 @@ fn test_execute_when_scheduled() { assert_operation_state(timelock, OperationState::Unset, target_id); // Schedule - testing::set_contract_address(PROPOSER()); + let mut spy = spy_events(); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule(call, predecessor, salt, delay); - assert_only_event_schedule( - timelock.contract_address, target_id, event_index, call, predecessor, delay - ); + + let event_index = 0; + spy + .assert_only_event_call_scheduled( + timelock.contract_address, target_id, event_index, call, predecessor, delay + ); assert_operation_state(timelock, OperationState::Waiting, target_id); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); assert_operation_state(timelock, OperationState::Ready, target_id); // Check initial target state @@ -468,11 +480,11 @@ fn test_execute_when_scheduled() { assert_eq!(check_target, 0); // Execute - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute(call, predecessor, salt); assert_operation_state(timelock, OperationState::Done, target_id); - assert_only_event_execute(timelock.contract_address, target_id, event_index, call); + spy.assert_only_event_call_executed(timelock.contract_address, target_id, event_index, call); // Check target state updates let check_target = target.get_number(); @@ -480,52 +492,52 @@ fn test_execute_when_scheduled() { } #[test] -#[should_panic(expected: ('Timelock: expected Ready op', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Timelock: expected Ready op',))] fn test_execute_early() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; let salt = 0; let delay = MIN_DELAY; - let call = single_operation(target.contract_address); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); // Schedule - testing::set_contract_address(PROPOSER()); + let call = single_operation(target.contract_address); timelock.schedule(call, predecessor, salt, delay); // Fast-forward let early_time = delay - 1; - testing::set_block_timestamp(early_time); + cheat_block_timestamp_global(early_time); // Execute - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute(call, predecessor, salt); } #[test] -#[should_panic(expected: ('Caller is missing role', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is missing role',))] fn test_execute_unauthorized() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; let salt = 0; let delay = MIN_DELAY; - let call = single_operation(target.contract_address); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); // Schedule - testing::set_contract_address(PROPOSER()); + let call = single_operation(target.contract_address); timelock.schedule(call, predecessor, salt, delay); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); // Execute - testing::set_contract_address(OTHER()); + start_cheat_caller_address(timelock.contract_address, OTHER()); timelock.execute(call, predecessor, salt); } #[test] -#[should_panic(expected: ('Expected failure', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Expected failure',))] fn test_execute_failing_tx() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; @@ -537,20 +549,21 @@ fn test_execute_failing_tx() { let target_id = timelock.hash_operation(call, predecessor, salt); // Schedule - testing::set_contract_address(PROPOSER()); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule(call, predecessor, salt, delay); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); assert_operation_state(timelock, OperationState::Ready, target_id); // Execute - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute(call, predecessor, salt); } #[test] -#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND', 'ENTRYPOINT_FAILED'))] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. +#[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_execute_bad_selector() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; @@ -562,24 +575,20 @@ fn test_execute_bad_selector() { let target_id = timelock.hash_operation(call, predecessor, salt); // Schedule - testing::set_contract_address(PROPOSER()); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule(call, predecessor, salt, delay); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); assert_operation_state(timelock, OperationState::Ready, target_id); // Execute - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute(call, predecessor, salt); } #[test] -#[should_panic( - expected: ( - 'Timelock: expected Ready op', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED' - ) -)] +#[should_panic(expected: ('Timelock: expected Ready op',))] fn test_execute_reentrant_call() { let mut timelock = deploy_timelock(); let mut attacker = deploy_attacker(); @@ -592,23 +601,23 @@ fn test_execute_reentrant_call() { }; // Schedule - testing::set_contract_address(PROPOSER()); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule(reentrant_call, predecessor, salt, delay); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); // Grant executor role to attacker - testing::set_contract_address(ADMIN()); + start_cheat_caller_address(timelock.contract_address, ADMIN()); timelock.grant_role(EXECUTOR_ROLE, attacker.contract_address); // Attempt reentrant call - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute(reentrant_call, predecessor, salt); } #[test] -#[should_panic(expected: ('Timelock: awaiting predecessor', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Timelock: awaiting predecessor',))] fn test_execute_before_dependency() { let (mut timelock, mut target) = setup_dispatchers(); let salt = 0; @@ -625,19 +634,19 @@ fn test_execute_before_dependency() { let target_id_2 = timelock.hash_operation(call_2, predecessor_2, salt); // Schedule call 1 - testing::set_contract_address(PROPOSER()); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule(call_1, predecessor_1, salt, delay); // Schedule call 2 timelock.schedule(call_2, predecessor_2, salt, delay); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); assert_operation_state(timelock, OperationState::Ready, target_id_1); assert_operation_state(timelock, OperationState::Ready, target_id_2); // Execute - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute(call_2, predecessor_2, salt); } @@ -661,35 +670,42 @@ fn test_execute_after_dependency() { assert_operation_state(timelock, OperationState::Unset, target_id_2); // Schedule call 1 - testing::set_contract_address(PROPOSER()); + let mut spy = spy_events(); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule(call_1, predecessor_1, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id_1); - assert_only_event_schedule( - timelock.contract_address, target_id_1, event_index, call_1, predecessor_1, delay - ); + spy + .assert_only_event_call_scheduled( + timelock.contract_address, target_id_1, event_index, call_1, predecessor_1, delay + ); // Schedule call 2 timelock.schedule(call_2, predecessor_2, salt, delay); assert_operation_state(timelock, OperationState::Waiting, target_id_2); - assert_only_event_schedule( - timelock.contract_address, target_id_2, event_index, call_2, predecessor_2, delay - ); + spy + .assert_only_event_call_scheduled( + timelock.contract_address, target_id_2, event_index, call_2, predecessor_2, delay + ); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); assert_operation_state(timelock, OperationState::Ready, target_id_1); assert_operation_state(timelock, OperationState::Ready, target_id_2); // Execute call 1 - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute(call_1, predecessor_1, salt); assert_operation_state(timelock, OperationState::Done, target_id_1); - assert_event_execute(timelock.contract_address, target_id_1, event_index, call_1); + spy.assert_event_call_executed(timelock.contract_address, target_id_1, event_index, call_1); // Execute call 2 timelock.execute(call_2, predecessor_2, salt); assert_operation_state(timelock, OperationState::Done, target_id_2); - assert_only_event_execute(timelock.contract_address, target_id_2, event_index, call_2); + spy + .assert_only_event_call_executed( + timelock.contract_address, target_id_2, event_index, call_2 + ); } // @@ -697,7 +713,7 @@ fn test_execute_after_dependency() { // #[test] -#[should_panic(expected: ('Timelock: expected Ready op', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Timelock: expected Ready op',))] fn test_execute_batch_when_not_scheduled() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; @@ -705,7 +721,7 @@ fn test_execute_batch_when_not_scheduled() { let calls = batched_operations(target.contract_address); - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute_batch(calls, predecessor, salt); } @@ -722,15 +738,18 @@ fn test_execute_batch_when_scheduled() { assert_operation_state(timelock, OperationState::Unset, target_id); // Schedule - testing::set_contract_address(PROPOSER()); + let mut spy = spy_events(); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule_batch(calls, predecessor, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id); - assert_only_events_schedule_batch( - timelock.contract_address, target_id, calls, predecessor, delay - ); + spy + .assert_only_events_call_scheduled_batch( + timelock.contract_address, target_id, calls, predecessor, delay + ); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); assert_operation_state(timelock, OperationState::Ready, target_id); // Check initial target state @@ -738,10 +757,10 @@ fn test_execute_batch_when_scheduled() { assert_eq!(check_target, 0); // Execute - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute_batch(calls, predecessor, salt); assert_operation_state(timelock, OperationState::Done, target_id); - assert_only_events_execute_batch(timelock.contract_address, target_id, calls); + spy.assert_only_events_call_executed_batch(timelock.contract_address, target_id, calls); // Check target state updates let check_target = target.get_number(); @@ -749,7 +768,7 @@ fn test_execute_batch_when_scheduled() { } #[test] -#[should_panic(expected: ('Timelock: expected Ready op', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Timelock: expected Ready op',))] fn test_execute_batch_early() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; @@ -759,20 +778,20 @@ fn test_execute_batch_early() { let calls = batched_operations(target.contract_address); // Schedule - testing::set_contract_address(PROPOSER()); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule_batch(calls, predecessor, salt, delay); // Fast-forward let early_time = delay - 1; - testing::set_block_timestamp(early_time); + cheat_block_timestamp_global(early_time); // Execute - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute_batch(calls, predecessor, salt); } #[test] -#[should_panic(expected: ('Caller is missing role', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is missing role',))] fn test_execute_batch_unauthorized() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; @@ -782,23 +801,19 @@ fn test_execute_batch_unauthorized() { let calls = batched_operations(target.contract_address); // Schedule - testing::set_contract_address(PROPOSER()); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule_batch(calls, predecessor, salt, delay); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); // Execute - testing::set_contract_address(OTHER()); + start_cheat_caller_address(timelock.contract_address, OTHER()); timelock.execute_batch(calls, predecessor, salt); } #[test] -#[should_panic( - expected: ( - 'Timelock: expected Ready op', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED' - ) -)] +#[should_panic(expected: ('Timelock: expected Ready op',))] fn test_execute_batch_reentrant_call() { let mut timelock = deploy_timelock(); let mut attacker = deploy_attacker(); @@ -814,23 +829,23 @@ fn test_execute_batch_reentrant_call() { let calls = array![reentrant_call].span(); // Schedule - testing::set_contract_address(PROPOSER()); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule_batch(calls, predecessor, salt, delay); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); // Grant executor role to attacker - testing::set_contract_address(ADMIN()); + start_cheat_caller_address(timelock.contract_address, ADMIN()); timelock.grant_role(EXECUTOR_ROLE, attacker.contract_address); // Attempt reentrant call - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute_batch(calls, predecessor, salt); } #[test] -#[should_panic(expected: ('Expected failure', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Expected failure',))] fn test_execute_batch_partial_execution() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; @@ -842,19 +857,19 @@ fn test_execute_batch_partial_execution() { let calls = array![good_call, bad_call].span(); // Schedule - testing::set_contract_address(PROPOSER()); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule_batch(calls, predecessor, salt, delay); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); // Execute - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute_batch(calls, predecessor, salt); } #[test] -#[should_panic(expected: ('Timelock: awaiting predecessor', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Timelock: awaiting predecessor',))] fn test_execute_batch_before_dependency() { let (mut timelock, mut target) = setup_dispatchers(); let salt = 0; @@ -870,17 +885,17 @@ fn test_execute_batch_before_dependency() { let predecessor_2 = target_id_1; // Schedule calls 1 - testing::set_contract_address(PROPOSER()); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule_batch(calls_1, predecessor_1, salt, delay); // Schedule calls 2 timelock.schedule_batch(calls_2, predecessor_2, salt, delay); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); // Execute - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute_batch(calls_2, predecessor_2, salt); } @@ -903,35 +918,42 @@ fn test_execute_batch_after_dependency() { assert_operation_state(timelock, OperationState::Unset, target_id_2); // Schedule calls 1 - testing::set_contract_address(PROPOSER()); + let mut spy = spy_events(); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule_batch(calls_1, predecessor_1, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id_1); - assert_only_events_schedule_batch( - timelock.contract_address, target_id_1, calls_1, predecessor_1, delay - ); + spy + .assert_only_events_call_scheduled_batch( + timelock.contract_address, target_id_1, calls_1, predecessor_1, delay + ); // Schedule calls 2 timelock.schedule_batch(calls_2, predecessor_2, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id_2); - assert_only_events_schedule_batch( - timelock.contract_address, target_id_2, calls_2, predecessor_2, delay - ); + spy + .assert_only_events_call_scheduled_batch( + timelock.contract_address, target_id_2, calls_2, predecessor_2, delay + ); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); assert_operation_state(timelock, OperationState::Ready, target_id_1); assert_operation_state(timelock, OperationState::Ready, target_id_2); // Execute calls 1 - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute_batch(calls_1, predecessor_1, salt); - assert_only_events_execute_batch(timelock.contract_address, target_id_1, calls_1); + + spy.assert_only_events_call_executed_batch(timelock.contract_address, target_id_1, calls_1); assert_operation_state(timelock, OperationState::Done, target_id_1); // Execute calls 2 timelock.execute_batch(calls_2, predecessor_2, salt); + assert_operation_state(timelock, OperationState::Done, target_id_2); - assert_only_events_execute_batch(timelock.contract_address, target_id_2, calls_2); + spy.assert_only_events_call_executed_batch(timelock.contract_address, target_id_2, calls_2); } // @@ -950,22 +972,26 @@ fn cancel_from_canceller(operation_state: OperationState) { assert_operation_state(timelock, OperationState::Unset, target_id); // Schedule - testing::set_contract_address(PROPOSER()); // PROPOSER is also CANCELLER + let mut spy = spy_events(); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); // PROPOSER is also CANCELLER timelock.schedule(call, predecessor, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id); - assert_only_event_schedule( - timelock.contract_address, target_id, event_index, call, predecessor, delay - ); + spy + .assert_only_event_call_scheduled( + timelock.contract_address, target_id, event_index, call, predecessor, delay + ); if operation_state == OperationState::Ready { // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); assert_operation_state(timelock, OperationState::Ready, target_id); } // Cancel timelock.cancel(target_id); - assert_only_event_cancel(timelock.contract_address, target_id); + + spy.assert_only_event_call_cancelled(timelock.contract_address, target_id); assert_operation_state(timelock, OperationState::Unset, target_id); } @@ -982,7 +1008,7 @@ fn test_cancel_when_ready() { } #[test] -#[should_panic(expected: ('Timelock: expected Pending op', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Timelock: expected Pending op',))] fn test_cancel_when_done() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; @@ -994,37 +1020,37 @@ fn test_cancel_when_done() { assert_operation_state(timelock, OperationState::Unset, target_id); // Schedule - testing::set_contract_address(PROPOSER()); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule(call, predecessor, salt, delay); assert_operation_state(timelock, OperationState::Waiting, target_id); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); assert_operation_state(timelock, OperationState::Ready, target_id); // Execute - testing::set_contract_address(EXECUTOR()); + start_cheat_caller_address(timelock.contract_address, EXECUTOR()); timelock.execute(call, predecessor, salt); assert_operation_state(timelock, OperationState::Done, target_id); // Attempt cancel - testing::set_contract_address(PROPOSER()); // PROPOSER is also CANCELLER + start_cheat_caller_address(timelock.contract_address, PROPOSER()); // PROPOSER is also CANCELLER timelock.cancel(target_id); } #[test] -#[should_panic(expected: ('Timelock: expected Pending op', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Timelock: expected Pending op',))] fn test_cancel_when_unset() { let (mut timelock, _) = setup_dispatchers(); let invalid_id = 0; // PROPOSER is also CANCELLER - testing::set_contract_address(PROPOSER()); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.cancel(invalid_id); } #[test] -#[should_panic(expected: ('Caller is missing role', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Caller is missing role',))] fn test_cancel_unauthorized() { let (mut timelock, mut target) = setup_dispatchers(); let predecessor = NO_PREDECESSOR; @@ -1035,11 +1061,11 @@ fn test_cancel_unauthorized() { let target_id = timelock.hash_operation(call, predecessor, salt); // Schedule - testing::set_contract_address(PROPOSER()); + start_cheat_caller_address(timelock.contract_address, PROPOSER()); timelock.schedule(call, predecessor, salt, delay); // Cancel - testing::set_contract_address(OTHER()); + start_cheat_caller_address(timelock.contract_address, OTHER()); timelock.cancel(target_id); } @@ -1048,7 +1074,7 @@ fn test_cancel_unauthorized() { // #[test] -#[should_panic(expected: ('Timelock: unauthorized caller', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('Timelock: unauthorized caller',))] fn test_update_delay_unauthorized() { let mut timelock = deploy_timelock(); @@ -1071,22 +1097,27 @@ fn test_update_delay_scheduled() { let target_id = timelock.hash_operation(call, predecessor, salt); // Schedule - testing::set_contract_address(PROPOSER()); + let mut spy = spy_events(); + cheat_caller_address(timelock.contract_address, PROPOSER(), CheatSpan::TargetCalls(1)); timelock.schedule(call, predecessor, salt, delay); + assert_operation_state(timelock, OperationState::Waiting, target_id); - assert_only_event_schedule( - timelock.contract_address, target_id, event_index, call, predecessor, delay - ); + spy + .assert_only_event_call_scheduled( + timelock.contract_address, target_id, event_index, call, predecessor, delay + ); // Fast-forward - testing::set_block_timestamp(delay); + cheat_block_timestamp_global(delay); // Execute - testing::set_contract_address(EXECUTOR()); + cheat_caller_address(timelock.contract_address, EXECUTOR(), CheatSpan::TargetCalls(1)); + timelock.execute(call, predecessor, salt); + assert_operation_state(timelock, OperationState::Done, target_id); - assert_event_delay(timelock.contract_address, MIN_DELAY, NEW_DELAY); - assert_only_event_execute(timelock.contract_address, target_id, event_index, call); + spy.assert_event_delay_changed(timelock.contract_address, MIN_DELAY, NEW_DELAY); + spy.assert_only_event_call_executed(timelock.contract_address, target_id, event_index, call); // Check new minimum delay let get_new_delay = timelock.get_min_delay(); @@ -1162,7 +1193,8 @@ fn test_initializer_no_admin() { // The initializer grants the timelock contract address the `DEFAULT_ADMIN_ROLE` // therefore, we need to set the address since it's not deployed in this context - testing::set_contract_address(contract_address_const::<'TIMELOCK_ADDRESS'>()); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, contract_address_const::<'TIMELOCK_ADDRESS'>()); state.initializer(min_delay, proposers, executors, admin_zero); let admin_does_not_have_role = !contract_state.has_role(DEFAULT_ADMIN_ROLE, admin_zero); @@ -1198,6 +1230,7 @@ fn test_initializer_min_delay() { let executors = array![EXECUTOR()].span(); let admin_zero = ZERO(); + let mut spy = spy_events(); state.initializer(min_delay, proposers, executors, admin_zero); // Check minimum delay is set @@ -1205,12 +1238,12 @@ fn test_initializer_min_delay() { assert_eq!(delay, MIN_DELAY); // The initializer emits 4 `RoleGranted` events prior to `MinDelayChanged`: - // - Self administration + // - 1 Self administration // - 1 proposer // - 1 canceller // - 1 executor - utils::drop_events(ZERO(), 4); - assert_only_event_delay_change(ZERO(), 0, MIN_DELAY); + EventSpyExt::drop_n_events(ref spy, 4); + spy.assert_only_event_delay_changed(test_address(), 0, MIN_DELAY); } // @@ -1228,14 +1261,15 @@ fn test_assert_only_role_or_open_role_when_has_role() { state.initializer(min_delay, proposers, executors, admin); - testing::set_caller_address(PROPOSER()); - state.assert_only_role_or_open_role(PROPOSER_ROLE); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, PROPOSER()); + state.assert_only_role_or_open_role(PROPOSER_ROLE); // PROPOSER == CANCELLER - testing::set_caller_address(PROPOSER()); state.assert_only_role_or_open_role(CANCELLER_ROLE); - testing::set_caller_address(EXECUTOR()); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, EXECUTOR()); state.assert_only_role_or_open_role(EXECUTOR_ROLE); } @@ -1251,7 +1285,9 @@ fn test_assert_only_role_or_open_role_unauthorized() { state.initializer(min_delay, proposers, executors, admin); - testing::set_caller_address(OTHER()); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, OTHER()); + state.assert_only_role_or_open_role(PROPOSER_ROLE); } @@ -1271,7 +1307,9 @@ fn test_assert_only_role_or_open_role_with_open_role() { let is_open_role = contract_state.has_role(EXECUTOR_ROLE, open_role); assert!(is_open_role); - testing::set_caller_address(OTHER()); + let contract_address = test_address(); + start_cheat_caller_address(contract_address, OTHER()); + state.assert_only_role_or_open_role(EXECUTOR_ROLE); } @@ -1292,7 +1330,7 @@ fn test__before_call() { state.TimelockController_timestamps.write(target_id, target_time); // Fast-forward - testing::set_block_timestamp(target_time); + cheat_block_timestamp_global(target_time); state._before_call(target_id, predecessor); } @@ -1327,7 +1365,7 @@ fn test__before_call_insufficient_time() { state.TimelockController_timestamps.write(target_id, target_time); // Fast-forward - testing::set_block_timestamp(target_time - 1); + cheat_block_timestamp_global(target_time - 1); state._before_call(target_id, predecessor); } @@ -1346,7 +1384,7 @@ fn test__before_call_when_already_done() { state.TimelockController_timestamps.write(target_id, done_time); // Fast-forward - testing::set_block_timestamp(done_time); + cheat_block_timestamp_global(done_time); state._before_call(target_id, predecessor); } @@ -1368,7 +1406,7 @@ fn test__before_call_with_predecessor_done() { state.TimelockController_timestamps.write(target_id, target_time); // Fast-forward - testing::set_block_timestamp(target_time); + cheat_block_timestamp_global(target_time); state._before_call(target_id, predecessor_id); } @@ -1391,7 +1429,7 @@ fn test__before_call_with_predecessor_not_done() { state.TimelockController_timestamps.write(target_id, target_time); // Fast-forward - testing::set_block_timestamp(target_time); + cheat_block_timestamp_global(target_time); state._before_call(target_id, predecessor_id); } @@ -1412,7 +1450,7 @@ fn test__after_call() { state.TimelockController_timestamps.write(target_id, target_time); // Fast-forward - testing::set_block_timestamp(target_time); + cheat_block_timestamp_global(target_time); state._after_call(target_id); @@ -1450,7 +1488,7 @@ fn test__after_call_insufficient_time() { state.TimelockController_timestamps.write(target_id, target_time); // Fast-forward - testing::set_block_timestamp(target_time - 1); + cheat_block_timestamp_global(target_time - 1); state._after_call(target_id); } @@ -1468,7 +1506,7 @@ fn test__after_call_already_done() { state.TimelockController_timestamps.write(target_id, done_time); // Fast-forward - testing::set_block_timestamp(done_time); + cheat_block_timestamp_global(done_time); state._after_call(target_id); } @@ -1560,7 +1598,7 @@ fn test__execute() { } #[test] -#[should_panic(expected: ('Expected failure', 'ENTRYPOINT_FAILED',))] +#[should_panic(expected: ('Expected failure',))] fn test__execute_with_failing_tx() { let mut state = COMPONENT_STATE(); let mut target = deploy_mock_target(); @@ -1573,6 +1611,7 @@ fn test__execute_with_failing_tx() { } #[test] +#[ignore] // REASON: should_panic attribute not fit for complex panic messages. #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test__execute_with_bad_selector() { let mut state = COMPONENT_STATE(); @@ -1630,170 +1669,164 @@ fn assert_operation_state(timelock: TimelockABIDispatcher, exp_state: OperationS // Event helpers // -// -// MinDelayChanged -// - -fn assert_event_delay_change(contract: ContractAddress, old_duration: u64, new_duration: u64) { - let event = utils::pop_log::(contract).unwrap(); - let expected = TimelockControllerComponent::Event::MinDelayChanged( - MinDelayChanged { old_duration, new_duration } - ); - assert!(event == expected); -} - -fn assert_only_event_delay_change(contract: ContractAddress, old_duration: u64, new_duration: u64) { - assert_event_delay_change(contract, old_duration, new_duration); - utils::assert_no_events_left(contract); -} - -// -// CallScheduled -// - -fn assert_event_schedule( - contract: ContractAddress, - id: felt252, - index: felt252, - call: Call, - predecessor: felt252, - delay: u64 -) { - let event = utils::pop_log::(contract).unwrap(); - let expected = TimelockControllerComponent::Event::CallScheduled( - CallScheduled { id, index, call, predecessor, delay } - ); - assert!(event == expected); - - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("CallScheduled")); - indexed_keys.append_serde(id); - indexed_keys.append_serde(index); - utils::assert_indexed_keys(event, indexed_keys.span()); -} - -fn assert_only_event_schedule( - contract: ContractAddress, - id: felt252, - index: felt252, - call: Call, - predecessor: felt252, - delay: u64 -) { - assert_event_schedule(contract, id, index, call, predecessor, delay); - utils::assert_no_events_left(contract); -} - -fn assert_events_schedule_batch( - contract: ContractAddress, id: felt252, calls: Span, predecessor: felt252, delay: u64 -) { - let mut i = 0; - loop { - if i == calls.len() { - break; - } - assert_event_schedule(contract, id, i.into(), *calls.at(i), predecessor, delay); - i += 1; +#[generate_trait] +pub(crate) impl TimelockSpyHelpersImpl of TimelockSpyHelpers { + // + // CallScheduled + // + + fn assert_event_call_scheduled( + ref self: EventSpy, + contract: ContractAddress, + id: felt252, + index: felt252, + call: Call, + predecessor: felt252, + delay: u64 + ) { + let expected = TimelockControllerComponent::Event::CallScheduled( + CallScheduled { id, index, call, predecessor, delay } + ); + self.assert_emitted_single(contract, expected); } -} -fn assert_only_events_schedule_batch( - contract: ContractAddress, id: felt252, calls: Span, predecessor: felt252, delay: u64 -) { - assert_events_schedule_batch(contract, id, calls, predecessor, delay); - utils::assert_no_events_left(contract); -} + fn assert_only_event_call_scheduled( + ref self: EventSpy, + contract: ContractAddress, + id: felt252, + index: felt252, + call: Call, + predecessor: felt252, + delay: u64 + ) { + self.assert_event_call_scheduled(contract, id, index, call, predecessor, delay); + self.assert_no_events_left_from(contract); + } -// -// CallSalt -// + fn assert_events_call_scheduled_batch( + ref self: EventSpy, + contract: ContractAddress, + id: felt252, + calls: Span, + predecessor: felt252, + delay: u64 + ) { + let mut i = 0; + loop { + if i == calls.len() { + break; + } + self + .assert_event_call_scheduled( + contract, id, i.into(), *calls.at(i), predecessor, delay + ); + i += 1; + }; + } -fn assert_event_call_salt(contract: ContractAddress, id: felt252, salt: felt252) { - let event = utils::pop_log::(contract).unwrap(); - let expected = TimelockControllerComponent::Event::CallSalt(CallSalt { id, salt }); - assert!(event == expected); -} + fn assert_only_events_call_scheduled_batch( + ref self: EventSpy, + contract: ContractAddress, + id: felt252, + calls: Span, + predecessor: felt252, + delay: u64 + ) { + self.assert_events_call_scheduled_batch(contract, id, calls, predecessor, delay); + self.assert_no_events_left_from(contract); + } -fn assert_only_event_call_salt(contract: ContractAddress, id: felt252, salt: felt252) { - assert_event_call_salt(contract, id, salt); - utils::assert_no_events_left(contract); -} + // + // CallSalt + // -// -// CallExecuted -// + fn assert_event_call_salt( + ref self: EventSpy, contract: ContractAddress, id: felt252, salt: felt252 + ) { + let expected = TimelockControllerComponent::Event::CallSalt(CallSalt { id, salt }); + self.assert_emitted_single(contract, expected); + } -fn assert_event_execute(contract: ContractAddress, id: felt252, index: felt252, call: Call) { - let event = utils::pop_log::(contract).unwrap(); - let expected = TimelockControllerComponent::Event::CallExecuted( - CallExecuted { id, index, call } - ); - assert!(event == expected); + fn assert_only_event_call_salt( + ref self: EventSpy, contract: ContractAddress, id: felt252, salt: felt252 + ) { + self.assert_event_call_salt(contract, id, salt); + self.assert_no_events_left_from(contract); + } - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("CallExecuted")); - indexed_keys.append_serde(id); - indexed_keys.append_serde(index); - utils::assert_indexed_keys(event, indexed_keys.span()); -} + // + // Cancelled + // -fn assert_only_event_execute(contract: ContractAddress, id: felt252, index: felt252, call: Call) { - assert_event_execute(contract, id, index, call); - utils::assert_no_events_left(contract); -} + fn assert_event_call_cancelled(ref self: EventSpy, contract: ContractAddress, id: felt252) { + let expected = TimelockControllerComponent::Event::CallCancelled(CallCancelled { id }); + self.assert_emitted_single(contract, expected); + } -fn assert_events_execute_batch(contract: ContractAddress, id: felt252, calls: Span) { - let mut i = 0; - loop { - if i == calls.len() { - break; - } - assert_event_execute(contract, id, i.into(), *calls.at(i)); - i += 1; + fn assert_only_event_call_cancelled( + ref self: EventSpy, contract: ContractAddress, id: felt252 + ) { + self.assert_event_call_cancelled(contract, id); + self.assert_no_events_left_from(contract); } -} -fn assert_only_events_execute_batch(contract: ContractAddress, id: felt252, calls: Span) { - assert_events_execute_batch(contract, id, calls); - utils::assert_no_events_left(contract); -} + // + // CallExecuted + // -// -// Cancelled -// + fn assert_event_call_executed( + ref self: EventSpy, contract: ContractAddress, id: felt252, index: felt252, call: Call + ) { + let expected = TimelockControllerComponent::Event::CallExecuted( + CallExecuted { id, index, call } + ); + self.assert_emitted_single(contract, expected); + } -fn assert_event_cancel(contract: ContractAddress, id: felt252) { - let event = utils::pop_log::(contract).unwrap(); - let expected = TimelockControllerComponent::Event::CallCancelled(CallCancelled { id }); - assert!(event == expected); + fn assert_only_event_call_executed( + ref self: EventSpy, contract: ContractAddress, id: felt252, index: felt252, call: Call + ) { + self.assert_event_call_executed(contract, id, index, call); + self.assert_no_events_left_from(contract); + } - // Check indexed keys - let mut indexed_keys = array![]; - indexed_keys.append_serde(selector!("CallCancelled")); - indexed_keys.append_serde(id); - utils::assert_indexed_keys(event, indexed_keys.span()); -} + fn assert_events_call_executed_batch( + ref self: EventSpy, contract: ContractAddress, id: felt252, calls: Span + ) { + let mut i = 0; + loop { + if i == calls.len() { + break; + } + self.assert_event_call_executed(contract, id, i.into(), *calls.at(i)); + i += 1; + } + } -fn assert_only_event_cancel(contract: ContractAddress, id: felt252) { - assert_event_cancel(contract, id); - utils::assert_no_events_left(contract); -} + fn assert_only_events_call_executed_batch( + ref self: EventSpy, contract: ContractAddress, id: felt252, calls: Span + ) { + self.assert_events_call_executed_batch(contract, id, calls); + self.assert_no_events_left_from(contract); + } -// -// MinDelayChanged -// + // + // MinDelayChanged + // -fn assert_event_delay(contract: ContractAddress, old_duration: u64, new_duration: u64) { - let event = utils::pop_log::(contract).unwrap(); - let expected = TimelockControllerComponent::Event::MinDelayChanged( - MinDelayChanged { old_duration, new_duration } - ); - assert!(event == expected); -} + fn assert_event_delay_changed( + ref self: EventSpy, contract: ContractAddress, old_duration: u64, new_duration: u64, + ) { + let expected = TimelockControllerComponent::Event::MinDelayChanged( + MinDelayChanged { old_duration, new_duration } + ); + self.assert_emitted_single(contract, expected); + } -fn assert_only_event_delay(contract: ContractAddress, old_duration: u64, new_duration: u64) { - assert_event_delay(contract, old_duration, new_duration); - utils::assert_no_events_left(contract); + fn assert_only_event_delay_changed( + ref self: EventSpy, contract: ContractAddress, old_duration: u64, new_duration: u64, + ) { + self.assert_event_delay_changed(contract, old_duration, new_duration); + self.assert_no_events_left_from(contract); + } }