diff --git a/contracts/core/price-aggregator/tests/price_aggregator_whitebox_test.rs b/contracts/core/price-aggregator/tests/price_aggregator_whitebox_test.rs index 180301b610..46ce6f52c3 100644 --- a/contracts/core/price-aggregator/tests/price_aggregator_whitebox_test.rs +++ b/contracts/core/price-aggregator/tests/price_aggregator_whitebox_test.rs @@ -4,7 +4,7 @@ use multiversx_price_aggregator_sc::{ }; use multiversx_sc_modules::{ pause::EndpointWrappers as PauseEndpointWrappers, - staking::EndpointWrappers as StakingEndpointWrappers, + staking::{EndpointWrappers as StakingEndpointWrappers, StakingModule}, }; use multiversx_sc_scenario::imports::*; @@ -17,9 +17,10 @@ pub const STAKE_AMOUNT: u64 = 20; pub const SUBMISSION_COUNT: usize = 3; pub const USD_TICKER: &[u8] = b"USDC"; -const OWNER_ADDRESS_EXPR: &str = "address:owner"; -const PRICE_AGGREGATOR_ADDRESS_EXPR: &str = "sc:price-aggregator"; -const PRICE_AGGREGATOR_PATH_EXPR: &str = "mxsc:output/multiversx-price-aggregator-sc.mxsc.json"; +const OWNER_ADDRESS: TestAddress = TestAddress::new("owner"); +const PRICE_AGGREGATOR_ADDRESS: TestSCAddress = TestSCAddress::new("price-aggregator"); +const PRICE_AGGREGATOR_PATH_EXPR: MxscPath = + MxscPath::new("mxsc:output/multiversx-price-aggregator-sc.mxsc.json"); fn world() -> ScenarioWorld { let mut blockchain = ScenarioWorld::new(); @@ -35,31 +36,27 @@ fn world() -> ScenarioWorld { #[test] fn test_price_aggregator_submit() { let (mut world, oracles) = setup(); - let price_aggregator_whitebox = WhiteboxContract::new( - PRICE_AGGREGATOR_ADDRESS_EXPR, - multiversx_price_aggregator_sc::contract_obj, - ); // configure the number of decimals - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new().from(OWNER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(OWNER_ADDRESS) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { sc.set_pair_decimals( managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER), DECIMALS, ) - }, - ); + }); // try submit while paused - world.whitebox_call_check( - &price_aggregator_whitebox, - ScCallStep::new() - .from(&oracles[0]) - .expect(TxExpect::user_error("str:Contract is paused")), - |sc| { + world + .tx() + .from(&oracles[0]) + .to(PRICE_AGGREGATOR_ADDRESS) + .returns(ExpectError(4u64, "Contract is paused")) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { sc.submit( managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER), @@ -67,22 +64,24 @@ fn test_price_aggregator_submit() { managed_biguint!(100), DECIMALS, ) - }, - |r| r.assert_user_error("Contract is paused"), - ); + }); // unpause - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new().from(OWNER_ADDRESS_EXPR), - |sc| sc.call_unpause_endpoint(), - ); + world + .tx() + .from(OWNER_ADDRESS) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { + sc.call_unpause_endpoint(); + }); // submit first timestamp too old - world.whitebox_call_check( - &price_aggregator_whitebox, - ScCallStep::new().from(&oracles[0]).no_expect(), - |sc| { + world + .tx() + .from(&oracles[0]) + .to(PRICE_AGGREGATOR_ADDRESS) + .returns(ExpectError(4u64, "First submission too old")) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { sc.submit( managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER), @@ -90,17 +89,14 @@ fn test_price_aggregator_submit() { managed_biguint!(100), DECIMALS, ) - }, - |r| { - r.assert_user_error("First submission too old"); - }, - ); + }); // submit ok - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new().from(&oracles[0]), - |sc| { + world + .tx() + .from(&oracles[0]) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { sc.submit( managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER), @@ -108,49 +104,50 @@ fn test_price_aggregator_submit() { managed_biguint!(100), DECIMALS, ) - }, - ); + }); let current_timestamp = 100; - world.whitebox_query(&price_aggregator_whitebox, |sc| { - let token_pair = TokenPair { - from: managed_buffer!(EGLD_TICKER), - to: managed_buffer!(USD_TICKER), - }; - assert_eq!( - sc.first_submission_timestamp(&token_pair).get(), - current_timestamp - ); - assert_eq!( - sc.last_submission_timestamp(&token_pair).get(), - current_timestamp - ); - - let submissions = sc.submissions().get(&token_pair).unwrap(); - assert_eq!(submissions.len(), 1); - assert_eq!( - submissions - .get(&managed_address!(&oracles[0].to_address())) - .unwrap(), - managed_biguint!(100) - ); - - assert_eq!( - sc.oracle_status() - .get(&managed_address!(&oracles[0].to_address())) - .unwrap(), - OracleStatus { - total_submissions: 1, - accepted_submissions: 1 - } - ); - }); + world.query().to(PRICE_AGGREGATOR_ADDRESS).whitebox( + multiversx_price_aggregator_sc::contract_obj, + |sc| { + let token_pair = TokenPair { + from: managed_buffer!(EGLD_TICKER), + to: managed_buffer!(USD_TICKER), + }; + assert_eq!( + sc.first_submission_timestamp(&token_pair).get(), + current_timestamp + ); + assert_eq!( + sc.last_submission_timestamp(&token_pair).get(), + current_timestamp + ); + + let submissions = sc.submissions().get(&token_pair).unwrap(); + assert_eq!(submissions.len(), 1); + assert_eq!( + submissions.get(&ManagedAddress::from(&oracles[0])).unwrap(), + managed_biguint!(100) + ); + + assert_eq!( + sc.oracle_status() + .get(&ManagedAddress::from(&oracles[0])) + .unwrap(), + OracleStatus { + total_submissions: 1, + accepted_submissions: 1 + } + ); + }, + ); // first oracle submit again - submission not accepted - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new().from(&oracles[0]), - |sc| { + world + .tx() + .from(&oracles[0]) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { sc.submit( managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER), @@ -158,55 +155,56 @@ fn test_price_aggregator_submit() { managed_biguint!(100), DECIMALS, ) + }); + + world.query().to(PRICE_AGGREGATOR_ADDRESS).whitebox( + multiversx_price_aggregator_sc::contract_obj, + |sc| { + assert_eq!( + sc.oracle_status() + .get(&ManagedAddress::from(&oracles[0])) + .unwrap(), + OracleStatus { + total_submissions: 2, + accepted_submissions: 1 + } + ); }, ); - - world.whitebox_query(&price_aggregator_whitebox, |sc| { - assert_eq!( - sc.oracle_status() - .get(&managed_address!(&oracles[0].to_address())) - .unwrap(), - OracleStatus { - total_submissions: 2, - accepted_submissions: 1 - } - ); - }); } #[test] fn test_price_aggregator_submit_round_ok() { let (mut world, oracles) = setup(); - let price_aggregator_whitebox = WhiteboxContract::new( - PRICE_AGGREGATOR_ADDRESS_EXPR, - multiversx_price_aggregator_sc::contract_obj, - ); // configure the number of decimals - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new().from(OWNER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(OWNER_ADDRESS) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { sc.set_pair_decimals( managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER), DECIMALS, ) - }, - ); + }); // unpause - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new().from(OWNER_ADDRESS_EXPR), - |sc| sc.call_unpause_endpoint(), - ); + world + .tx() + .from(OWNER_ADDRESS) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { + sc.call_unpause_endpoint(); + }); // submit first - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new().from(&oracles[0]), - |sc| { + world + .tx() + .from(&oracles[0]) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { sc.submit( managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER), @@ -214,17 +212,17 @@ fn test_price_aggregator_submit_round_ok() { managed_biguint!(10_000), DECIMALS, ) - }, - ); + }); let current_timestamp = 110; - world.set_state_step(SetStateStep::new().block_timestamp(current_timestamp)); + world.current_block().block_timestamp(current_timestamp); // submit second - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new().from(&oracles[1]), - |sc| { + world + .tx() + .from(&oracles[1]) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { sc.submit( managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER), @@ -232,14 +230,14 @@ fn test_price_aggregator_submit_round_ok() { managed_biguint!(11_000), DECIMALS, ) - }, - ); + }); // submit third - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new().from(&oracles[2]), - |sc| { + world + .tx() + .from(&oracles[2]) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { sc.submit( managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER), @@ -247,72 +245,73 @@ fn test_price_aggregator_submit_round_ok() { managed_biguint!(12_000), DECIMALS, ) + }); + + world.query().to(PRICE_AGGREGATOR_ADDRESS).whitebox( + multiversx_price_aggregator_sc::contract_obj, + |sc| { + let result = + sc.latest_price_feed(managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER)); + + let (round_id, from, to, timestamp, price, decimals) = result.into_tuple(); + assert_eq!(round_id, 1); + assert_eq!(from, managed_buffer!(EGLD_TICKER)); + assert_eq!(to, managed_buffer!(USD_TICKER)); + assert_eq!(timestamp, current_timestamp); + assert_eq!(price, managed_biguint!(11_000)); + assert_eq!(decimals, DECIMALS); + + // submissions are deleted after round is created + let token_pair = TokenPair { from, to }; + let submissions = sc.submissions().get(&token_pair).unwrap(); + assert_eq!(submissions.len(), 0); + + let rounds = sc.rounds().get(&token_pair).unwrap(); + assert_eq!(rounds.len(), 1); + assert_eq!( + rounds.get(1), + TimestampedPrice { + timestamp, + price, + decimals + } + ); }, ); - - world.whitebox_query(&price_aggregator_whitebox, |sc| { - let result = - sc.latest_price_feed(managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER)); - - let (round_id, from, to, timestamp, price, decimals) = result.into_tuple(); - assert_eq!(round_id, 1); - assert_eq!(from, managed_buffer!(EGLD_TICKER)); - assert_eq!(to, managed_buffer!(USD_TICKER)); - assert_eq!(timestamp, current_timestamp); - assert_eq!(price, managed_biguint!(11_000)); - assert_eq!(decimals, DECIMALS); - - // submissions are deleted after round is created - let token_pair = TokenPair { from, to }; - let submissions = sc.submissions().get(&token_pair).unwrap(); - assert_eq!(submissions.len(), 0); - - let rounds = sc.rounds().get(&token_pair).unwrap(); - assert_eq!(rounds.len(), 1); - assert_eq!( - rounds.get(1), - TimestampedPrice { - timestamp, - price, - decimals - } - ); - }); } #[test] fn test_price_aggregator_discarded_round() { let (mut world, oracles) = setup(); - let price_aggregator_whitebox = WhiteboxContract::new( - PRICE_AGGREGATOR_ADDRESS_EXPR, - multiversx_price_aggregator_sc::contract_obj, - ); // configure the number of decimals - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new().from(OWNER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(OWNER_ADDRESS) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { sc.set_pair_decimals( managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER), DECIMALS, ) - }, - ); + }); // unpause - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new().from(OWNER_ADDRESS_EXPR), - |sc| sc.call_unpause_endpoint(), - ); + world + .tx() + .from(OWNER_ADDRESS) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { + sc.call_unpause_endpoint(); + }); // submit first - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new().from(&oracles[0]), - |sc| { + world + .tx() + .from(&oracles[0]) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { sc.submit( managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER), @@ -320,17 +319,17 @@ fn test_price_aggregator_discarded_round() { managed_biguint!(10_000), DECIMALS, ) - }, - ); + }); let current_timestamp = 100 + MAX_ROUND_DURATION_SECONDS + 1; - world.set_state_step(SetStateStep::new().block_timestamp(current_timestamp)); + world.current_block().block_timestamp(current_timestamp); // submit second - this will discard the previous submission - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new().from(&oracles[1]), - |sc| { + world + .tx() + .from(&oracles[1]) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { sc.submit( managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER), @@ -338,77 +337,100 @@ fn test_price_aggregator_discarded_round() { managed_biguint!(11_000), DECIMALS, ) + }); + + world.query().to(PRICE_AGGREGATOR_ADDRESS).whitebox( + multiversx_price_aggregator_sc::contract_obj, + |sc| { + let token_pair = TokenPair { + from: managed_buffer!(EGLD_TICKER), + to: managed_buffer!(USD_TICKER), + }; + let submissions = sc.submissions().get(&token_pair).unwrap(); + assert_eq!(submissions.len(), 1); + assert_eq!( + submissions.get(&managed_address!(&oracles[1])).unwrap(), + managed_biguint!(11_000) + ); }, ); - - world.whitebox_query(&price_aggregator_whitebox, |sc| { - let token_pair = TokenPair { - from: managed_buffer!(EGLD_TICKER), - to: managed_buffer!(USD_TICKER), - }; - let submissions = sc.submissions().get(&token_pair).unwrap(); - assert_eq!(submissions.len(), 1); - assert_eq!( - submissions - .get(&managed_address!(&oracles[1].to_address())) - .unwrap(), - managed_biguint!(11_000) - ); - }); } #[test] fn test_price_aggregator_slashing() { let (mut world, oracles) = setup(); - let price_aggregator_whitebox = WhiteboxContract::new( - PRICE_AGGREGATOR_ADDRESS_EXPR, - multiversx_price_aggregator_sc::contract_obj, - ); - // unpause - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new().from(OWNER_ADDRESS_EXPR), - |sc| sc.call_unpause_endpoint(), - ); - - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new() - .from(&oracles[0]) - .argument(BytesValue::from(oracles[1].to_address().as_bytes())), - |sc| sc.call_vote_slash_member(), - ); - - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new() - .from(&oracles[2]) - .argument(BytesValue::from(oracles[1].to_address().as_bytes())), - |sc| sc.call_vote_slash_member(), - ); + // configure the number of decimals + world + .tx() + .from(OWNER_ADDRESS) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { + sc.set_pair_decimals( + managed_buffer!(EGLD_TICKER), + managed_buffer!(USD_TICKER), + DECIMALS, + ) + }); - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new() - .from(&oracles[3]) - .argument(BytesValue::from(oracles[1].to_address().as_bytes())), - |sc| sc.call_vote_slash_member(), + // unpause + world + .tx() + .from(OWNER_ADDRESS) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { + sc.call_unpause_endpoint(); + }); + + world + .tx() + .from(&oracles[0]) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { + sc.vote_slash_member(ManagedAddress::from(&oracles[1])); + }); + + world + .tx() + .from(&oracles[2]) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { + sc.vote_slash_member(ManagedAddress::from(&oracles[1])) + }); + + world + .tx() + .from(&oracles[3]) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { + sc.vote_slash_member(ManagedAddress::from(&oracles[1])); + }); + + world.query().to(PRICE_AGGREGATOR_ADDRESS).whitebox( + multiversx_price_aggregator_sc::contract_obj, + |sc| { + let list = sc.slashing_proposal_voters(&ManagedAddress::from(&oracles[1])); + assert!(list.contains(&ManagedAddress::from(&oracles[0]))); + assert!(list.contains(&ManagedAddress::from(&oracles[2]))); + assert!(list.contains(&ManagedAddress::from(&oracles[3]))); + }, ); - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new() - .from(&oracles[0]) - .argument(BytesValue::from(oracles[1].to_address().as_bytes())), - |sc| sc.call_slash_member(), - ); + world + .tx() + .from(&oracles[0]) + .to(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { + sc.slash_member(ManagedAddress::from(&oracles[1])); + }); // oracle 1 try submit after slashing - world.whitebox_call_check( - &price_aggregator_whitebox, - ScCallStep::new().from(&oracles[1]).no_expect(), - |sc| { + world + .tx() + .from(&oracles[1]) + .to(PRICE_AGGREGATOR_ADDRESS) + .returns(ExpectError(4u64, "only oracles allowed")) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { sc.submit( managed_buffer!(EGLD_TICKER), managed_buffer!(USD_TICKER), @@ -416,49 +438,37 @@ fn test_price_aggregator_slashing() { managed_biguint!(10_000), DECIMALS, ) - }, - |r| { - r.assert_user_error("only oracles allowed"); - }, - ); + }); } -fn setup() -> (ScenarioWorld, Vec) { +fn setup() -> (ScenarioWorld, Vec
) { // setup let mut world = world(); - let price_aggregator_whitebox = WhiteboxContract::new( - PRICE_AGGREGATOR_ADDRESS_EXPR, - multiversx_price_aggregator_sc::contract_obj, - ); - let price_aggregator_code = world.code_expression(PRICE_AGGREGATOR_PATH_EXPR); - let mut set_state_step = SetStateStep::new() - .put_account(OWNER_ADDRESS_EXPR, Account::new().nonce(1)) - .new_address(OWNER_ADDRESS_EXPR, 1, PRICE_AGGREGATOR_ADDRESS_EXPR) - .block_timestamp(100); + world.account(OWNER_ADDRESS).nonce(1); + world.current_block().block_timestamp(100); let mut oracles = Vec::new(); for i in 1..=NR_ORACLES { - let oracle_address_expr = format!("address:oracle{i}"); - let oracle_address = AddressValue::from(oracle_address_expr.as_str()); - - set_state_step = set_state_step.put_account( - oracle_address_expr.as_str(), - Account::new().nonce(1).balance(STAKE_AMOUNT), - ); - oracles.push(oracle_address); + let oracle_address_expr = format!("oracle{i}"); + let oracle_address = TestAddress::new(&oracle_address_expr); + + world.account(oracle_address).nonce(1).balance(STAKE_AMOUNT); + + oracles.push(oracle_address.to_address()); } // init price aggregator - world.set_state_step(set_state_step).whitebox_deploy( - &price_aggregator_whitebox, - ScDeployStep::new() - .from(OWNER_ADDRESS_EXPR) - .code(price_aggregator_code), - |sc| { + world + .tx() + .from(OWNER_ADDRESS) + .raw_deploy() + .code(PRICE_AGGREGATOR_PATH_EXPR) + .new_address(PRICE_AGGREGATOR_ADDRESS) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { let mut oracle_args = MultiValueEncoded::new(); for oracle_address in &oracles { - oracle_args.push(managed_address!(&oracle_address.to_address())); + oracle_args.push(ManagedAddress::from(oracle_address)); } sc.init( @@ -469,17 +479,17 @@ fn setup() -> (ScenarioWorld, Vec) { SUBMISSION_COUNT, oracle_args, ) - }, - ); + }); for oracle_address in &oracles { - world.whitebox_call( - &price_aggregator_whitebox, - ScCallStep::new() - .from(oracle_address) - .egld_value(STAKE_AMOUNT), - |sc| sc.call_stake(), - ); + world + .tx() + .from(oracle_address) + .to(PRICE_AGGREGATOR_ADDRESS) + .egld(STAKE_AMOUNT) + .whitebox(multiversx_price_aggregator_sc::contract_obj, |sc| { + sc.call_stake(); + }); } (world, oracles) diff --git a/contracts/examples/adder/tests/adder_whitebox_test.rs b/contracts/examples/adder/tests/adder_whitebox_test.rs index 9e7abe4a51..c609b08302 100644 --- a/contracts/examples/adder/tests/adder_whitebox_test.rs +++ b/contracts/examples/adder/tests/adder_whitebox_test.rs @@ -1,49 +1,50 @@ use adder::*; use multiversx_sc_scenario::imports::*; -const ADDER_PATH_EXPR: &str = "mxsc:output/adder.mxsc.json"; +const OWNER: TestAddress = TestAddress::new("owner"); +const ADDER_ADDRESS: TestSCAddress = TestSCAddress::new("adder"); +const CODE_PATH: MxscPath = MxscPath::new("mxsc:output/adder.mxsc.json"); fn world() -> ScenarioWorld { let mut blockchain = ScenarioWorld::new(); - blockchain.register_contract("mxsc:output/adder.mxsc.json", adder::ContractBuilder); + blockchain.register_contract(CODE_PATH, adder::ContractBuilder); blockchain } #[test] fn adder_whitebox() { let mut world = world(); - let adder_whitebox = WhiteboxContract::new("sc:adder", adder::contract_obj); - let adder_code = world.code_expression(ADDER_PATH_EXPR); + + world.account(OWNER).nonce(1); + + let new_address = world + .tx() + .from(OWNER) + .raw_deploy() + .code(CODE_PATH) + .new_address(ADDER_ADDRESS) + .returns(ReturnsNewBech32Address) + .whitebox(adder::contract_obj, |sc| { + sc.init(BigUint::from(3u64)); + }); + + assert_eq!(new_address, ADDER_ADDRESS.to_address().into()); world - .set_state_step( - SetStateStep::new() - .put_account("address:owner", Account::new().nonce(1)) - .new_address("address:owner", 1, "sc:adder"), - ) - .whitebox_deploy( - &adder_whitebox, - ScDeployStep::new().from("address:owner").code(adder_code), - |sc| { - sc.init(5u32.into()); - }, - ) - .whitebox_query(&adder_whitebox, |sc| { - let sum_value = sc.sum(); - assert_eq!(sum_value.get(), 5u32); - }) - .whitebox_call( - &adder_whitebox, - ScCallStep::new().from("address:owner"), - |sc| sc.add(3u32.into()), - ) - .check_state_step( - CheckStateStep::new() - .put_account("address:owner", CheckAccount::new()) - .put_account( - "sc:adder", - CheckAccount::new().check_storage("str:sum", "8"), - ), - ); + .tx() + .from(OWNER) + .to(ADDER_ADDRESS) + .whitebox(adder::contract_obj, |sc| { + sc.add(BigUint::from(5u64)); + }); + + let _raw_response = world + .query() + .to(ADDER_ADDRESS) + .returns(ReturnsRawResult) + .whitebox(adder::contract_obj, |sc| { + let sum = sc.sum().get(); + assert_eq!(sum, BigUint::from(8u64)); + }); } diff --git a/contracts/examples/multisig/tests/multisig_whitebox_test.rs b/contracts/examples/multisig/tests/multisig_whitebox_test.rs index 904a03c7e0..5e1347d82b 100644 --- a/contracts/examples/multisig/tests/multisig_whitebox_test.rs +++ b/contracts/examples/multisig/tests/multisig_whitebox_test.rs @@ -10,11 +10,11 @@ use multisig::{ user_role::UserRole, Multisig, }; -const OWNER_ADDRESS_EXPR: &str = "address:owner"; -const PROPOSER_ADDRESS_EXPR: &str = "address:proposer"; -const BOARD_MEMBER_ADDRESS_EXPR: &str = "address:board-member"; -const MULTISIG_ADDRESS_EXPR: &str = "sc:multisig"; -const MULTISIG_PATH_EXPR: &str = "mxsc:output/multisig.mxsc.json"; +const OWNER_ADDRESS: TestAddress = TestAddress::new("owner"); +const PROPOSER_ADDRESS: TestAddress = TestAddress::new("proposer"); +const BOARD_MEMBER_ADDRESS: TestAddress = TestAddress::new("board-member"); +const MULTISIG_ADDRESS: TestSCAddress = TestSCAddress::new("multisig"); +const MULTISIG_PATH_EXPR: MxscPath = MxscPath::new("mxsc:output/multisig.mxsc.json"); const QUORUM_SIZE: usize = 1; type RustBigUint = num_bigint::BigUint; @@ -59,40 +59,28 @@ fn world() -> ScenarioWorld { fn setup() -> ScenarioWorld { // setup let mut world = world(); - let multisig_whitebox = WhiteboxContract::new(MULTISIG_ADDRESS_EXPR, multisig::contract_obj); - let multisig_code = world.code_expression(MULTISIG_PATH_EXPR); - - world.set_state_step( - SetStateStep::new() - .put_account(OWNER_ADDRESS_EXPR, Account::new().nonce(1)) - .new_address(OWNER_ADDRESS_EXPR, 1, MULTISIG_ADDRESS_EXPR) - .put_account( - PROPOSER_ADDRESS_EXPR, - Account::new().nonce(1).balance(100_000_000u64), - ) - .put_account(BOARD_MEMBER_ADDRESS_EXPR, Account::new().nonce(1)), - ); + + world.account(OWNER_ADDRESS).nonce(1); + world + .account(PROPOSER_ADDRESS) + .nonce(1) + .balance(100_000_000u64); + world.account(BOARD_MEMBER_ADDRESS).nonce(1); // init multisig - world.whitebox_deploy( - &multisig_whitebox, - ScDeployStep::new() - .from(OWNER_ADDRESS_EXPR) - .code(multisig_code), - |sc| { + world + .tx() + .from(OWNER_ADDRESS) + .raw_deploy() + .code(MULTISIG_PATH_EXPR) + .new_address(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { let mut board_members = ManagedVec::new(); - board_members.push(managed_address!(&address_expr_to_address( - BOARD_MEMBER_ADDRESS_EXPR - ))); + board_members.push(BOARD_MEMBER_ADDRESS.to_managed_address()); sc.init(QUORUM_SIZE, board_members.into()); - sc.change_user_role( - 0, - managed_address!(&address_expr_to_address(PROPOSER_ADDRESS_EXPR)), - UserRole::Proposer, - ); - }, - ); + sc.change_user_role(0, PROPOSER_ADDRESS.to_managed_address(), UserRole::Proposer); + }); world } @@ -119,70 +107,66 @@ fn call_propose( let mut action_id = 0; - let multisig_whitebox = WhiteboxContract::new(MULTISIG_ADDRESS_EXPR, multisig::contract_obj); - - world.whitebox_call_check( - &multisig_whitebox, - ScCallStep::new() - .from(PROPOSER_ADDRESS_EXPR) - .egld_value(amount_rust_biguint) - .no_expect(), - |sc| { - action_id = match action { - ActionRaw::_Nothing => panic!("Invalid action"), - ActionRaw::AddBoardMember(addr) => { - sc.propose_add_board_member(managed_address!(&addr)) + let mut transaction = world + .tx() + .from(PROPOSER_ADDRESS) + .to(MULTISIG_ADDRESS) + .egld(BigUint::from(egld_amount)); + + let mut transaction_with_err = match expected_message { + Some(message) => transaction.returns(ExpectError(4u64, message)), + None => transaction.returns(ExpectError(0u64, "")), + }; + + transaction_with_err.whitebox(multisig::contract_obj, |sc| { + action_id = match action { + ActionRaw::_Nothing => panic!("Invalid action"), + ActionRaw::AddBoardMember(addr) => sc.propose_add_board_member(managed_address!(&addr)), + ActionRaw::AddProposer(addr) => sc.propose_add_proposer(managed_address!(&addr)), + ActionRaw::RemoveUser(addr) => sc.propose_remove_user(managed_address!(&addr)), + ActionRaw::ChangeQuorum(new_size) => sc.propose_change_quorum(new_size), + ActionRaw::SendTransferExecute(call_data) => sc.propose_transfer_execute( + managed_address!(&call_data.to), + call_data.egld_amount.into(), + FunctionCall { + function_name: call_data.endpoint_name.into(), + arg_buffer: call_data.arguments.into(), }, - ActionRaw::AddProposer(addr) => sc.propose_add_proposer(managed_address!(&addr)), - ActionRaw::RemoveUser(addr) => sc.propose_remove_user(managed_address!(&addr)), - ActionRaw::ChangeQuorum(new_size) => sc.propose_change_quorum(new_size), - ActionRaw::SendTransferExecute(call_data) => sc.propose_transfer_execute( - managed_address!(&call_data.to), - BigUint::from_bytes_be(&call_data.egld_amount.to_bytes_be()), - FunctionCall { - function_name: call_data.endpoint_name.into(), - arg_buffer: call_data.arguments.into(), - }, - ), - ActionRaw::SendAsyncCall(call_data) => sc.propose_async_call( - managed_address!(&call_data.to), - BigUint::from_bytes_be(&call_data.egld_amount.to_bytes_be()), - FunctionCall { - function_name: call_data.endpoint_name.into(), - arg_buffer: call_data.arguments.into(), - }, - ), - ActionRaw::SCDeployFromSource { - amount, - source, - code_metadata, - arguments, - } => sc.propose_sc_deploy_from_source( - BigUint::from_bytes_be(&amount.to_bytes_be()), - managed_address!(&source), - code_metadata, - boxed_bytes_vec_to_managed(arguments).into(), - ), - ActionRaw::SCUpgradeFromSource { - sc_address, - amount, - source, - code_metadata, - arguments, - } => sc.propose_sc_upgrade_from_source( - managed_address!(&sc_address), - BigUint::from_bytes_be(&amount.to_bytes_be()), - managed_address!(&source), - code_metadata, - boxed_bytes_vec_to_managed(arguments).into(), - ), - } - }, - |r| match expected_message { - Some(msg) => r.assert_user_error(msg), - None => r.assert_ok(), - }, - ); + ), + ActionRaw::SendAsyncCall(call_data) => sc.propose_async_call( + managed_address!(&call_data.to), + call_data.egld_amount.into(), + FunctionCall { + function_name: call_data.endpoint_name.into(), + arg_buffer: call_data.arguments.into(), + }, + ), + ActionRaw::SCDeployFromSource { + amount, + source, + code_metadata, + arguments, + } => sc.propose_sc_deploy_from_source( + amount.into(), + managed_address!(&source), + code_metadata, + boxed_bytes_vec_to_managed(arguments).into(), + ), + ActionRaw::SCUpgradeFromSource { + sc_address, + amount, + source, + code_metadata, + arguments, + } => sc.propose_sc_upgrade_from_source( + managed_address!(&sc_address), + amount.into(), + managed_address!(&source), + code_metadata, + boxed_bytes_vec_to_managed(arguments).into(), + ), + } + }); action_id } @@ -190,341 +174,300 @@ fn call_propose( #[test] fn test_add_board_member() { let mut world = setup(); - let multisig_whitebox = WhiteboxContract::new(MULTISIG_ADDRESS_EXPR, multisig::contract_obj); - const NEW_BOARD_MEMBER_ADDRESS_EXPR: &str = "address:new-board-member"; - world.set_state_step( - SetStateStep::new().put_account(NEW_BOARD_MEMBER_ADDRESS_EXPR, Account::new().nonce(1)), - ); + const NEW_BOARD_MEMBER_ADDRESS: TestAddress = TestAddress::new("new-board-member"); + world.account(NEW_BOARD_MEMBER_ADDRESS).nonce(1); - world.whitebox_query(&multisig_whitebox, |sc| { - // check role before - let user_role = sc.user_role(managed_address!(&address_expr_to_address( - NEW_BOARD_MEMBER_ADDRESS_EXPR - ))); - assert_eq!(user_role, UserRole::None); - }); + world + .query() + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { + // check role before + let user_role = sc.user_role(NEW_BOARD_MEMBER_ADDRESS.to_managed_address()); + assert_eq!(user_role, UserRole::None); + }); let action_id = call_propose( &mut world, - ActionRaw::AddBoardMember(address_expr_to_address(NEW_BOARD_MEMBER_ADDRESS_EXPR)), + ActionRaw::AddBoardMember(NEW_BOARD_MEMBER_ADDRESS.to_address()), None, ); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| sc.sign(action_id), - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.sign(action_id)); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { let _ = sc.perform_action_endpoint(action_id); - }, - ); + }); - world.whitebox_query(&multisig_whitebox, |sc| { - // check role after - let user_role = sc.user_role(managed_address!(&address_expr_to_address( - NEW_BOARD_MEMBER_ADDRESS_EXPR - ))); - assert_eq!(user_role, UserRole::BoardMember); - - let board_members = sc.get_all_board_members().to_vec(); - assert_eq!( - (board_members.get(0).borrow() as &ManagedAddress).clone(), - managed_address!(&address_expr_to_address(BOARD_MEMBER_ADDRESS_EXPR)) - ); - assert_eq!( - (board_members.get(1).borrow() as &ManagedAddress).clone(), - managed_address!(&address_expr_to_address(NEW_BOARD_MEMBER_ADDRESS_EXPR)) - ); - }); + world + .query() + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { + // check role after + let user_role = sc.user_role(NEW_BOARD_MEMBER_ADDRESS.to_managed_address()); + assert_eq!(user_role, UserRole::BoardMember); + + let board_members = sc.get_all_board_members().to_vec(); + assert_eq!(*board_members.get(0), BOARD_MEMBER_ADDRESS); + assert_eq!(*board_members.get(1), NEW_BOARD_MEMBER_ADDRESS); + }); } #[test] fn test_add_proposer() { let mut world = setup(); - let multisig_whitebox = WhiteboxContract::new(MULTISIG_ADDRESS_EXPR, multisig::contract_obj); - const NEW_PROPOSER_ADDRESS_EXPR: &str = "address:new-proposer"; - world.set_state_step( - SetStateStep::new().put_account(NEW_PROPOSER_ADDRESS_EXPR, Account::new().nonce(1)), - ); + const NEW_PROPOSER_ADDRESS: TestAddress = TestAddress::new("new-proposer"); + world.account(NEW_PROPOSER_ADDRESS).nonce(1); - world.whitebox_query(&multisig_whitebox, |sc| { - // check role before - let user_role = sc.user_role(managed_address!(&address_expr_to_address( - NEW_PROPOSER_ADDRESS_EXPR - ))); - assert_eq!(user_role, UserRole::None); - }); + world + .query() + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { + // check role before + let user_role = sc.user_role(NEW_PROPOSER_ADDRESS.to_managed_address()); + assert_eq!(user_role, UserRole::None); + }); let action_id = call_propose( &mut world, - ActionRaw::AddProposer(address_expr_to_address(NEW_PROPOSER_ADDRESS_EXPR)), + ActionRaw::AddProposer(NEW_PROPOSER_ADDRESS.to_address()), None, ); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| sc.sign(action_id), - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.sign(action_id)); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { let _ = sc.perform_action_endpoint(action_id); - }, - ); + }); - world.whitebox_query(&multisig_whitebox, |sc| { - // check role after - let user_role = sc.user_role(managed_address!(&address_expr_to_address( - NEW_PROPOSER_ADDRESS_EXPR - ))); - assert_eq!(user_role, UserRole::Proposer); - - let proposers = sc.get_all_proposers().to_vec(); - assert_eq!( - (proposers.get(0).borrow() as &ManagedAddress).clone(), - managed_address!(&address_expr_to_address(PROPOSER_ADDRESS_EXPR)) - ); - assert_eq!( - (proposers.get(1).borrow() as &ManagedAddress).clone(), - managed_address!(&address_expr_to_address(NEW_PROPOSER_ADDRESS_EXPR)) - ); - }); + world + .query() + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { + // check role after + let user_role = sc.user_role(NEW_PROPOSER_ADDRESS.to_managed_address()); + assert_eq!(user_role, UserRole::Proposer); + + let proposers = sc.get_all_proposers().to_vec(); + assert_eq!(*proposers.get(0), PROPOSER_ADDRESS); + assert_eq!(*proposers.get(1), NEW_PROPOSER_ADDRESS); + }); } #[test] fn test_remove_proposer() { let mut world = setup(); - let multisig_whitebox = WhiteboxContract::new(MULTISIG_ADDRESS_EXPR, multisig::contract_obj); - - world.whitebox_query(&multisig_whitebox, |sc| { - // check role before - let user_role = sc.user_role(managed_address!(&address_expr_to_address( - PROPOSER_ADDRESS_EXPR - ))); - assert_eq!(user_role, UserRole::Proposer); - }); + + world + .query() + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { + // check role before + let user_role = sc.user_role(PROPOSER_ADDRESS.to_managed_address()); + assert_eq!(user_role, UserRole::Proposer); + }); let action_id = call_propose( &mut world, - ActionRaw::RemoveUser(address_expr_to_address(PROPOSER_ADDRESS_EXPR)), + ActionRaw::RemoveUser(PROPOSER_ADDRESS.to_address()), None, ); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| sc.sign(action_id), - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.sign(action_id)); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { let _ = sc.perform_action_endpoint(action_id); - }, - ); + }); - world.whitebox_query(&multisig_whitebox, |sc| { - // check role after - let user_role = sc.user_role(managed_address!(&address_expr_to_address( - PROPOSER_ADDRESS_EXPR - ))); - assert_eq!(user_role, UserRole::None); - - let proposers = sc.get_all_proposers().to_vec(); - assert!(proposers.is_empty()); - }); + world + .query() + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { + // check role after + let user_role = sc.user_role(PROPOSER_ADDRESS.to_managed_address()); + assert_eq!(user_role, UserRole::None); + + let proposers = sc.get_all_proposers(); + assert!(proposers.is_empty()); + }); } #[test] fn test_try_remove_all_board_members() { let mut world = setup(); - let multisig_whitebox = WhiteboxContract::new(MULTISIG_ADDRESS_EXPR, multisig::contract_obj); let action_id = call_propose( &mut world, - ActionRaw::RemoveUser(address_expr_to_address(BOARD_MEMBER_ADDRESS_EXPR)), + ActionRaw::RemoveUser(BOARD_MEMBER_ADDRESS.to_address()), None, ); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| sc.sign(action_id), - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.sign(action_id)); - world.whitebox_call_check( - &multisig_whitebox, - ScCallStep::new() - .from(BOARD_MEMBER_ADDRESS_EXPR) - .no_expect(), - |sc| { + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .returns(ExpectError(4u64, "quorum cannot exceed board size")) + .whitebox(multisig::contract_obj, |sc| { let _ = sc.perform_action_endpoint(action_id); - }, - |r| { - r.assert_user_error("quorum cannot exceed board size"); - }, - ); + }); } #[test] fn test_change_quorum() { let mut world = setup(); - let multisig_whitebox = WhiteboxContract::new(MULTISIG_ADDRESS_EXPR, multisig::contract_obj); let new_quorum_size = 2; // try change quorum > board size let action_id = call_propose(&mut world, ActionRaw::ChangeQuorum(new_quorum_size), None); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| sc.sign(action_id), - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.sign(action_id)); - world.whitebox_call_check( - &multisig_whitebox, - ScCallStep::new() - .from(BOARD_MEMBER_ADDRESS_EXPR) - .no_expect(), - |sc| { + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .returns(ExpectError(4u64, "quorum cannot exceed board size")) + .whitebox(multisig::contract_obj, |sc| { let _ = sc.perform_action_endpoint(action_id); - }, - |r| { - r.assert_user_error("quorum cannot exceed board size"); - }, - ); + }); // try discard before unsigning - world.whitebox_call_check( - &multisig_whitebox, - ScCallStep::new() - .from(BOARD_MEMBER_ADDRESS_EXPR) - .no_expect(), - |sc| { - sc.discard_action(action_id); - }, - |r| { - r.assert_user_error("cannot discard action with valid signatures"); - }, - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .returns(ExpectError( + 4u64, + "cannot discard action with valid signatures", + )) + .whitebox(multisig::contract_obj, |sc| sc.discard_action(action_id)); // unsign and discard action - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| sc.unsign(action_id), - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.unsign(action_id)); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| { - sc.discard_action(action_id); - }, - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.discard_action(action_id)); // try sign discarded action - world.whitebox_call_check( - &multisig_whitebox, - ScCallStep::new() - .from(BOARD_MEMBER_ADDRESS_EXPR) - .no_expect(), - |sc| { + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .returns(ExpectError(4u64, "action does not exist")) + .whitebox(multisig::contract_obj, |sc| { sc.sign(action_id); - }, - |r| { - r.assert_user_error("action does not exist"); - }, - ); + }); // add another board member - const NEW_BOARD_MEMBER_ADDRESS_EXPR: &str = "address:new-board-member"; - world.set_state_step( - SetStateStep::new().put_account(NEW_BOARD_MEMBER_ADDRESS_EXPR, Account::new().nonce(1)), - ); + const NEW_BOARD_MEMBER_ADDRESS: TestAddress = TestAddress::new("new-board-member"); + world.account(NEW_BOARD_MEMBER_ADDRESS).nonce(1); let action_id = call_propose( &mut world, - ActionRaw::AddBoardMember(address_expr_to_address(NEW_BOARD_MEMBER_ADDRESS_EXPR)), + ActionRaw::AddBoardMember(NEW_BOARD_MEMBER_ADDRESS.to_address()), None, ); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| sc.sign(action_id), - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.sign(action_id)); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { let _ = sc.perform_action_endpoint(action_id); - }, - ); + }); // change quorum to 2 let action_id = call_propose(&mut world, ActionRaw::ChangeQuorum(new_quorum_size), None); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| sc.sign(action_id), - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.sign(action_id)); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { let _ = sc.perform_action_endpoint(action_id); - }, - ); + }); } #[test] fn test_transfer_execute_to_user() { let mut world = setup(); - let multisig_whitebox = WhiteboxContract::new(MULTISIG_ADDRESS_EXPR, multisig::contract_obj); - const NEW_USER_ADDRESS_EXPR: &str = "address:new-user"; - world.set_state_step( - SetStateStep::new().put_account(NEW_USER_ADDRESS_EXPR, Account::new().nonce(1)), - ); + const NEW_USER_ADDRESS: TestAddress = TestAddress::new("new-user"); + world.account(NEW_USER_ADDRESS).nonce(1); const EGLD_AMOUNT: u64 = 100; - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new() - .from(PROPOSER_ADDRESS_EXPR) - .egld_value(EGLD_AMOUNT), - |sc| { + world + .tx() + .from(PROPOSER_ADDRESS) + .to(MULTISIG_ADDRESS) + .egld(EGLD_AMOUNT) + .whitebox(multisig::contract_obj, |sc| { sc.deposit(); - }, - ); + }); - world.check_state_step(CheckStateStep::new().put_account( - MULTISIG_ADDRESS_EXPR, - CheckAccount::new().balance(EGLD_AMOUNT.to_string().as_str()), - )); + world.check_account(MULTISIG_ADDRESS).balance(EGLD_AMOUNT); // failed attempt let action_id = call_propose( &mut world, ActionRaw::SendTransferExecute(CallActionDataRaw { - to: address_expr_to_address(NEW_USER_ADDRESS_EXPR), + to: NEW_USER_ADDRESS.to_address(), egld_amount: rust_biguint!(0), endpoint_name: BoxedBytes::empty(), arguments: Vec::new(), @@ -536,7 +479,7 @@ fn test_transfer_execute_to_user() { let action_id = call_propose( &mut world, ActionRaw::SendTransferExecute(CallActionDataRaw { - to: address_expr_to_address(NEW_USER_ADDRESS_EXPR), + to: NEW_USER_ADDRESS.to_address(), egld_amount: rust_biguint!(EGLD_AMOUNT), endpoint_name: BoxedBytes::empty(), arguments: Vec::new(), @@ -544,59 +487,48 @@ fn test_transfer_execute_to_user() { None, ); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| sc.sign(action_id), - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.sign(action_id)); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { let _ = sc.perform_action_endpoint(action_id); - }, - ); + }); - world.check_state_step(CheckStateStep::new().put_account( - NEW_USER_ADDRESS_EXPR, - CheckAccount::new().balance(EGLD_AMOUNT.to_string().as_str()), - )); + world.check_account(NEW_USER_ADDRESS).balance(EGLD_AMOUNT); } #[test] fn test_transfer_execute_sc_all() { let mut world = setup(); - let multisig_whitebox = WhiteboxContract::new(MULTISIG_ADDRESS_EXPR, multisig::contract_obj); - - let adder_whitebox = WhiteboxContract::new(ADDER_ADDRESS_EXPR, adder::contract_obj); - let adder_code = world.code_expression(ADDER_PATH_EXPR); - const ADDER_OWNER_ADDRESS_EXPR: &str = "address:adder-owner"; - const ADDER_ADDRESS_EXPR: &str = "sc:adder"; - const ADDER_PATH_EXPR: &str = "mxsc:test-contracts/adder.mxsc.json"; + const ADDER_OWNER_ADDRESS: TestAddress = TestAddress::new("adder-owner"); + const ADDER_ADDRESS: TestSCAddress = TestSCAddress::new("adder"); + const ADDER_PATH_EXPR: MxscPath = MxscPath::new("mxsc:test-contracts/adder.mxsc.json"); world.register_contract(ADDER_PATH_EXPR, adder::ContractBuilder); - world.set_state_step( - SetStateStep::new() - .put_account(ADDER_OWNER_ADDRESS_EXPR, Account::new().nonce(1)) - .new_address(ADDER_OWNER_ADDRESS_EXPR, 1, ADDER_ADDRESS_EXPR), - ); + world.account(ADDER_OWNER_ADDRESS).nonce(1); - world.whitebox_deploy( - &adder_whitebox, - ScDeployStep::new() - .from(ADDER_OWNER_ADDRESS_EXPR) - .code(adder_code), - |sc| { - sc.init(managed_biguint!(5)); - }, - ); + world + .tx() + .raw_deploy() + .from(ADDER_OWNER_ADDRESS) + .code(ADDER_PATH_EXPR) + .new_address(ADDER_ADDRESS) + .whitebox(adder::contract_obj, |sc| { + sc.init(BigUint::from(5u64)); + }); let action_id = call_propose( &mut world, ActionRaw::SendTransferExecute(CallActionDataRaw { - to: address_expr_to_address(ADDER_ADDRESS_EXPR), + to: ADDER_ADDRESS.to_address(), egld_amount: 0u64.into(), endpoint_name: BoxedBytes::from(&b"add"[..]), arguments: vec![BoxedBytes::from(&[5u8][..])], @@ -604,60 +536,55 @@ fn test_transfer_execute_sc_all() { None, ); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| sc.sign(action_id), - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.sign(action_id)); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { let _ = sc.perform_action_endpoint(action_id); - }, - ); + }); - world.whitebox_query(&adder_whitebox, |sc| { - let actual_sum = sc.sum().get(); - let expected_sum = managed_biguint!(10); - assert_eq!(actual_sum, expected_sum); - }); + world + .query() + .to(ADDER_ADDRESS) + .whitebox(adder::contract_obj, |sc| { + let actual_sum = sc.sum().get(); + let expected_sum = managed_biguint!(10); + assert_eq!(actual_sum, expected_sum); + }); } #[test] fn test_async_call_to_sc() { let mut world = setup(); - let multisig_whitebox = WhiteboxContract::new(MULTISIG_ADDRESS_EXPR, multisig::contract_obj); - - let adder_whitebox = WhiteboxContract::new(ADDER_ADDRESS_EXPR, adder::contract_obj); - let adder_code = world.code_expression(ADDER_PATH_EXPR); - const ADDER_OWNER_ADDRESS_EXPR: &str = "address:adder-owner"; - const ADDER_ADDRESS_EXPR: &str = "sc:adder"; - const ADDER_PATH_EXPR: &str = "mxsc:test-contracts/adder.mxsc.json"; + const ADDER_OWNER_ADDRESS: TestAddress = TestAddress::new("adder-owner"); + const ADDER_ADDRESS: TestSCAddress = TestSCAddress::new("adder"); + const ADDER_PATH_EXPR: MxscPath = MxscPath::new("mxsc:test-contracts/adder.mxsc.json"); world.register_contract(ADDER_PATH_EXPR, adder::ContractBuilder); - world.set_state_step( - SetStateStep::new() - .put_account(ADDER_OWNER_ADDRESS_EXPR, Account::new().nonce(1)) - .new_address(ADDER_OWNER_ADDRESS_EXPR, 1, ADDER_ADDRESS_EXPR), - ); + world.account(ADDER_OWNER_ADDRESS).nonce(1); - world.whitebox_deploy( - &adder_whitebox, - ScDeployStep::new() - .from(ADDER_OWNER_ADDRESS_EXPR) - .code(adder_code), - |sc| { - sc.init(managed_biguint!(5)); - }, - ); + world + .tx() + .raw_deploy() + .from(ADDER_OWNER_ADDRESS) + .code(ADDER_PATH_EXPR) + .new_address(ADDER_ADDRESS) + .whitebox(adder::contract_obj, |sc| { + sc.init(BigUint::from(5u64)); + }); let action_id = call_propose( &mut world, ActionRaw::SendAsyncCall(CallActionDataRaw { - to: address_expr_to_address(ADDER_ADDRESS_EXPR), + to: ADDER_ADDRESS.to_address(), egld_amount: 0u64.into(), endpoint_name: BoxedBytes::from(&b"add"[..]), arguments: vec![BoxedBytes::from(&[5u8][..])], @@ -665,93 +592,90 @@ fn test_async_call_to_sc() { None, ); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| sc.sign(action_id), - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.sign(action_id)); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { let _ = sc.perform_action_endpoint(action_id); - }, - ); + }); - world.whitebox_query(&adder_whitebox, |sc| { - let actual_sum = sc.sum().get(); - let expected_sum = managed_biguint!(10); - assert_eq!(actual_sum, expected_sum); - }); + world + .query() + .to(ADDER_ADDRESS) + .whitebox(adder::contract_obj, |sc| { + let actual_sum = sc.sum().get(); + let expected_sum = managed_biguint!(10); + assert_eq!(actual_sum, expected_sum); + }); } #[test] fn test_deploy_and_upgrade_from_source() { let mut world = setup(); - let multisig_whitebox = WhiteboxContract::new(MULTISIG_ADDRESS_EXPR, multisig::contract_obj); + const NEW_ADDER_ADDRESS: TestSCAddress = TestSCAddress::new("new-adder"); - let adder_whitebox = WhiteboxContract::new(ADDER_ADDRESS_EXPR, adder::contract_obj); - let adder_code = world.code_expression(ADDER_PATH_EXPR); - - let new_adder_whitebox = WhiteboxContract::new(NEW_ADDER_ADDRESS_EXPR, adder::contract_obj); - - const ADDER_OWNER_ADDRESS_EXPR: &str = "address:adder-owner"; - const ADDER_ADDRESS_EXPR: &str = "sc:adder"; - const NEW_ADDER_ADDRESS_EXPR: &str = "sc:new-adder"; - const ADDER_PATH_EXPR: &str = "mxsc:test-contracts/adder.mxsc.json"; + const ADDER_OWNER_ADDRESS: TestAddress = TestAddress::new("adder-owner"); + const ADDER_ADDRESS: TestSCAddress = TestSCAddress::new("adder"); + const ADDER_PATH_EXPR: MxscPath = MxscPath::new("mxsc:test-contracts/adder.mxsc.json"); world.register_contract(ADDER_PATH_EXPR, adder::ContractBuilder); - world.set_state_step( - SetStateStep::new() - .put_account(ADDER_OWNER_ADDRESS_EXPR, Account::new().nonce(1)) - .new_address(ADDER_OWNER_ADDRESS_EXPR, 1, ADDER_ADDRESS_EXPR) - .new_address(MULTISIG_ADDRESS_EXPR, 0, NEW_ADDER_ADDRESS_EXPR), - ); + world.set_state_step(SetStateStep::new().new_address( + MULTISIG_ADDRESS.eval_to_expr().as_str(), + 0, + NEW_ADDER_ADDRESS.eval_to_expr().as_str(), + )); - world.whitebox_deploy( - &adder_whitebox, - ScDeployStep::new() - .from(ADDER_OWNER_ADDRESS_EXPR) - .code(adder_code), - |sc| { - sc.init(managed_biguint!(5)); - }, - ); + world.account(ADDER_OWNER_ADDRESS).nonce(1); + + world + .tx() + .raw_deploy() + .from(ADDER_OWNER_ADDRESS) + .code(ADDER_PATH_EXPR) + .new_address(ADDER_ADDRESS) + .whitebox(adder::contract_obj, |sc| { + sc.init(BigUint::from(5u64)); + }); let action_id = call_propose( &mut world, ActionRaw::SCDeployFromSource { amount: 0u64.into(), - source: address_expr_to_address(ADDER_ADDRESS_EXPR), + source: ADDER_ADDRESS.to_address(), code_metadata: CodeMetadata::all(), arguments: vec![BoxedBytes::from(&[5u8][..])], }, None, ); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| sc.sign(action_id), - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.sign(action_id)); - let mut addr = Address::zero(); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { let opt_address = sc.perform_action_endpoint(action_id); - addr = opt_address.into_option().unwrap().to_address(); - }, - ); + let addr = opt_address.into_option().unwrap().to_address(); - assert_eq!(address_expr_to_address(NEW_ADDER_ADDRESS_EXPR), addr); + assert_eq!(NEW_ADDER_ADDRESS.to_address(), addr); + }); let action_id = call_propose( &mut world, ActionRaw::SendTransferExecute(CallActionDataRaw { - to: address_expr_to_address(NEW_ADDER_ADDRESS_EXPR), + to: NEW_ADDER_ADDRESS.to_address(), egld_amount: 0u64.into(), endpoint_name: BoxedBytes::from(&b"add"[..]), arguments: vec![BoxedBytes::from(&[5u8][..])], @@ -759,71 +683,70 @@ fn test_deploy_and_upgrade_from_source() { None, ); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| sc.sign(action_id), - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.sign(action_id)); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { let _ = sc.perform_action_endpoint(action_id); - }, - ); + }); - world.whitebox_query(&new_adder_whitebox, |sc| { - let actual_sum = sc.sum().get(); - let expected_sum = managed_biguint!(10); - assert_eq!(actual_sum, expected_sum); - }); - - let factorial_code = world.code_expression(FACTORIAL_PATH_EXPR); + world + .query() + .to(NEW_ADDER_ADDRESS) + .whitebox(adder::contract_obj, |sc| { + let actual_sum = sc.sum().get(); + let expected_sum = managed_biguint!(10); + assert_eq!(actual_sum, expected_sum); + }); - const FACTORIAL_ADDRESS_EXPR: &str = "sc:factorial"; - const FACTORIAL_PATH_EXPR: &str = "mxsc:test-contracts/factorial.mxsc.json"; + const FACTORIAL_ADDRESS: TestSCAddress = TestSCAddress::new("factorial"); + const FACTORIAL_PATH_EXPR: MxscPath = MxscPath::new("mxsc:test-contracts/factorial.mxsc.json"); world.register_contract(FACTORIAL_PATH_EXPR, factorial::ContractBuilder); - world.set_state_step(SetStateStep::new().put_account( - FACTORIAL_ADDRESS_EXPR, - Account::new().nonce(1).code(factorial_code.clone()), - )); + world + .tx() + .raw_deploy() + .from(OWNER_ADDRESS) + .code(FACTORIAL_PATH_EXPR) + .new_address(FACTORIAL_ADDRESS) + .whitebox(factorial::contract_obj, |sc| { + sc.init(); + }); let action_id = call_propose( &mut world, ActionRaw::SCUpgradeFromSource { - source: address_expr_to_address(FACTORIAL_ADDRESS_EXPR), + source: FACTORIAL_ADDRESS.to_address(), amount: 0u64.into(), code_metadata: CodeMetadata::all(), arguments: Vec::new(), - sc_address: address_expr_to_address(ADDER_ADDRESS_EXPR), + sc_address: ADDER_ADDRESS.to_address(), }, None, ); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| sc.sign(action_id), - ); + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| sc.sign(action_id)); - world.whitebox_call( - &multisig_whitebox, - ScCallStep::new().from(BOARD_MEMBER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(BOARD_MEMBER_ADDRESS) + .to(MULTISIG_ADDRESS) + .whitebox(multisig::contract_obj, |sc| { let _ = sc.perform_action_endpoint(action_id); - }, - ); - - world.check_state_step( - CheckStateStep::new() - .put_account(ADDER_ADDRESS_EXPR, CheckAccount::new().code(factorial_code)), - ); -} + }); -fn address_expr_to_address(address_expr: &str) -> Address { - AddressValue::from(address_expr).to_address() + world.check_account(ADDER_ADDRESS).code(FACTORIAL_PATH_EXPR); } fn boxed_bytes_vec_to_managed( diff --git a/contracts/feature-tests/composability/transfer-role-features/tests/transfer_role_whitebox_test.rs b/contracts/feature-tests/composability/transfer-role-features/tests/transfer_role_whitebox_test.rs index adf9582982..130c2b237c 100644 --- a/contracts/feature-tests/composability/transfer-role-features/tests/transfer_role_whitebox_test.rs +++ b/contracts/feature-tests/composability/transfer-role-features/tests/transfer_role_whitebox_test.rs @@ -1,15 +1,17 @@ use multiversx_sc_modules::transfer_role_proxy::TransferRoleProxyModule; use multiversx_sc_scenario::imports::*; use transfer_role_features::TransferRoleFeatures; +use vault::Vault; -const OWNER_ADDRESS_EXPR: &str = "address:owner"; -const USER_ADDRESS_EXPR: &str = "address:user"; +const OWNER_ADDRESS: TestAddress = TestAddress::new("owner"); +const USER_ADDRESS: TestAddress = TestAddress::new("user"); -const TRANSFER_ROLE_FEATURES_ADDRESS_EXPR: &str = "sc:transfer-role-features"; -const TRANSFER_ROLE_FEATURES_PATH_EXPR: &str = "mxsc:output/transfer-role-features.mxsc.json"; +const TRANSFER_ROLE_FEATURES_ADDRESS: TestSCAddress = TestSCAddress::new("transfer-role-features"); +const TRANSFER_ROLE_FEATURES_PATH_EXPR: MxscPath = + MxscPath::new("mxsc:output/transfer-role-features.mxsc.json"); -const TRANSFER_TOKEN_ID_EXPR: &str = "str:TRANSFER-123456"; -const TRANSFER_TOKEN_ID: &[u8] = b"TRANSFER-123456"; +const TRANSFER_TOKEN_ID: TestTokenIdentifier = TestTokenIdentifier::new("TRANSFER-123456"); +const TRANSFER_TOKEN_ID_EXPR: &[u8] = b"TRANSFER-123456"; const ACCEPT_FUNDS_FUNC_NAME: &[u8] = b"accept_funds"; const REJECT_FUNDS_FUNC_NAME: &[u8] = b"reject_funds"; @@ -27,175 +29,161 @@ fn world() -> ScenarioWorld { fn test_transfer_role() { let mut world = world(); - world.set_state_step( - SetStateStep::new() - .put_account(OWNER_ADDRESS_EXPR, Account::new().nonce(1)) - .new_address(OWNER_ADDRESS_EXPR, 1, TRANSFER_ROLE_FEATURES_ADDRESS_EXPR) - .put_account( - USER_ADDRESS_EXPR, - Account::new() - .nonce(1) - .esdt_balance(TRANSFER_TOKEN_ID_EXPR, 1_000u64), - ), - ); + world.account(OWNER_ADDRESS).nonce(1); + world + .account(USER_ADDRESS) + .nonce(1) + .esdt_balance(TRANSFER_TOKEN_ID, BigUint::from(1_000u64)); // vault - let vault_code = world.code_expression(VAULT_PATH_EXPR); - - const VAULT_ADDRESS_EXPR: &str = "sc:vault"; - const VAULT_PATH_EXPR: &str = "mxsc:../vault/output/vault.mxsc.json"; + const VAULT_ADDRESS: TestSCAddress = TestSCAddress::new("vault"); + const VAULT_PATH_EXPR: MxscPath = MxscPath::new("mxsc:../vault/output/vault.mxsc.json"); world.register_contract(VAULT_PATH_EXPR, vault::ContractBuilder); - world.set_state_step( - SetStateStep::new() - .put_account(VAULT_ADDRESS_EXPR, Account::new().nonce(1).code(vault_code)), - ); - - let transfer_role_features_whitebox = WhiteboxContract::new( - TRANSFER_ROLE_FEATURES_ADDRESS_EXPR, - transfer_role_features::contract_obj, - ); - let transfer_role_features_code = world.code_expression(TRANSFER_ROLE_FEATURES_PATH_EXPR); + world + .tx() + .from(OWNER_ADDRESS) + .raw_deploy() + .new_address(VAULT_ADDRESS) + .code(VAULT_PATH_EXPR) + .whitebox(vault::contract_obj, |sc| { + let _ = sc.init(OptionalValue::None); + }); // init - world.whitebox_deploy( - &transfer_role_features_whitebox, - ScDeployStep::new() - .from(OWNER_ADDRESS_EXPR) - .code(transfer_role_features_code), - |sc| { + world + .tx() + .from(OWNER_ADDRESS) + .raw_deploy() + .new_address(TRANSFER_ROLE_FEATURES_ADDRESS) + .code(TRANSFER_ROLE_FEATURES_PATH_EXPR) + .whitebox(transfer_role_features::contract_obj, |sc| { let mut whitelist = MultiValueEncoded::new(); - whitelist.push(managed_address!(&address_expr_to_address( - OWNER_ADDRESS_EXPR - ))); - whitelist.push(managed_address!(&address_expr_to_address( - VAULT_ADDRESS_EXPR - ))); + whitelist.push(OWNER_ADDRESS.to_managed_address()); + whitelist.push(VAULT_ADDRESS.to_managed_address()); sc.init(whitelist); - }, - ); + }); // transfer to user - ok - world.whitebox_call( - &transfer_role_features_whitebox, - ScCallStep::new() - .from(USER_ADDRESS_EXPR) - .esdt_transfer(TRANSFER_TOKEN_ID, 0, "100"), - |sc| { + world + .tx() + .from(USER_ADDRESS) + .to(TRANSFER_ROLE_FEATURES_ADDRESS) + .single_esdt( + &TokenIdentifier::from(TRANSFER_TOKEN_ID), + 0, + &BigUint::from(100u64), + ) + .whitebox(transfer_role_features::contract_obj, |sc| { let payments = ManagedVec::from_single_item(EsdtTokenPayment::new( - managed_token_id!(TRANSFER_TOKEN_ID), + managed_token_id!(TRANSFER_TOKEN_ID_EXPR), 0, managed_biguint!(100), )); sc.transfer_to_user( - managed_address!(&address_expr_to_address(USER_ADDRESS_EXPR)), - managed_address!(&address_expr_to_address(OWNER_ADDRESS_EXPR)), + USER_ADDRESS.to_managed_address(), + OWNER_ADDRESS.to_managed_address(), &payments, managed_buffer!(b"enjoy"), ); - }, - ); + }); - world.check_state_step(CheckStateStep::new().put_account( - USER_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(TRANSFER_TOKEN_ID_EXPR, "900"), - )); - world.check_state_step(CheckStateStep::new().put_account( - OWNER_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(TRANSFER_TOKEN_ID_EXPR, "100"), - )); + world + .check_account(USER_ADDRESS) + .esdt_balance(TRANSFER_TOKEN_ID, BigUint::from(900u64)); + world + .check_account(OWNER_ADDRESS) + .esdt_balance(TRANSFER_TOKEN_ID, BigUint::from(100u64)); // transfer to user - err, not whitelisted - world.whitebox_call_check( - &transfer_role_features_whitebox, - ScCallStep::new() - .from(USER_ADDRESS_EXPR) - .esdt_transfer(TRANSFER_TOKEN_ID, 0, "100") - .no_expect(), - |sc| { + world + .tx() + .from(USER_ADDRESS) + .to(TRANSFER_ROLE_FEATURES_ADDRESS) + .single_esdt( + &TokenIdentifier::from(TRANSFER_TOKEN_ID), + 0, + &BigUint::from(100u64), + ) + .returns(ExpectError(4u64, "Destination address not whitelisted")) + .whitebox(transfer_role_features::contract_obj, |sc| { let payments = ManagedVec::from_single_item(EsdtTokenPayment::new( - managed_token_id!(TRANSFER_TOKEN_ID), + managed_token_id!(TRANSFER_TOKEN_ID_EXPR), 0, managed_biguint!(100), )); sc.transfer_to_user( - managed_address!(&address_expr_to_address(USER_ADDRESS_EXPR)), + USER_ADDRESS.to_managed_address(), managed_address!(&Address::zero()), &payments, managed_buffer!(b"enjoy"), ); - }, - |r| { - r.assert_user_error("Destination address not whitelisted"); - }, - ); + }); // transfer to sc - ok - world.whitebox_call( - &transfer_role_features_whitebox, - ScCallStep::new() - .from(USER_ADDRESS_EXPR) - .esdt_transfer(TRANSFER_TOKEN_ID, 0, "100"), - |sc| { + world + .tx() + .from(USER_ADDRESS) + .to(TRANSFER_ROLE_FEATURES_ADDRESS) + .single_esdt( + &TokenIdentifier::from(TRANSFER_TOKEN_ID), + 0, + &BigUint::from(100u64), + ) + .whitebox(transfer_role_features::contract_obj, |sc| { let payments = ManagedVec::from_single_item(EsdtTokenPayment::new( - managed_token_id!(TRANSFER_TOKEN_ID), + managed_token_id!(TRANSFER_TOKEN_ID_EXPR), 0, managed_biguint!(100), )); sc.transfer_to_contract_raw( - managed_address!(&address_expr_to_address(USER_ADDRESS_EXPR)), - managed_address!(&address_expr_to_address(VAULT_ADDRESS_EXPR)), + USER_ADDRESS.to_managed_address(), + VAULT_ADDRESS.to_managed_address(), &payments, managed_buffer!(ACCEPT_FUNDS_FUNC_NAME), ManagedArgBuffer::new(), None, ); - }, - ); + }); - world.check_state_step(CheckStateStep::new().put_account( - USER_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(TRANSFER_TOKEN_ID_EXPR, "800"), - )); - world.check_state_step(CheckStateStep::new().put_account( - VAULT_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(TRANSFER_TOKEN_ID_EXPR, "100"), - )); + world + .check_account(USER_ADDRESS) + .esdt_balance(TRANSFER_TOKEN_ID, BigUint::from(800u64)); + world + .check_account(VAULT_ADDRESS) + .esdt_balance(TRANSFER_TOKEN_ID, BigUint::from(100u64)); // transfer to sc - reject - world.whitebox_call( - &transfer_role_features_whitebox, - ScCallStep::new() - .from(USER_ADDRESS_EXPR) - .esdt_transfer(TRANSFER_TOKEN_ID, 0, "100"), - |sc| { + world + .tx() + .from(USER_ADDRESS) + .to(TRANSFER_ROLE_FEATURES_ADDRESS) + .single_esdt( + &TokenIdentifier::from(TRANSFER_TOKEN_ID), + 0, + &BigUint::from(100u64), + ) + .whitebox(transfer_role_features::contract_obj, |sc| { let payments = ManagedVec::from_single_item(EsdtTokenPayment::new( - managed_token_id!(TRANSFER_TOKEN_ID), + managed_token_id!(TRANSFER_TOKEN_ID_EXPR), 0, managed_biguint!(100), )); sc.transfer_to_contract_raw( - managed_address!(&address_expr_to_address(USER_ADDRESS_EXPR)), - managed_address!(&address_expr_to_address(VAULT_ADDRESS_EXPR)), + USER_ADDRESS.to_managed_address(), + VAULT_ADDRESS.to_managed_address(), &payments, managed_buffer!(REJECT_FUNDS_FUNC_NAME), ManagedArgBuffer::new(), None, ); - }, - ); - - world.check_state_step(CheckStateStep::new().put_account( - USER_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(TRANSFER_TOKEN_ID_EXPR, "800"), - )); - world.check_state_step(CheckStateStep::new().put_account( - VAULT_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(TRANSFER_TOKEN_ID_EXPR, "100"), - )); -} - -fn address_expr_to_address(address_expr: &str) -> Address { - AddressValue::from(address_expr).to_address() + }); + + world + .check_account(USER_ADDRESS) + .esdt_balance(TRANSFER_TOKEN_ID, BigUint::from(800u64)); + world + .check_account(VAULT_ADDRESS) + .esdt_balance(TRANSFER_TOKEN_ID, BigUint::from(100u64)); } diff --git a/contracts/feature-tests/scenario-tester/tests/st_whitebox_test.rs b/contracts/feature-tests/scenario-tester/tests/st_whitebox_test.rs index a6318b1f7f..61dd6cdc2d 100644 --- a/contracts/feature-tests/scenario-tester/tests/st_whitebox_test.rs +++ b/contracts/feature-tests/scenario-tester/tests/st_whitebox_test.rs @@ -1,7 +1,9 @@ use multiversx_sc_scenario::imports::*; use scenario_tester::*; -const ADDER_PATH_EXPR: &str = "mxsc:output/scenario-tester.mxsc.json"; +const ST_PATH_EXPR: MxscPath = MxscPath::new("mxsc:output/scenario-tester.mxsc.json"); +const OWNER: TestAddress = TestAddress::new("owner"); +const SCENARIO_TESTER: TestSCAddress = TestSCAddress::new("scenario-tester"); fn world() -> ScenarioWorld { let mut blockchain = ScenarioWorld::new(); @@ -16,37 +18,37 @@ fn world() -> ScenarioWorld { #[test] fn st_whitebox() { let mut world = world(); - let st_whitebox = WhiteboxContract::new("sc:adder", scenario_tester::contract_obj); - let st_code = world.code_expression(ADDER_PATH_EXPR); + + world.account(OWNER).nonce(1); + + let new_address = world + .tx() + .from(OWNER) + .raw_deploy() + .code(ST_PATH_EXPR) + .new_address(SCENARIO_TESTER) + .returns(ReturnsNewBech32Address) + .whitebox(scenario_tester::contract_obj, |sc| { + sc.init(BigUint::from(5u64)); + }); + + assert_eq!(new_address.to_address(), SCENARIO_TESTER.to_address()); world - .set_state_step( - SetStateStep::new() - .put_account("address:owner", Account::new().nonce(1)) - .new_address("address:owner", 1, "sc:adder"), - ) - .whitebox_deploy( - &st_whitebox, - ScDeployStep::new().from("address:owner").code(st_code), - |sc| { - sc.init(5u32.into()); - }, - ) - .whitebox_query(&st_whitebox, |sc| { + .query() + .to(SCENARIO_TESTER) + .whitebox(scenario_tester::contract_obj, |sc| { let sum_value = sc.sum(); - assert_eq!(sum_value.get(), 5u32); - }) - .whitebox_call( - &st_whitebox, - ScCallStep::new().from("address:owner"), - |sc| sc.add(3u32.into()), - ) - .check_state_step( - CheckStateStep::new() - .put_account("address:owner", CheckAccount::new()) - .put_account( - "sc:adder", - CheckAccount::new().check_storage("str:sum", "8"), - ), - ); + assert_eq!(sum_value.get(), BigUint::from(5u32)); + }); + + world + .tx() + .from(OWNER) + .to(SCENARIO_TESTER) + .whitebox(scenario_tester::contract_obj, |sc| sc.add(3u32.into())); + + world + .check_account(SCENARIO_TESTER) + .check_storage("str:sum", "8"); } diff --git a/contracts/feature-tests/use-module/tests/gov_module_whitebox_test.rs b/contracts/feature-tests/use-module/tests/gov_module_whitebox_test.rs index 72b15aac66..288d3042f5 100644 --- a/contracts/feature-tests/use-module/tests/gov_module_whitebox_test.rs +++ b/contracts/feature-tests/use-module/tests/gov_module_whitebox_test.rs @@ -4,7 +4,7 @@ use multiversx_sc_modules::governance::{ }; use multiversx_sc_scenario::imports::*; -const GOV_TOKEN_ID_EXPR: &str = "str:GOV-123456"; +const GOV_TOKEN_ID_EXPR: TestTokenIdentifier = TestTokenIdentifier::new("GOV-123456"); const GOV_TOKEN_ID: &[u8] = b"GOV-123456"; const QUORUM: u64 = 1_500; const MIN_BALANCE_PROPOSAL: u64 = 500; @@ -15,13 +15,13 @@ const LOCKING_PERIOD_BLOCKS: u64 = 30; const INITIAL_GOV_TOKEN_BALANCE: u64 = 1_000; const GAS_LIMIT: u64 = 1_000_000; -const USE_MODULE_ADDRESS_EXPR: &str = "sc:use-module"; -const USE_MODULE_PATH_EXPR: &str = "mxsc:output/use-module.mxsc.json"; +const USE_MODULE_ADDRESS: TestSCAddress = TestSCAddress::new("use-module"); +const USE_MODULE_PATH_EXPR: MxscPath = MxscPath::new("mxsc:output/use-module.mxsc.json"); -const OWNER_ADDRESS_EXPR: &str = "address:owner"; -const FIRST_USER_ADDRESS_EXPR: &str = "address:first-user"; -const SECOND_USER_ADDRESS_EXPR: &str = "address:second-user"; -const THIRD_USER_ADDRESS_EXPR: &str = "address:third-user"; +const OWNER_ADDRESS: TestAddress = TestAddress::new("owner"); +const FIRST_USER_ADDRESS: TestAddress = TestAddress::new("first-user"); +const SECOND_USER_ADDRESS: TestAddress = TestAddress::new("second-user"); +const THIRD_USER_ADDRESS: TestAddress = TestAddress::new("third-user"); pub struct Payment { pub token: Vec, @@ -39,58 +39,45 @@ fn world() -> ScenarioWorld { fn setup() -> ScenarioWorld { let mut world = world(); - world.set_state_step( - SetStateStep::new() - .put_account( - OWNER_ADDRESS_EXPR, - Account::new() - .nonce(1) - .esdt_balance(GOV_TOKEN_ID_EXPR, INITIAL_GOV_TOKEN_BALANCE), - ) - .new_address(OWNER_ADDRESS_EXPR, 1, USE_MODULE_ADDRESS_EXPR) - .put_account( - FIRST_USER_ADDRESS_EXPR, - Account::new() - .nonce(1) - .esdt_balance(GOV_TOKEN_ID_EXPR, INITIAL_GOV_TOKEN_BALANCE), - ) - .put_account( - SECOND_USER_ADDRESS_EXPR, - Account::new() - .nonce(1) - .esdt_balance(GOV_TOKEN_ID_EXPR, INITIAL_GOV_TOKEN_BALANCE), - ) - .put_account( - THIRD_USER_ADDRESS_EXPR, - Account::new() - .nonce(1) - .esdt_balance(GOV_TOKEN_ID_EXPR, INITIAL_GOV_TOKEN_BALANCE), - ), - ); + world + .account(OWNER_ADDRESS) + .nonce(1) + .esdt_balance(GOV_TOKEN_ID_EXPR, INITIAL_GOV_TOKEN_BALANCE); + world + .account(FIRST_USER_ADDRESS) + .nonce(1) + .esdt_balance(GOV_TOKEN_ID_EXPR, INITIAL_GOV_TOKEN_BALANCE); + world + .account(SECOND_USER_ADDRESS) + .nonce(1) + .esdt_balance(GOV_TOKEN_ID_EXPR, INITIAL_GOV_TOKEN_BALANCE); + world + .account(THIRD_USER_ADDRESS) + .nonce(1) + .esdt_balance(GOV_TOKEN_ID_EXPR, INITIAL_GOV_TOKEN_BALANCE); // init - let use_module_whitebox = - WhiteboxContract::new(USE_MODULE_ADDRESS_EXPR, use_module::contract_obj); - let use_module_code = world.code_expression(USE_MODULE_PATH_EXPR); - - world.whitebox_deploy( - &use_module_whitebox, - ScDeployStep::new() - .from(OWNER_ADDRESS_EXPR) - .code(use_module_code), - |sc| { + let new_address = world + .tx() + .from(OWNER_ADDRESS) + .raw_deploy() + .code(USE_MODULE_PATH_EXPR) + .new_address(USE_MODULE_ADDRESS) + .returns(ReturnsNewBech32Address) + .whitebox(use_module::contract_obj, |sc| { sc.init_governance_module( - managed_token_id!(GOV_TOKEN_ID), - managed_biguint!(QUORUM), - managed_biguint!(MIN_BALANCE_PROPOSAL), + TokenIdentifier::from(GOV_TOKEN_ID_EXPR), + BigUint::from(QUORUM), + BigUint::from(MIN_BALANCE_PROPOSAL), VOTING_DELAY_BLOCKS, VOTING_PERIOD_BLOCKS, LOCKING_PERIOD_BLOCKS, ); - }, - ); + }); - world.set_state_step(SetStateStep::new().block_nonce(10)); + assert_eq!(new_address.to_address(), USE_MODULE_ADDRESS.to_address()); + + world.current_block().block_nonce(10); world } @@ -103,17 +90,18 @@ pub fn propose( endpoint_name: &[u8], args: Vec>, ) -> usize { - let use_module_whitebox = - WhiteboxContract::new(USE_MODULE_ADDRESS_EXPR, use_module::contract_obj); - let mut proposal_id = 0; - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new() - .from(proposer) - .esdt_transfer(GOV_TOKEN_ID, 0, gov_token_amount), - |sc| { + world + .tx() + .from(proposer) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(GOV_TOKEN_ID_EXPR), + 0, + &BigUint::from(gov_token_amount), + ) + .whitebox(use_module::contract_obj, |sc| { let mut args_managed = ManagedVec::new(); for arg in args { args_managed.push(managed_buffer!(&arg)); @@ -131,8 +119,7 @@ pub fn propose( ); proposal_id = sc.propose(managed_buffer!(b"change quorum"), actions); - }, - ); + }); proposal_id } @@ -145,16 +132,14 @@ fn test_init() { #[test] fn test_change_gov_config() { let mut world = setup(); - let use_module_whitebox = - WhiteboxContract::new(USE_MODULE_ADDRESS_EXPR, use_module::contract_obj); let mut current_block_nonce = 10; let proposal_id = propose( &mut world, - &address_expr_to_address(FIRST_USER_ADDRESS_EXPR), + &FIRST_USER_ADDRESS.to_address(), 500, - &address_expr_to_address(USE_MODULE_ADDRESS_EXPR), + &USE_MODULE_ADDRESS.to_address(), b"changeQuorum", vec![1_000u64.to_be_bytes().to_vec()], ); @@ -162,209 +147,212 @@ fn test_change_gov_config() { assert_eq!(proposal_id, 1); // vote too early - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new() - .from(SECOND_USER_ADDRESS_EXPR) - .esdt_transfer(GOV_TOKEN_ID, 0, "999") - .no_expect(), - |sc| { + world + .tx() + .from(SECOND_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(GOV_TOKEN_ID), + 0, + &BigUint::from(999u64), + ) + .returns(ExpectError(4u64, "Proposal is not active")) + .whitebox(use_module::contract_obj, |sc| { sc.vote(proposal_id, VoteType::UpVote); - }, - |r| { - r.assert_user_error("Proposal is not active"); - }, - ); + }); current_block_nonce += VOTING_DELAY_BLOCKS; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new() - .from(SECOND_USER_ADDRESS_EXPR) - .esdt_transfer(GOV_TOKEN_ID, 0, "999"), - |sc| { + world.current_block().block_nonce(current_block_nonce); + + world + .tx() + .from(SECOND_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(GOV_TOKEN_ID), + 0, + &BigUint::from(999u64), + ) + .whitebox(use_module::contract_obj, |sc| { sc.vote(proposal_id, VoteType::UpVote); - }, - ); + }); // try execute before queue - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new().from(FIRST_USER_ADDRESS_EXPR).no_expect(), - |sc| { + world + .tx() + .from(FIRST_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .returns(ExpectError(4u64, "Can only execute queued proposals")) + .whitebox(use_module::contract_obj, |sc| { sc.execute(proposal_id); - }, - |r| { - r.assert_user_error("Can only execute queued proposals"); - }, - ); + }); // try queue before voting ends - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new().from(FIRST_USER_ADDRESS_EXPR).no_expect(), - |sc| { + world + .tx() + .from(FIRST_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .returns(ExpectError(4u64, "Can only queue succeeded proposals")) + .whitebox(use_module::contract_obj, |sc| { sc.queue(proposal_id); - }, - |r| { - r.assert_user_error("Can only queue succeeded proposals"); - }, - ); + }); current_block_nonce += VOTING_PERIOD_BLOCKS; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); + world.current_block().block_nonce(current_block_nonce); // try queue not enough votes - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new().from(FIRST_USER_ADDRESS_EXPR).no_expect(), - |sc| { + world + .tx() + .from(FIRST_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .returns(ExpectError(4u64, "Can only queue succeeded proposals")) + .whitebox(use_module::contract_obj, |sc| { sc.queue(proposal_id); - }, - |r| { - r.assert_user_error("Can only queue succeeded proposals"); - }, - ); + }); // user 1 vote again current_block_nonce = 20; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new() - .from(FIRST_USER_ADDRESS_EXPR) - .esdt_transfer(GOV_TOKEN_ID, 0, "200"), - |sc| { + world.current_block().block_nonce(current_block_nonce); + + world + .tx() + .from(FIRST_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(GOV_TOKEN_ID), + 0, + &BigUint::from(200u64), + ) + .whitebox(use_module::contract_obj, |sc| { sc.vote(proposal_id, VoteType::UpVote); - }, - ); + }); // owner downvote - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new() - .from(OWNER_ADDRESS_EXPR) - .esdt_transfer(GOV_TOKEN_ID, 0, "200"), - |sc| { + world + .tx() + .from(OWNER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(GOV_TOKEN_ID), + 0, + &BigUint::from(200u64), + ) + .whitebox(use_module::contract_obj, |sc| { sc.vote(proposal_id, VoteType::DownVote); - }, - ); + }); // try queue too many downvotes current_block_nonce = 45; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new().from(FIRST_USER_ADDRESS_EXPR).no_expect(), - |sc| { + world.current_block().block_nonce(current_block_nonce); + + world + .tx() + .from(FIRST_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .returns(ExpectError(4u64, "Can only queue succeeded proposals")) + .whitebox(use_module::contract_obj, |sc| { sc.queue(proposal_id); - }, - |r| { - r.assert_user_error("Can only queue succeeded proposals"); - }, - ); + }); // user 1 vote again current_block_nonce = 20; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new() - .from(FIRST_USER_ADDRESS_EXPR) - .esdt_transfer(GOV_TOKEN_ID, 0, "200") - .no_expect(), - |sc| { + world.current_block().block_nonce(current_block_nonce); + + world + .tx() + .from(FIRST_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(GOV_TOKEN_ID), + 0, + &BigUint::from(200u64), + ) + .returns(ExpectError(4u64, "Already voted for this proposal")) + .whitebox(use_module::contract_obj, |sc| { sc.vote(proposal_id, VoteType::UpVote); - }, - |r| { - r.assert_user_error("Already voted for this proposal"); - }, - ); + }); // user 3 vote again - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new() - .from(THIRD_USER_ADDRESS_EXPR) - .esdt_transfer(GOV_TOKEN_ID, 0, "200"), - |sc| { + world + .tx() + .from(THIRD_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(GOV_TOKEN_ID), + 0, + &BigUint::from(200u64), + ) + .whitebox(use_module::contract_obj, |sc| { sc.vote(proposal_id, VoteType::UpVote); - }, - ); + }); // queue ok current_block_nonce = 45; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(FIRST_USER_ADDRESS_EXPR).no_expect(), - |sc| { + world.current_block().block_nonce(current_block_nonce); + + world + .tx() + .from(FIRST_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { sc.queue(proposal_id); - }, - ); + }); // try execute too early - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new().from(FIRST_USER_ADDRESS_EXPR).no_expect(), - |sc| { - sc.execute(proposal_id); - }, - |r| { - r.assert_user_error("Proposal is in timelock status. Try again later"); - }, - ); + world + .tx() + .from(FIRST_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .returns(ExpectError( + 4u64, + "Proposal is in timelock status. Try again later", + )) + .whitebox(use_module::contract_obj, |sc| sc.execute(proposal_id)); // execute ok current_block_nonce += LOCKING_PERIOD_BLOCKS; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(FIRST_USER_ADDRESS_EXPR).no_expect(), - |sc| { - sc.execute(proposal_id); - }, - ); + world.current_block().block_nonce(current_block_nonce); + + world + .tx() + .from(FIRST_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| sc.execute(proposal_id)); // after execution, quorum changed from 1_500 to the proposed 1_000 - world.whitebox_query(&use_module_whitebox, |sc| { - assert_eq!(sc.quorum().get(), managed_biguint!(1_000)); - assert!(sc.proposals().item_is_empty(1)); - }); - - world.check_state_step(CheckStateStep::new().put_account( - FIRST_USER_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(GOV_TOKEN_ID_EXPR, "300"), - )); - world.check_state_step(CheckStateStep::new().put_account( - SECOND_USER_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(GOV_TOKEN_ID_EXPR, "1"), - )); - world.check_state_step(CheckStateStep::new().put_account( - THIRD_USER_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(GOV_TOKEN_ID_EXPR, "800"), - )); - world.check_state_step(CheckStateStep::new().put_account( - OWNER_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(GOV_TOKEN_ID_EXPR, "800"), - )); + world + .query() + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { + assert_eq!(sc.quorum().get(), managed_biguint!(1_000)); + assert!(sc.proposals().item_is_empty(1)); + }); + + world + .check_account(FIRST_USER_ADDRESS) + .esdt_balance(GOV_TOKEN_ID_EXPR, BigUint::from(300u64)); + world + .check_account(SECOND_USER_ADDRESS) + .esdt_balance(GOV_TOKEN_ID_EXPR, BigUint::from(1u64)); + world + .check_account(THIRD_USER_ADDRESS) + .esdt_balance(GOV_TOKEN_ID_EXPR, BigUint::from(800u64)); + world + .check_account(OWNER_ADDRESS) + .esdt_balance(GOV_TOKEN_ID_EXPR, BigUint::from(800u64)); } #[test] fn test_down_veto_gov_config() { let mut world = setup(); - let use_module_whitebox = - WhiteboxContract::new(USE_MODULE_ADDRESS_EXPR, use_module::contract_obj); let mut current_block_nonce = 10; let proposal_id = propose( &mut world, - &address_expr_to_address(FIRST_USER_ADDRESS_EXPR), + &FIRST_USER_ADDRESS.to_address(), 500, - &address_expr_to_address(USE_MODULE_ADDRESS_EXPR), + &USE_MODULE_ADDRESS.to_address(), b"changeQuorum", vec![1_000u64.to_be_bytes().to_vec()], ); @@ -372,81 +360,87 @@ fn test_down_veto_gov_config() { assert_eq!(proposal_id, 1); current_block_nonce += VOTING_DELAY_BLOCKS; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new() - .from(FIRST_USER_ADDRESS_EXPR) - .esdt_transfer(GOV_TOKEN_ID, 0, "300"), - |sc| { + world.current_block().block_nonce(current_block_nonce); + + world + .tx() + .from(FIRST_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(GOV_TOKEN_ID), + 0, + &BigUint::from(300u64), + ) + .whitebox(use_module::contract_obj, |sc| { sc.vote(proposal_id, VoteType::UpVote); - }, - ); + }); current_block_nonce = 20; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new() - .from(SECOND_USER_ADDRESS_EXPR) - .esdt_transfer(GOV_TOKEN_ID, 0, "200"), - |sc| { + world.current_block().block_nonce(current_block_nonce); + + world + .tx() + .from(SECOND_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(GOV_TOKEN_ID), + 0, + &BigUint::from(200u64), + ) + .whitebox(use_module::contract_obj, |sc| { sc.vote(proposal_id, VoteType::UpVote); - }, - ); + }); - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new() - .from(THIRD_USER_ADDRESS_EXPR) - .esdt_transfer(GOV_TOKEN_ID, 0, "200"), - |sc| { - sc.vote(proposal_id, VoteType::DownVetoVote); - }, - ); + world + .tx() + .from(THIRD_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(GOV_TOKEN_ID), + 0, + &BigUint::from(200u64), + ) + .whitebox(use_module::contract_obj, |sc| { + sc.vote(proposal_id, VoteType::DownVetoVote) + }); // Vote didn't succeed; current_block_nonce = 45; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new().from(FIRST_USER_ADDRESS_EXPR).no_expect(), - |sc| { + world.current_block().block_epoch(current_block_nonce); + + world + .tx() + .from(FIRST_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .returns(ExpectError(4u64, "Can only queue succeeded proposals")) + .whitebox(use_module::contract_obj, |sc| { sc.queue(proposal_id); - }, - |r| { - r.assert_user_error("Can only queue succeeded proposals"); - }, - ); + }); + + world + .check_account(FIRST_USER_ADDRESS) + .esdt_balance(GOV_TOKEN_ID_EXPR, BigUint::from(200u64)); - world.check_state_step(CheckStateStep::new().put_account( - FIRST_USER_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(GOV_TOKEN_ID_EXPR, "200"), - )); - world.check_state_step(CheckStateStep::new().put_account( - SECOND_USER_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(GOV_TOKEN_ID_EXPR, "800"), - )); - world.check_state_step(CheckStateStep::new().put_account( - THIRD_USER_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(GOV_TOKEN_ID_EXPR, "800"), - )); + world + .check_account(SECOND_USER_ADDRESS) + .esdt_balance(GOV_TOKEN_ID_EXPR, BigUint::from(800u64)); + + world + .check_account(THIRD_USER_ADDRESS) + .esdt_balance(GOV_TOKEN_ID_EXPR, BigUint::from(800u64)); } #[test] fn test_abstain_vote_gov_config() { let mut world = setup(); - let use_module_whitebox = - WhiteboxContract::new(USE_MODULE_ADDRESS_EXPR, use_module::contract_obj); let mut current_block_nonce = 10; let proposal_id = propose( &mut world, - &address_expr_to_address(FIRST_USER_ADDRESS_EXPR), + &FIRST_USER_ADDRESS.to_address(), 500, - &address_expr_to_address(USE_MODULE_ADDRESS_EXPR), + &USE_MODULE_ADDRESS.to_address(), b"changeQuorum", vec![1_000u64.to_be_bytes().to_vec()], ); @@ -454,95 +448,105 @@ fn test_abstain_vote_gov_config() { assert_eq!(proposal_id, 1); current_block_nonce += VOTING_DELAY_BLOCKS; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new() - .from(FIRST_USER_ADDRESS_EXPR) - .esdt_transfer(GOV_TOKEN_ID, 0, "500"), - |sc| { + world.current_block().block_nonce(current_block_nonce); + + world + .tx() + .from(FIRST_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(GOV_TOKEN_ID), + 0, + &BigUint::from(500u64), + ) + .whitebox(use_module::contract_obj, |sc| { sc.vote(proposal_id, VoteType::UpVote); - }, - ); + }); current_block_nonce = 20; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new() - .from(SECOND_USER_ADDRESS_EXPR) - .esdt_transfer(GOV_TOKEN_ID, 0, "400"), - |sc| { + world.current_block().block_nonce(current_block_nonce); + + world + .tx() + .from(SECOND_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(GOV_TOKEN_ID), + 0, + &BigUint::from(400u64), + ) + .whitebox(use_module::contract_obj, |sc| { sc.vote(proposal_id, VoteType::DownVote); - }, - ); + }); - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new() - .from(THIRD_USER_ADDRESS_EXPR) - .esdt_transfer(GOV_TOKEN_ID, 0, "600"), - |sc| { + world + .tx() + .from(THIRD_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(GOV_TOKEN_ID), + 0, + &BigUint::from(600u64), + ) + .whitebox(use_module::contract_obj, |sc| { sc.vote(proposal_id, VoteType::AbstainVote); - }, - ); + }); // Vote didn't succeed; current_block_nonce = 45; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(FIRST_USER_ADDRESS_EXPR).no_expect(), - |sc| { + world.current_block().block_nonce(current_block_nonce); + + world + .tx() + .from(FIRST_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { sc.queue(proposal_id); - }, - ); + }); // execute ok current_block_nonce += LOCKING_PERIOD_BLOCKS; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(FIRST_USER_ADDRESS_EXPR).no_expect(), - |sc| { + world.current_block().block_nonce(current_block_nonce); + + world + .tx() + .from(FIRST_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { sc.execute(proposal_id); - }, - ); + }); // after execution, quorum changed from 1_500 to the proposed 1_000 - world.whitebox_query(&use_module_whitebox, |sc| { - assert_eq!(sc.quorum().get(), managed_biguint!(1_000)); - assert!(sc.proposals().item_is_empty(1)); - }); - - world.check_state_step(CheckStateStep::new().put_account( - FIRST_USER_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(GOV_TOKEN_ID_EXPR, "0"), - )); - world.check_state_step(CheckStateStep::new().put_account( - SECOND_USER_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(GOV_TOKEN_ID_EXPR, "600"), - )); - world.check_state_step(CheckStateStep::new().put_account( - THIRD_USER_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(GOV_TOKEN_ID_EXPR, "400"), - )); + world + .query() + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { + assert_eq!(sc.quorum().get(), managed_biguint!(1_000)); + assert!(sc.proposals().item_is_empty(1)); + }); + + world + .check_account(FIRST_USER_ADDRESS) + .esdt_balance(GOV_TOKEN_ID_EXPR, BigUint::zero()); + world + .check_account(SECOND_USER_ADDRESS) + .esdt_balance(GOV_TOKEN_ID_EXPR, BigUint::from(600u64)); + world + .check_account(THIRD_USER_ADDRESS) + .esdt_balance(GOV_TOKEN_ID_EXPR, BigUint::from(400u64)); } #[test] fn test_gov_cancel_defeated_proposal() { let mut world = setup(); - let use_module_whitebox = - WhiteboxContract::new(USE_MODULE_ADDRESS_EXPR, use_module::contract_obj); let mut current_block_nonce = 10; let proposal_id = propose( &mut world, - &address_expr_to_address(FIRST_USER_ADDRESS_EXPR), + &FIRST_USER_ADDRESS.to_address(), 500, - &address_expr_to_address(USE_MODULE_ADDRESS_EXPR), + &USE_MODULE_ADDRESS.to_address(), b"changeQuorum", vec![1_000u64.to_be_bytes().to_vec()], ); @@ -550,42 +554,37 @@ fn test_gov_cancel_defeated_proposal() { assert_eq!(proposal_id, 1); current_block_nonce += VOTING_DELAY_BLOCKS; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new() - .from(SECOND_USER_ADDRESS_EXPR) - .esdt_transfer(GOV_TOKEN_ID, 0, "999"), - |sc| { - sc.vote(proposal_id, VoteType::DownVote); - }, - ); + world.current_block().block_nonce(current_block_nonce); + + world + .tx() + .from(SECOND_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(GOV_TOKEN_ID), + 0, + &BigUint::from(999u64), + ) + .whitebox(use_module::contract_obj, |sc| { + sc.vote(proposal_id, VoteType::DownVote) + }); // try cancel too early - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new().from(SECOND_USER_ADDRESS_EXPR).no_expect(), - |sc| { + world + .tx() + .from(SECOND_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .returns(ExpectError(4u64, "Action may not be cancelled")) + .whitebox(use_module::contract_obj, |sc| { sc.cancel(proposal_id); - }, - |r| { - r.assert_user_error("Action may not be cancelled"); - }, - ); + }); current_block_nonce += VOTING_PERIOD_BLOCKS; - world.set_state_step(SetStateStep::new().block_nonce(current_block_nonce)); - - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(SECOND_USER_ADDRESS_EXPR).no_expect(), - |sc| { - sc.cancel(proposal_id); - }, - ); -} + world.current_block().block_nonce(current_block_nonce); -fn address_expr_to_address(address_expr: &str) -> Address { - AddressValue::from(address_expr).to_address() + world + .tx() + .from(SECOND_USER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| sc.cancel(proposal_id)); } diff --git a/contracts/feature-tests/use-module/tests/staking_module_whitebox_test.rs b/contracts/feature-tests/use-module/tests/staking_module_whitebox_test.rs index 612fa725df..0f705ad131 100644 --- a/contracts/feature-tests/use-module/tests/staking_module_whitebox_test.rs +++ b/contracts/feature-tests/use-module/tests/staking_module_whitebox_test.rs @@ -1,23 +1,23 @@ use multiversx_sc_modules::staking::StakingModule; use multiversx_sc_scenario::imports::*; -const STAKING_TOKEN_ID_EXPR: &str = "str:STAKE-123456"; +const STAKING_TOKEN_ID_EXPR: TestTokenIdentifier = TestTokenIdentifier::new("STAKE-123456"); const STAKING_TOKEN_ID: &[u8] = b"STAKE-123456"; const INITIAL_BALANCE: u64 = 2_000_000; const REQUIRED_STAKE_AMOUNT: u64 = 1_000_000; const SLASH_AMOUNT: u64 = 600_000; const QUORUM: usize = 3; -const OWNER_ADDRESS_EXPR: &str = "address:owner"; -const ALICE_ADDRESS_EXPR: &str = "address:alice"; -const BOB_ADDRESS_EXPR: &str = "address:bob"; -const CAROL_ADDRESS_EXPR: &str = "address:carol"; -const EVE_ADDRESS_EXPR: &str = "address:eve"; -const PAUL_ADDRESS_EXPR: &str = "address:paul"; -const SALLY_ADDRESS_EXPR: &str = "address:sally"; +const OWNER_ADDRESS: TestAddress = TestAddress::new("owner"); +const ALICE_ADDRESS: TestAddress = TestAddress::new("alice"); +const BOB_ADDRESS: TestAddress = TestAddress::new("bob"); +const CAROL_ADDRESS: TestAddress = TestAddress::new("carol"); +const EVE_ADDRESS: TestAddress = TestAddress::new("eve"); +const PAUL_ADDRESS: TestAddress = TestAddress::new("paul"); +const SALLY_ADDRESS: TestAddress = TestAddress::new("sally"); -const USE_MODULE_ADDRESS_EXPR: &str = "sc:use-module"; -const USE_MODULE_PATH_EXPR: &str = "mxsc:output/use-module.mxsc.json"; +const USE_MODULE_ADDRESS: TestSCAddress = TestSCAddress::new("use-module"); +const USE_MODULE_PATH_EXPR: MxscPath = MxscPath::new("mxsc:output/use-module.mxsc.json"); fn world() -> ScenarioWorld { let mut blockchain = ScenarioWorld::new(); @@ -30,447 +30,364 @@ fn world() -> ScenarioWorld { fn test_staking_module() { let mut world = world(); - world.set_state_step( - SetStateStep::new() - .put_account(OWNER_ADDRESS_EXPR, Account::new().nonce(1)) - .new_address(OWNER_ADDRESS_EXPR, 1, USE_MODULE_ADDRESS_EXPR) - .put_account( - ALICE_ADDRESS_EXPR, - Account::new() - .nonce(1) - .esdt_balance(STAKING_TOKEN_ID_EXPR, INITIAL_BALANCE), - ) - .put_account( - BOB_ADDRESS_EXPR, - Account::new() - .nonce(1) - .esdt_balance(STAKING_TOKEN_ID_EXPR, INITIAL_BALANCE), - ) - .put_account( - CAROL_ADDRESS_EXPR, - Account::new() - .nonce(1) - .esdt_balance(STAKING_TOKEN_ID_EXPR, INITIAL_BALANCE), - ) - .put_account( - EVE_ADDRESS_EXPR, - Account::new() - .nonce(1) - .esdt_balance(STAKING_TOKEN_ID_EXPR, INITIAL_BALANCE), - ) - .put_account( - PAUL_ADDRESS_EXPR, - Account::new() - .nonce(1) - .esdt_balance(STAKING_TOKEN_ID_EXPR, INITIAL_BALANCE), - ) - .put_account( - SALLY_ADDRESS_EXPR, - Account::new() - .nonce(1) - .esdt_balance(STAKING_TOKEN_ID_EXPR, INITIAL_BALANCE), - ), - ); + world.account(OWNER_ADDRESS).nonce(1); + world + .account(ALICE_ADDRESS) + .nonce(1) + .esdt_balance(STAKING_TOKEN_ID_EXPR, INITIAL_BALANCE); + world + .account(BOB_ADDRESS) + .nonce(1) + .esdt_balance(STAKING_TOKEN_ID_EXPR, INITIAL_BALANCE); + world + .account(CAROL_ADDRESS) + .nonce(1) + .esdt_balance(STAKING_TOKEN_ID_EXPR, INITIAL_BALANCE); + world + .account(EVE_ADDRESS) + .nonce(1) + .esdt_balance(STAKING_TOKEN_ID_EXPR, INITIAL_BALANCE); + world + .account(PAUL_ADDRESS) + .nonce(1) + .esdt_balance(STAKING_TOKEN_ID_EXPR, INITIAL_BALANCE); + world + .account(SALLY_ADDRESS) + .nonce(1) + .esdt_balance(STAKING_TOKEN_ID_EXPR, INITIAL_BALANCE); // init - let use_module_whitebox = - WhiteboxContract::new(USE_MODULE_ADDRESS_EXPR, use_module::contract_obj); - let use_module_code = world.code_expression(USE_MODULE_PATH_EXPR); - - world.whitebox_deploy( - &use_module_whitebox, - ScDeployStep::new() - .from(OWNER_ADDRESS_EXPR) - .code(use_module_code), - |sc| { + let new_address = world + .tx() + .from(OWNER_ADDRESS) + .raw_deploy() + .code(USE_MODULE_PATH_EXPR) + .new_address(USE_MODULE_ADDRESS) + .returns(ReturnsNewBech32Address) + .whitebox(use_module::contract_obj, |sc| { let mut whitelist = ManagedVec::new(); - whitelist.push(managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))); - whitelist.push(managed_address!(&address_expr_to_address(BOB_ADDRESS_EXPR))); - whitelist.push(managed_address!(&address_expr_to_address( - CAROL_ADDRESS_EXPR - ))); - whitelist.push(managed_address!(&address_expr_to_address( - PAUL_ADDRESS_EXPR - ))); - whitelist.push(managed_address!(&address_expr_to_address( - SALLY_ADDRESS_EXPR - ))); + whitelist.push(ALICE_ADDRESS.to_managed_address()); + whitelist.push(BOB_ADDRESS.to_managed_address()); + whitelist.push(CAROL_ADDRESS.to_managed_address()); + whitelist.push(PAUL_ADDRESS.to_managed_address()); + whitelist.push(SALLY_ADDRESS.to_managed_address()); sc.init_staking_module( - &EgldOrEsdtTokenIdentifier::esdt(managed_token_id!(STAKING_TOKEN_ID)), - &managed_biguint!(REQUIRED_STAKE_AMOUNT), - &managed_biguint!(SLASH_AMOUNT), + &EgldOrEsdtTokenIdentifier::esdt(STAKING_TOKEN_ID), + &BigUint::from(REQUIRED_STAKE_AMOUNT), + &BigUint::from(SLASH_AMOUNT), QUORUM, &whitelist, ); - }, - ); + }); + + assert_eq!(new_address.to_address(), USE_MODULE_ADDRESS.to_address()); // try stake - not a board member - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new() - .from(EVE_ADDRESS_EXPR) - .esdt_transfer(STAKING_TOKEN_ID, 0, REQUIRED_STAKE_AMOUNT) - .no_expect(), - |sc| sc.stake(), - |r| { - r.assert_user_error("Only whitelisted members can stake"); - }, - ); + world + .tx() + .from(EVE_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(STAKING_TOKEN_ID_EXPR), + 0, + &BigUint::from(REQUIRED_STAKE_AMOUNT), + ) + .returns(ExpectError(4u64, "Only whitelisted members can stake")) + .whitebox(use_module::contract_obj, |sc| { + sc.stake(); + }); // stake half and try unstake - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(ALICE_ADDRESS_EXPR).esdt_transfer( - STAKING_TOKEN_ID, + world + .tx() + .from(ALICE_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(STAKING_TOKEN_ID_EXPR), 0, - REQUIRED_STAKE_AMOUNT / 2, - ), - |sc| sc.stake(), - ); + &BigUint::from(REQUIRED_STAKE_AMOUNT / 2), + ) + .whitebox(use_module::contract_obj, |sc| { + sc.stake(); + }); - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new().from(ALICE_ADDRESS_EXPR).no_expect(), - |sc| sc.unstake(managed_biguint!(REQUIRED_STAKE_AMOUNT / 4)), - |r| { - r.assert_user_error("Not enough stake"); - }, - ); + world + .tx() + .from(ALICE_ADDRESS) + .to(USE_MODULE_ADDRESS) + .returns(ExpectError(4u64, "Not enough stake")) + .whitebox(use_module::contract_obj, |sc| { + sc.unstake(BigUint::from(REQUIRED_STAKE_AMOUNT / 4)); + }); // bob and carol stake - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(BOB_ADDRESS_EXPR).esdt_transfer( - STAKING_TOKEN_ID, + world + .tx() + .from(BOB_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(STAKING_TOKEN_ID), 0, - REQUIRED_STAKE_AMOUNT, - ), - |sc| sc.stake(), - ); - - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(CAROL_ADDRESS_EXPR).esdt_transfer( - STAKING_TOKEN_ID, + &BigUint::from(REQUIRED_STAKE_AMOUNT), + ) + .whitebox(use_module::contract_obj, |sc| { + sc.stake(); + }); + + world + .tx() + .from(CAROL_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(STAKING_TOKEN_ID), 0, - REQUIRED_STAKE_AMOUNT, - ), - |sc| sc.stake(), - ); - - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(PAUL_ADDRESS_EXPR).esdt_transfer( - STAKING_TOKEN_ID, + &BigUint::from(REQUIRED_STAKE_AMOUNT), + ) + .whitebox(use_module::contract_obj, |sc| { + sc.stake(); + }); + + world + .tx() + .from(PAUL_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(STAKING_TOKEN_ID), 0, - REQUIRED_STAKE_AMOUNT, - ), - |sc| sc.stake(), - ); - - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(SALLY_ADDRESS_EXPR).esdt_transfer( - STAKING_TOKEN_ID, + &BigUint::from(REQUIRED_STAKE_AMOUNT), + ) + .whitebox(use_module::contract_obj, |sc| { + sc.stake(); + }); + + world + .tx() + .from(SALLY_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(STAKING_TOKEN_ID), 0, - REQUIRED_STAKE_AMOUNT, - ), - |sc| sc.stake(), - ); + &BigUint::from(REQUIRED_STAKE_AMOUNT), + ) + .whitebox(use_module::contract_obj, |sc| { + sc.stake(); + }); // try vote slash, not enough stake - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new().from(ALICE_ADDRESS_EXPR).no_expect(), - |sc| sc.vote_slash_member(managed_address!(&address_expr_to_address(BOB_ADDRESS_EXPR))), - |r| { - r.assert_user_error("Not enough stake"); - }, - ); + world + .tx() + .from(ALICE_ADDRESS) + .to(USE_MODULE_ADDRESS) + .returns(ExpectError(4u64, "Not enough stake")) + .whitebox(use_module::contract_obj, |sc| { + sc.vote_slash_member(BOB_ADDRESS.to_managed_address()); + }); // try vote slash, slashed address not a board member - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new().from(ALICE_ADDRESS_EXPR).no_expect(), - |sc| sc.vote_slash_member(managed_address!(&address_expr_to_address(EVE_ADDRESS_EXPR))), - |r| { - r.assert_user_error("Voted user is not a staked board member"); - }, - ); + world + .tx() + .from(ALICE_ADDRESS) + .to(USE_MODULE_ADDRESS) + .returns(ExpectError(4u64, "Voted user is not a staked board member")) + .whitebox(use_module::contract_obj, |sc| { + sc.vote_slash_member(EVE_ADDRESS.to_managed_address()); + }); // alice stake over max amount and withdraw surplus - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(ALICE_ADDRESS_EXPR).esdt_transfer( - STAKING_TOKEN_ID, + world + .tx() + .from(ALICE_ADDRESS) + .to(USE_MODULE_ADDRESS) + .single_esdt( + &TokenIdentifier::from(STAKING_TOKEN_ID_EXPR), 0, - REQUIRED_STAKE_AMOUNT, - ), - |sc| { + &BigUint::from(REQUIRED_STAKE_AMOUNT), + ) + .whitebox(use_module::contract_obj, |sc| { sc.stake(); - let alice_staked_amount = sc - .staked_amount(&managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))) - .get(); - assert_eq!(alice_staked_amount, managed_biguint!(1_500_000)); - }, - ); - - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(ALICE_ADDRESS_EXPR), - |sc| { - sc.unstake(managed_biguint!(500_000)); - - let alice_staked_amount = sc - .staked_amount(&managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))) - .get(); - assert_eq!(alice_staked_amount, managed_biguint!(1_000_000)); - }, - ); - - world.check_state_step(CheckStateStep::new().put_account( - ALICE_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(STAKING_TOKEN_ID_EXPR, "1_000_000"), - )); + let alice_staked_amount = sc.staked_amount(&ALICE_ADDRESS.to_managed_address()).get(); + assert_eq!(alice_staked_amount, BigUint::from(1_500_000u64)); + }); + + world + .tx() + .from(ALICE_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { + sc.unstake(BigUint::from(500_000u64)); + let alice_staked_amount = sc.staked_amount(&ALICE_ADDRESS.to_managed_address()).get(); + assert_eq!(alice_staked_amount, BigUint::from(1_000_000u64)); + }); + + world + .check_account(ALICE_ADDRESS) + .esdt_balance(STAKING_TOKEN_ID_EXPR, BigUint::from(1_000_000u64)); // alice vote to slash bob - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(ALICE_ADDRESS_EXPR), - |sc| { - sc.vote_slash_member(managed_address!(&address_expr_to_address(BOB_ADDRESS_EXPR))); + world + .tx() + .from(ALICE_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { + sc.vote_slash_member(BOB_ADDRESS.to_managed_address()); assert_eq!( - sc.slashing_proposal_voters(&managed_address!(&address_expr_to_address( - BOB_ADDRESS_EXPR - ))) - .len(), + sc.slashing_proposal_voters(&BOB_ADDRESS.to_managed_address()) + .len(), 1 ); assert!(sc - .slashing_proposal_voters(&managed_address!(&address_expr_to_address( - BOB_ADDRESS_EXPR - ))) - .contains(&managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - )))); - }, - ); + .slashing_proposal_voters(&BOB_ADDRESS.to_managed_address()) + .contains(&ALICE_ADDRESS.to_managed_address())); + }); // bob vote to slash alice - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(BOB_ADDRESS_EXPR), - |sc| { - sc.vote_slash_member(managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))); - }, - ); + world + .tx() + .from(BOB_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { + sc.vote_slash_member(ALICE_ADDRESS.to_managed_address()); + }); // try slash before quorum reached - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new().from(BOB_ADDRESS_EXPR).no_expect(), - |sc| { - sc.slash_member(managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))); - }, - |r| { - r.assert_user_error("Quorum not reached"); - }, - ); + world + .tx() + .from(BOB_ADDRESS) + .to(USE_MODULE_ADDRESS) + .returns(ExpectError(4u64, "Quorum not reached")) + .whitebox(use_module::contract_obj, |sc| { + sc.slash_member(ALICE_ADDRESS.to_managed_address()); + }); // paul vote to slash alice - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(PAUL_ADDRESS_EXPR), - |sc| { - sc.vote_slash_member(managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))); - }, - ); + world + .tx() + .from(PAUL_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { + sc.vote_slash_member(ALICE_ADDRESS.to_managed_address()); + }); // sally vote to slash alice - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(SALLY_ADDRESS_EXPR), - |sc| { - sc.vote_slash_member(managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))); - }, - ); + world + .tx() + .from(SALLY_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { + sc.vote_slash_member(ALICE_ADDRESS.to_managed_address()); + }); // sally cancels vote to slash alice - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(SALLY_ADDRESS_EXPR), - |sc| { - sc.cancel_vote_slash_member(managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))); - }, - ); + world + .tx() + .from(SALLY_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { + sc.cancel_vote_slash_member(ALICE_ADDRESS.to_managed_address()); + }); // carol vote - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(CAROL_ADDRESS_EXPR), - |sc| { - sc.vote_slash_member(managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))); + world + .tx() + .from(CAROL_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { + sc.vote_slash_member(ALICE_ADDRESS.to_managed_address()); assert_eq!( - sc.slashing_proposal_voters(&managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))) - .len(), + sc.slashing_proposal_voters(&ALICE_ADDRESS.to_managed_address()) + .len(), 3 ); assert!(sc - .slashing_proposal_voters(&managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))) - .contains(&managed_address!(&address_expr_to_address( - BOB_ADDRESS_EXPR - )))); + .slashing_proposal_voters(&ALICE_ADDRESS.to_managed_address()) + .contains(&BOB_ADDRESS.to_managed_address())); assert!(sc - .slashing_proposal_voters(&managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))) - .contains(&managed_address!(&address_expr_to_address( - CAROL_ADDRESS_EXPR - )))); + .slashing_proposal_voters(&ALICE_ADDRESS.to_managed_address()) + .contains(&CAROL_ADDRESS.to_managed_address())); assert!(sc - .slashing_proposal_voters(&managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))) - .contains(&managed_address!(&address_expr_to_address( - PAUL_ADDRESS_EXPR - )))); + .slashing_proposal_voters(&ALICE_ADDRESS.to_managed_address()) + .contains(&PAUL_ADDRESS.to_managed_address())); assert!(!sc - .slashing_proposal_voters(&managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))) - .contains(&managed_address!(&address_expr_to_address( - SALLY_ADDRESS_EXPR - )))); - }, - ); + .slashing_proposal_voters(&ALICE_ADDRESS.to_managed_address()) + .contains(&SALLY_ADDRESS.to_managed_address())); + }); // slash alice - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(BOB_ADDRESS_EXPR), - |sc| { - sc.slash_member(managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))); + world + .tx() + .from(BOB_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { + sc.slash_member(ALICE_ADDRESS.to_managed_address()); assert_eq!( - sc.staked_amount(&managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))) - .get(), - managed_biguint!(REQUIRED_STAKE_AMOUNT - SLASH_AMOUNT) - ); - assert_eq!( - sc.total_slashed_amount().get(), - managed_biguint!(SLASH_AMOUNT) + sc.staked_amount(&ALICE_ADDRESS.to_managed_address()).get(), + BigUint::from(REQUIRED_STAKE_AMOUNT - SLASH_AMOUNT) ); + assert_eq!(sc.total_slashed_amount().get(), BigUint::from(SLASH_AMOUNT)); assert!(sc - .slashing_proposal_voters(&managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))) + .slashing_proposal_voters(&ALICE_ADDRESS.to_managed_address()) .is_empty()); - }, - ); + }); // alice try vote after slash - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new().from(ALICE_ADDRESS_EXPR).no_expect(), - |sc| { - sc.vote_slash_member(managed_address!(&address_expr_to_address(BOB_ADDRESS_EXPR))); - }, - |r| { - r.assert_user_error("Not enough stake"); - }, - ); + world + .tx() + .from(ALICE_ADDRESS) + .to(USE_MODULE_ADDRESS) + .returns(ExpectError(4u64, "Not enough stake")) + .whitebox(use_module::contract_obj, |sc| { + sc.vote_slash_member(BOB_ADDRESS.to_managed_address()); + }); // alice try unstake the remaining tokens - world.whitebox_call_check( - &use_module_whitebox, - ScCallStep::new().from(ALICE_ADDRESS_EXPR).no_expect(), - |sc| { - sc.unstake(managed_biguint!(400_000)); - }, - |r| { - r.assert_user_error("Not enough stake"); - }, - ); + world + .tx() + .from(ALICE_ADDRESS) + .to(USE_MODULE_ADDRESS) + .returns(ExpectError(4u64, "Not enough stake")) + .whitebox(use_module::contract_obj, |sc| { + sc.unstake(BigUint::from(400_000u64)); + }); // alice remove from board members - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(OWNER_ADDRESS_EXPR), - |sc| { + world + .tx() + .from(OWNER_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { // check alice's votes before slash assert!(sc - .slashing_proposal_voters(&managed_address!(&address_expr_to_address( - BOB_ADDRESS_EXPR - ))) - .contains(&managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - )))); + .slashing_proposal_voters(&BOB_ADDRESS.to_managed_address()) + .contains(&ALICE_ADDRESS.to_managed_address())); - sc.remove_board_member(&managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - ))); + sc.remove_board_member(&ALICE_ADDRESS.to_managed_address()); assert_eq!(sc.user_whitelist().len(), 4); assert!(!sc .user_whitelist() - .contains(&managed_address!(&address_expr_to_address( - ALICE_ADDRESS_EXPR - )))); + .contains(&ALICE_ADDRESS.to_managed_address())); // alice's vote gets removed assert!(sc - .slashing_proposal_voters(&managed_address!(&address_expr_to_address( - BOB_ADDRESS_EXPR - ))) + .slashing_proposal_voters(&BOB_ADDRESS.to_managed_address()) .is_empty()); - }, - ); + }); // alice unstake ok - world.whitebox_call( - &use_module_whitebox, - ScCallStep::new().from(ALICE_ADDRESS_EXPR), - |sc| { - sc.unstake(managed_biguint!(400_000)); - }, + world + .tx() + .from(ALICE_ADDRESS) + .to(USE_MODULE_ADDRESS) + .whitebox(use_module::contract_obj, |sc| { + sc.unstake(BigUint::from(400_000u64)); + }); + + world.check_account(ALICE_ADDRESS).esdt_balance( + TokenIdentifier::from(STAKING_TOKEN_ID_EXPR), + BigUint::from(INITIAL_BALANCE - SLASH_AMOUNT), ); - - world.check_state_step(CheckStateStep::new().put_account( - ALICE_ADDRESS_EXPR, - CheckAccount::new().esdt_balance(STAKING_TOKEN_ID_EXPR, INITIAL_BALANCE - SLASH_AMOUNT), - )); -} - -fn address_expr_to_address(address_expr: &str) -> Address { - AddressValue::from(address_expr).to_address() } diff --git a/framework/scenario/src/facade/world_tx.rs b/framework/scenario/src/facade/world_tx.rs index a2d7373a42..d052614297 100644 --- a/framework/scenario/src/facade/world_tx.rs +++ b/framework/scenario/src/facade/world_tx.rs @@ -5,7 +5,9 @@ mod scenario_query_call; mod scenario_rh_impl; mod scenario_set_state; mod scenario_tx_env; +mod scenario_tx_whitebox; pub use scenario_exec_call::ScenarioEnvExec; pub use scenario_query_call::ScenarioEnvQuery; pub use scenario_tx_env::{ScenarioTxEnv, ScenarioTxEnvData, ScenarioTxRun}; +pub use scenario_tx_whitebox::ScenarioTxWhitebox; diff --git a/framework/scenario/src/facade/world_tx/scenario_tx_whitebox.rs b/framework/scenario/src/facade/world_tx/scenario_tx_whitebox.rs new file mode 100644 index 0000000000..62842a6e25 --- /dev/null +++ b/framework/scenario/src/facade/world_tx/scenario_tx_whitebox.rs @@ -0,0 +1,217 @@ +use crate::debug_executor::contract_instance_wrapped_execution; +use crate::scenario::tx_to_step::TxToQueryStep; +use crate::{ + imports::StaticApi, scenario::tx_to_step::TxToStep, scenario_model::TxResponse, ScenarioEnvExec, +}; +use crate::{DebugApi, ScenarioEnvQuery}; +use multiversx_chain_vm::tx_mock::TxFunctionName; +use multiversx_sc::contract_base::ContractBase; +use multiversx_sc::{ + tuple_util::NestedTupleFlatten, + types::{ + Code, DeployCall, FunctionCall, RHListExec, Tx, TxCodeValue, TxFromSpecified, TxNoPayment, + TxPayment, TxToSpecified, + }, +}; + +pub trait ScenarioTxWhitebox { + type Returns; + + /// Runs a lambda function in the name of a smart contract, with the configured transaction context. + fn whitebox(self, contract_obj: fn() -> ContractObj, f: F) -> Self::Returns + where + ContractObj: ContractBase + 'static, + F: FnOnce(ContractObj); +} + +impl<'w, From, Payment, CodeValue, RH> ScenarioTxWhitebox + for Tx< + ScenarioEnvExec<'w>, + From, + (), + Payment, + (), + DeployCall, Code>, + RH, + > +where + From: TxFromSpecified>, + Payment: TxNoPayment>, + CodeValue: TxCodeValue>, + RH: RHListExec>, + RH::ListReturns: NestedTupleFlatten, +{ + type Returns = ::Unpacked; + + fn whitebox( + self, + contract_obj_builder: fn() -> ContractObj, + f: F, + ) -> Self::Returns + where + ContractObj: ContractBase + 'static, + F: FnOnce(ContractObj), + { + let contract_obj = contract_obj_builder(); + + let mut step_wrapper = self.tx_to_step(); + let (new_address, tx_result) = step_wrapper + .env + .world + .get_mut_debugger_backend() + .vm_runner + .perform_sc_deploy_lambda(&step_wrapper.step, || { + contract_instance_wrapped_execution(true, || { + f(contract_obj); + Ok(()) + }); + }); + + let mut response = TxResponse::from_tx_result(tx_result); + response.new_deployed_address = Some(new_address); + step_wrapper.step.save_response(response); + step_wrapper.process_result() + } +} + +impl<'w, From, To, Payment, RH> ScenarioTxWhitebox + for Tx, From, To, Payment, (), (), RH> +where + From: TxFromSpecified>, + To: TxToSpecified>, + Payment: TxPayment>, + RH: RHListExec>, + RH::ListReturns: NestedTupleFlatten, +{ + type Returns = ::Unpacked; + + fn whitebox( + self, + contract_obj_builder: fn() -> ContractObj, + f: F, + ) -> Self::Returns + where + ContractObj: ContractBase + 'static, + F: FnOnce(ContractObj), + { + self.raw_call(TxFunctionName::WHITEBOX_CALL.as_str()) + .whitebox(contract_obj_builder, f) + } +} + +impl<'w, From, To, Payment, RH> ScenarioTxWhitebox + for Tx, From, To, Payment, (), FunctionCall, RH> +where + From: TxFromSpecified>, + To: TxToSpecified>, + Payment: TxPayment>, + RH: RHListExec>, + RH::ListReturns: NestedTupleFlatten, +{ + type Returns = ::Unpacked; + + fn whitebox( + self, + contract_obj_builder: fn() -> ContractObj, + f: F, + ) -> Self::Returns + where + ContractObj: ContractBase + 'static, + F: FnOnce(ContractObj), + { + let contract_obj = contract_obj_builder(); + + let mut step_wrapper = self.tx_to_step(); + + // no endpoint is called per se, but if it is empty, the VM thinks it is a simple transfer of value + if step_wrapper.step.tx.function.is_empty() { + step_wrapper.step.tx.function = TxFunctionName::WHITEBOX_CALL.to_string(); + } + + let tx_result = step_wrapper + .env + .world + .get_mut_debugger_backend() + .vm_runner + .perform_sc_call_lambda(&step_wrapper.step, || { + contract_instance_wrapped_execution(true, || { + f(contract_obj); + Ok(()) + }); + }); + + let response = TxResponse::from_tx_result(tx_result); + step_wrapper.step.save_response(response); + step_wrapper.process_result() + } +} + +impl<'w, To, Payment, RH> ScenarioTxWhitebox + for Tx, (), To, Payment, (), (), RH> +where + To: TxToSpecified>, + Payment: TxNoPayment>, + RH: RHListExec>, + RH::ListReturns: NestedTupleFlatten, +{ + type Returns = ::Unpacked; + + fn whitebox( + self, + contract_obj_builder: fn() -> ContractObj, + f: F, + ) -> Self::Returns + where + ContractObj: ContractBase + 'static, + F: FnOnce(ContractObj), + { + self.raw_call(TxFunctionName::WHITEBOX_CALL.as_str()) + .whitebox(contract_obj_builder, f) + } +} + +impl<'w, To, Payment, RH> ScenarioTxWhitebox + for Tx, (), To, Payment, (), FunctionCall, RH> +where + To: TxToSpecified>, + Payment: TxNoPayment>, + RH: RHListExec>, + RH::ListReturns: NestedTupleFlatten, +{ + type Returns = ::Unpacked; + + fn whitebox( + self, + contract_obj_builder: fn() -> ContractObj, + f: F, + ) -> Self::Returns + where + ContractObj: ContractBase + 'static, + F: FnOnce(ContractObj), + { + let contract_obj = contract_obj_builder(); + + let mut step_wrapper = self.tx_to_query_step(); + + // no endpoint is called per se, but if it is empty, the VM thinks it is a simple transfer of value + if step_wrapper.step.tx.function.is_empty() { + step_wrapper.step.tx.function = TxFunctionName::WHITEBOX_CALL.to_string(); + } + + let tx_result = step_wrapper + .env + .world + .get_mut_debugger_backend() + .vm_runner + .perform_sc_query_lambda(&step_wrapper.step, || { + contract_instance_wrapped_execution(true, || { + f(contract_obj); + Ok(()) + }); + }); + + let response = TxResponse::from_tx_result(tx_result); + step_wrapper.step.save_response(response); + step_wrapper.process_result() + } +}