From 61ce458c8adf238f2356be21d5648119edb320b3 Mon Sep 17 00:00:00 2001 From: Alhibb <63309522+Alhibb@users.noreply.github.com> Date: Tue, 2 Dec 2025 20:59:12 +0100 Subject: [PATCH 1/5] test refactored --- .../public/assets-precompile/lib.rs | 356 +----------------- .../public/assets-precompile/tests.rs | 354 +++++++++++++++++ 2 files changed, 355 insertions(+), 355 deletions(-) create mode 100644 integration-tests/public/assets-precompile/tests.rs diff --git a/integration-tests/public/assets-precompile/lib.rs b/integration-tests/public/assets-precompile/lib.rs index 86d455b382..f543a3a148 100644 --- a/integration-tests/public/assets-precompile/lib.rs +++ b/integration-tests/public/assets-precompile/lib.rs @@ -150,358 +150,4 @@ mod asset_hub_precompile { } #[cfg(test)] -mod tests { - use super::*; - - #[test] - fn contract_stores_asset_id() { - use asset_hub_precompile::AssetHubPrecompile; - - let contract = AssetHubPrecompile::new(1337); - - assert_eq!(contract.asset_id(), 1337); - } - - #[test] - fn contract_stores_owner() { - use asset_hub_precompile::AssetHubPrecompile; - - let contract = AssetHubPrecompile::new(1337); - - assert_eq!(contract.asset_id(), 1337); - // Note: In unit tests, the caller is always the zero address - assert_eq!(contract.owner(), H160::from([0u8; 20])); - } -} - -#[cfg(all(test, feature = "e2e-tests"))] -mod e2e_tests { - use super::*; - use crate::asset_hub_precompile::{ - Approval, - AssetHubPrecompile, - AssetHubPrecompileRef, - Transfer, - }; - use ink_e2e::{ - ContractsBackend, - IntoAddress, - alice, - bob, - }; - use ink_runtime::{ - E2EError, - api::prelude::{ - AssetsAPI, - ContractAPI, - }, - assert_last_event, - assert_noop, - assert_ok, - }; - - type E2EResult = std::result::Result; - - #[ink_e2e::test(runtime)] - async fn deployment_works(mut client: Client) -> E2EResult<()> { - let asset_id: u32 = 1; - let mut constructor = AssetHubPrecompileRef::new(asset_id); - - let contract = client - .instantiate("assets_precompile", &alice(), &mut constructor) - .value(1_000_000_000_000u128) // Transfer native tokens to contract - .submit() - .await?; - - let contract_call = contract.call_builder::(); - let result = client - .call(&alice(), &contract_call.asset_id()) - .dry_run() - .await?; - - assert_eq!(result.return_value(), asset_id); - - Ok(()) - } - - #[ink_e2e::test(runtime)] - async fn total_supply_works(mut client: Client) -> E2EResult<()> { - let asset_id: u32 = 1; - let admin = alice(); - - client.runtime().create(&asset_id, &admin, 1u128)?; - client.runtime().mint_into(&asset_id, &admin, 1000u128)?; - - let contract = client - .instantiate( - "assets_precompile", - &admin, - &mut AssetHubPrecompileRef::new(asset_id), - ) - .submit() - .await?; - - let contract_call = contract.call_builder::(); - let result = client - .call(&admin, &contract_call.total_supply()) - .submit() - .await?; - - assert_eq!(result.return_value(), U256::from(1000)); - Ok(()) - } - - #[ink_e2e::test(runtime)] - async fn balance_of_works(mut client: Client) -> E2EResult<()> { - let asset_id: u32 = 1; - let alice = alice(); - let bob = bob(); - - client.runtime().create(&asset_id, &alice, 1u128)?; - client.runtime().mint_into(&asset_id, &alice, 1000u128)?; - client.runtime().mint_into(&asset_id, &bob, 500u128)?; - - let contract = client - .instantiate( - "assets_precompile", - &alice, - &mut AssetHubPrecompileRef::new(asset_id), - ) - .submit() - .await?; - - // Map bob's account otherwise it fails. - client.runtime().map_account(&bob)?; - - let contract_call = contract.call_builder::(); - let alice_balance = client - .call(&alice, &contract_call.balance_of(alice.address())) - .dry_run() - .await?; - assert_eq!(alice_balance.return_value(), U256::from(1000)); - let bob_balance = client - .call(&alice, &contract_call.balance_of(bob.address())) - .dry_run() - .await?; - assert_eq!(bob_balance.return_value(), U256::from(500)); - - Ok(()) - } - - #[ink_e2e::test(runtime)] - async fn transfer_works(mut client: Client) -> E2EResult<()> { - let asset_id: u32 = 1; - let alice = alice(); - let bob = bob(); - - client.runtime().create(&asset_id, &alice, 1u128)?; - - let contract = client - .instantiate( - "assets_precompile", - &alice, - &mut AssetHubPrecompileRef::new(asset_id), - ) - .submit() - .await?; - - client - .runtime() - .mint_into(&asset_id, &contract.account_id, 100_000u128)?; - client.runtime().map_account(&bob)?; - - let mut contract_call = contract.call_builder::(); - let bob_address = bob.address(); - let transfer_amount = U256::from(1_000); - - let result = client - .call( - &alice, - &contract_call.transfer(bob_address, transfer_amount), - ) - .submit() - .await?; - assert_ok!(result); - assert_last_event!( - &mut client, - Transfer { - from: contract.addr, - to: bob_address, - value: transfer_amount - } - ); - - let contract_balance = - client.runtime().balance_of(&asset_id, &contract.account_id); - let bob_balance = client.runtime().balance_of(&asset_id, &bob); - assert_eq!(contract_balance, 99_000u128); - assert_eq!(bob_balance, 1_000u128); - - let result = client - .call( - &alice, - &contract_call.transfer(bob_address, U256::from(1_000_000)), - ) - .submit() - .await?; - assert_noop!(result, "BalanceLow"); - - Ok(()) - } - - #[ink_e2e::test(runtime)] - async fn approve_works(mut client: Client) -> E2EResult<()> { - let asset_id: u32 = 1; - let alice = alice(); - let bob = bob(); - - client.runtime().create(&asset_id, &alice, 1u128)?; - - let contract = client - .instantiate("assets_precompile", &alice, &mut AssetHubPrecompileRef::new(asset_id)) - // Contract needs native balance for approval deposit. - .value(100_000) - .submit() - .await?; - - client - .runtime() - .mint_into(&asset_id, &contract.account_id, 100_000u128)?; - client.runtime().map_account(&bob)?; - let bob_allowance_before = - client - .runtime() - .allowance(&asset_id, &contract.account_id, &bob); - assert_eq!(bob_allowance_before, 0u128); // Bob's allowance is 0 - - let mut contract_call = contract.call_builder::(); - let bob_address = bob.address(); - let approve_amount = U256::from(200); - - let result = client - .call(&alice, &contract_call.approve(bob_address, approve_amount)) - .submit() - .await?; - assert_ok!(result); - assert_last_event!( - &mut client, - Approval { - owner: contract.addr, - spender: bob_address, - value: approve_amount, - } - ); - - let bob_allowance = - client - .runtime() - .allowance(&asset_id, &contract.account_id, &bob); - assert_eq!(bob_allowance, 200u128); - - Ok(()) - } - - #[ink_e2e::test(runtime)] - async fn allowance_works(mut client: Client) -> E2EResult<()> { - let asset_id: u32 = 1; - let alice = alice(); - let bob = bob(); - - client.runtime().create(&asset_id, &alice, 1u128)?; - - let contract = client - .instantiate( - "assets_precompile", - &alice, - &mut AssetHubPrecompileRef::new(asset_id), - ) - .submit() - .await?; - - let contract_call = contract.call_builder::(); - client.runtime().mint_into(&asset_id, &alice, 100_000u128)?; - client.runtime().map_account(&bob)?; - - let allowance_call = &contract_call.allowance(alice.address(), bob.address()); - let result = client.call(&alice, allowance_call).dry_run().await?; - assert_eq!(result.return_value(), U256::from(0)); - - // Approve bob to spend alice's tokens - client.runtime().approve(&asset_id, &alice, &bob, 300u128)?; - - let result = client.call(&alice, allowance_call).dry_run().await?; - assert_eq!(result.return_value(), U256::from(300)); - - Ok(()) - } - - #[ink_e2e::test(runtime)] - async fn transfer_from_works(mut client: Client) -> E2EResult<()> { - let asset_id: u32 = 1; - let alice = alice(); - let bob = bob(); - - client.runtime().create(&asset_id, &alice, 1u128)?; - - let contract = client - .instantiate( - "assets_precompile", - &alice, - &mut AssetHubPrecompileRef::new(asset_id), - ) - .submit() - .await?; - - client.runtime().mint_into(&asset_id, &alice, 100_000u128)?; - // Approve alice to spend contract's tokens - client - .runtime() - .approve(&asset_id, &alice, &contract.account_id, 50_000u128)?; - client.runtime().map_account(&bob)?; - - let mut contract_call = contract.call_builder::(); - let alice_address = alice.address(); - let bob_address = bob.address(); - let transfer_amount = U256::from(1_500); - let result = client - .call( - &alice, - &contract_call.transfer_from(alice_address, bob_address, transfer_amount), - ) - .submit() - .await?; - assert_ok!(result); - assert_last_event!( - &mut client, - Transfer { - from: alice_address, - to: bob_address, - value: transfer_amount, - } - ); - - let alice_balance = client.runtime().balance_of(&asset_id, &alice); - let bob_balance = client.runtime().balance_of(&asset_id, &bob); - let contract_allowance = - client - .runtime() - .allowance(&asset_id, &alice, &contract.account_id); - assert_eq!(alice_balance, 98_500u128); - assert_eq!(bob_balance, 1_500u128); - assert_eq!(contract_allowance, 48_500u128); - - let result = client - .call( - &alice, - &contract_call.transfer_from( - alice_address, - bob_address, - U256::from(1_000_000), - ), - ) - .submit() - .await?; - assert_noop!(result, "Unapproved"); - Ok(()) - } -} +mod tests; \ No newline at end of file diff --git a/integration-tests/public/assets-precompile/tests.rs b/integration-tests/public/assets-precompile/tests.rs new file mode 100644 index 0000000000..e054153ad8 --- /dev/null +++ b/integration-tests/public/assets-precompile/tests.rs @@ -0,0 +1,354 @@ +use super::*; +use ink::{H160, U256}; + +#[test] +fn contract_stores_asset_id() { + use asset_hub_precompile::AssetHubPrecompile; + + let contract = AssetHubPrecompile::new(1337); + + assert_eq!(contract.asset_id(), 1337); +} + +#[test] +fn contract_stores_owner() { + use asset_hub_precompile::AssetHubPrecompile; + + let contract = AssetHubPrecompile::new(1337); + + assert_eq!(contract.asset_id(), 1337); + // Note: In unit tests, the caller is always the zero address + assert_eq!(contract.owner(), H160::from([0u8; 20])); +} + +#[cfg(all(test, feature = "e2e-tests"))] +mod e2e_tests { + use super::*; + use crate::asset_hub_precompile::{ + Approval, + AssetHubPrecompile, + AssetHubPrecompileRef, + Transfer, + }; + use ink_e2e::{ + ContractsBackend, + IntoAddress, + alice, + bob, + }; + use ink_runtime::{ + E2EError, + api::prelude::{ + AssetsAPI, + ContractAPI, + }, + assert_last_event, + assert_noop, + assert_ok, + }; + + type E2EResult = std::result::Result; + + #[ink_e2e::test(runtime)] + async fn deployment_works(mut client: Client) -> E2EResult<()> { + let asset_id: u32 = 1; + let mut constructor = AssetHubPrecompileRef::new(asset_id); + + let contract = client + .instantiate("assets_precompile", &alice(), &mut constructor) + .value(1_000_000_000_000u128) // Transfer native tokens to contract + .submit() + .await?; + + let contract_call = contract.call_builder::(); + let result = client + .call(&alice(), &contract_call.asset_id()) + .dry_run() + .await?; + + assert_eq!(result.return_value(), asset_id); + + Ok(()) + } + + #[ink_e2e::test(runtime)] + async fn total_supply_works(mut client: Client) -> E2EResult<()> { + let asset_id: u32 = 1; + let admin = alice(); + + client.runtime().create(&asset_id, &admin, 1u128)?; + client.runtime().mint_into(&asset_id, &admin, 1000u128)?; + + let contract = client + .instantiate( + "assets_precompile", + &admin, + &mut AssetHubPrecompileRef::new(asset_id), + ) + .submit() + .await?; + + let contract_call = contract.call_builder::(); + let result = client + .call(&admin, &contract_call.total_supply()) + .submit() + .await?; + + assert_eq!(result.return_value(), U256::from(1000)); + Ok(()) + } + + #[ink_e2e::test(runtime)] + async fn balance_of_works(mut client: Client) -> E2EResult<()> { + let asset_id: u32 = 1; + let alice = alice(); + let bob = bob(); + + client.runtime().create(&asset_id, &alice, 1u128)?; + client.runtime().mint_into(&asset_id, &alice, 1000u128)?; + client.runtime().mint_into(&asset_id, &bob, 500u128)?; + + let contract = client + .instantiate( + "assets_precompile", + &alice, + &mut AssetHubPrecompileRef::new(asset_id), + ) + .submit() + .await?; + + // Map bob's account otherwise it fails. + client.runtime().map_account(&bob)?; + + let contract_call = contract.call_builder::(); + let alice_balance = client + .call(&alice, &contract_call.balance_of(alice.address())) + .dry_run() + .await?; + assert_eq!(alice_balance.return_value(), U256::from(1000)); + let bob_balance = client + .call(&alice, &contract_call.balance_of(bob.address())) + .dry_run() + .await?; + assert_eq!(bob_balance.return_value(), U256::from(500)); + + Ok(()) + } + + #[ink_e2e::test(runtime)] + async fn transfer_works(mut client: Client) -> E2EResult<()> { + let asset_id: u32 = 1; + let alice = alice(); + let bob = bob(); + + client.runtime().create(&asset_id, &alice, 1u128)?; + + let contract = client + .instantiate( + "assets_precompile", + &alice, + &mut AssetHubPrecompileRef::new(asset_id), + ) + .submit() + .await?; + + client + .runtime() + .mint_into(&asset_id, &contract.account_id, 100_000u128)?; + client.runtime().map_account(&bob)?; + + let mut contract_call = contract.call_builder::(); + let bob_address = bob.address(); + let transfer_amount = U256::from(1_000); + + let result = client + .call( + &alice, + &contract_call.transfer(bob_address, transfer_amount), + ) + .submit() + .await?; + assert_ok!(result); + assert_last_event!( + &mut client, + Transfer { + from: contract.addr, + to: bob_address, + value: transfer_amount + } + ); + + let contract_balance = + client.runtime().balance_of(&asset_id, &contract.account_id); + let bob_balance = client.runtime().balance_of(&asset_id, &bob); + assert_eq!(contract_balance, 99_000u128); + assert_eq!(bob_balance, 1_000u128); + + let result = client + .call( + &alice, + &contract_call.transfer(bob_address, U256::from(1_000_000)), + ) + .submit() + .await?; + assert_noop!(result, "BalanceLow"); + + Ok(()) + } + + #[ink_e2e::test(runtime)] + async fn approve_works(mut client: Client) -> E2EResult<()> { + let asset_id: u32 = 1; + let alice = alice(); + let bob = bob(); + + client.runtime().create(&asset_id, &alice, 1u128)?; + + let contract = client + .instantiate("assets_precompile", &alice, &mut AssetHubPrecompileRef::new(asset_id)) + // Contract needs native balance for approval deposit. + .value(100_000) + .submit() + .await?; + + client + .runtime() + .mint_into(&asset_id, &contract.account_id, 100_000u128)?; + client.runtime().map_account(&bob)?; + let bob_allowance_before = + client + .runtime() + .allowance(&asset_id, &contract.account_id, &bob); + assert_eq!(bob_allowance_before, 0u128); // Bob's allowance is 0 + + let mut contract_call = contract.call_builder::(); + let bob_address = bob.address(); + let approve_amount = U256::from(200); + + let result = client + .call(&alice, &contract_call.approve(bob_address, approve_amount)) + .submit() + .await?; + assert_ok!(result); + assert_last_event!( + &mut client, + Approval { + owner: contract.addr, + spender: bob_address, + value: approve_amount, + } + ); + + let bob_allowance = + client + .runtime() + .allowance(&asset_id, &contract.account_id, &bob); + assert_eq!(bob_allowance, 200u128); + + Ok(()) + } + + #[ink_e2e::test(runtime)] + async fn allowance_works(mut client: Client) -> E2EResult<()> { + let asset_id: u32 = 1; + let alice = alice(); + let bob = bob(); + + client.runtime().create(&asset_id, &alice, 1u128)?; + + let contract = client + .instantiate( + "assets_precompile", + &alice, + &mut AssetHubPrecompileRef::new(asset_id), + ) + .submit() + .await?; + + let contract_call = contract.call_builder::(); + client.runtime().mint_into(&asset_id, &alice, 100_000u128)?; + client.runtime().map_account(&bob)?; + + let allowance_call = &contract_call.allowance(alice.address(), bob.address()); + let result = client.call(&alice, allowance_call).dry_run().await?; + assert_eq!(result.return_value(), U256::from(0)); + + // Approve bob to spend alice's tokens + client.runtime().approve(&asset_id, &alice, &bob, 300u128)?; + + let result = client.call(&alice, allowance_call).dry_run().await?; + assert_eq!(result.return_value(), U256::from(300)); + + Ok(()) + } + + #[ink_e2e::test(runtime)] + async fn transfer_from_works(mut client: Client) -> E2EResult<()> { + let asset_id: u32 = 1; + let alice = alice(); + let bob = bob(); + + client.runtime().create(&asset_id, &alice, 1u128)?; + + let contract = client + .instantiate( + "assets_precompile", + &alice, + &mut AssetHubPrecompileRef::new(asset_id), + ) + .submit() + .await?; + + client.runtime().mint_into(&asset_id, &alice, 100_000u128)?; + // Approve alice to spend contract's tokens + client + .runtime() + .approve(&asset_id, &alice, &contract.account_id, 50_000u128)?; + client.runtime().map_account(&bob)?; + + let mut contract_call = contract.call_builder::(); + let alice_address = alice.address(); + let bob_address = bob.address(); + let transfer_amount = U256::from(1_500); + let result = client + .call( + &alice, + &contract_call.transfer_from(alice_address, bob_address, transfer_amount), + ) + .submit() + .await?; + assert_ok!(result); + assert_last_event!( + &mut client, + Transfer { + from: alice_address, + to: bob_address, + value: transfer_amount, + } + ); + + let alice_balance = client.runtime().balance_of(&asset_id, &alice); + let bob_balance = client.runtime().balance_of(&asset_id, &bob); + let contract_allowance = + client + .runtime() + .allowance(&asset_id, &alice, &contract.account_id); + assert_eq!(alice_balance, 98_500u128); + assert_eq!(bob_balance, 1_500u128); + assert_eq!(contract_allowance, 48_500u128); + + let result = client + .call( + &alice, + &contract_call.transfer_from( + alice_address, + bob_address, + U256::from(1_000_000), + ), + ) + .submit() + .await?; + assert_noop!(result, "Unapproved"); + Ok(()) + } +} \ No newline at end of file From 8829539cd50e4585d1854b867ba941de5857c773 Mon Sep 17 00:00:00 2001 From: Alhibb <63309522+Alhibb@users.noreply.github.com> Date: Tue, 2 Dec 2025 23:21:52 +0100 Subject: [PATCH 2/5] test refactored --- integration-tests/public/bytes/lib.rs | 155 +----------- integration-tests/public/bytes/tests.rs | 150 +++++++++++ .../public/complex-storage-structures/lib.rs | 3 + .../complex-storage-structures/tests.rs | 25 ++ .../public/conditional-compilation/tests.rs | 30 +++ .../public/contract-terminate/lib.rs | 70 +----- .../public/contract-terminate/tests.rs | 63 +++++ .../public/contract-transfer/lib.rs | 222 +---------------- .../public/contract-transfer/tests.rs | 232 ++++++++++++++++++ integration-tests/public/contract-xcm/lib.rs | 218 +++------------- .../public/contract-xcm/tests.rs | 142 +++++++++++ .../public/custom-allocator/lib.rs | 96 +------- .../public/custom-allocator/tests.rs | 78 ++++++ .../public/custom-environment/lib.rs | 83 +------ .../public/custom-environment/tests.rs | 77 ++++++ .../public/debugging-strategies/lib.rs | 205 +--------------- .../public/debugging-strategies/tests.rs | 157 ++++++++++++ integration-tests/public/dns/lib.rs | 76 +----- integration-tests/public/dns/tests.rs | 82 +++++++ .../public/e2e-call-runtime/lib.rs | 82 +------ .../public/e2e-call-runtime/tests.rs | 75 ++++++ 21 files changed, 1172 insertions(+), 1149 deletions(-) create mode 100644 integration-tests/public/bytes/tests.rs create mode 100644 integration-tests/public/complex-storage-structures/tests.rs create mode 100644 integration-tests/public/conditional-compilation/tests.rs create mode 100644 integration-tests/public/contract-terminate/tests.rs create mode 100644 integration-tests/public/contract-transfer/tests.rs create mode 100644 integration-tests/public/contract-xcm/tests.rs create mode 100644 integration-tests/public/custom-allocator/tests.rs create mode 100644 integration-tests/public/custom-environment/tests.rs create mode 100644 integration-tests/public/debugging-strategies/tests.rs create mode 100644 integration-tests/public/dns/tests.rs create mode 100644 integration-tests/public/e2e-call-runtime/tests.rs diff --git a/integration-tests/public/bytes/lib.rs b/integration-tests/public/bytes/lib.rs index ac434fe482..93e97901cf 100644 --- a/integration-tests/public/bytes/lib.rs +++ b/integration-tests/public/bytes/lib.rs @@ -59,156 +59,5 @@ pub mod bytes { } #[cfg(test)] - mod tests { - use super::*; - - #[ink::test] - fn fixed_bytes_works() { - // given - let mut bytes = Bytes::new(); - - // when - let fixed_bytes = - ink::sol::FixedBytes::from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); - bytes.handle_fixed_bytes(fixed_bytes); - - // then - let emitted_events = ink::env::test::recorded_events(); - assert_eq!(1, emitted_events.len()); - - // then - let event = &emitted_events[0]; - let mut encoded = vec![0x0; 32]; - encoded.as_mut_slice()[..8].copy_from_slice(fixed_bytes.as_slice()); - assert_eq!(encoded, event.data); - - // then - let decoded_data = - ink::sol::decode_sequence::<(ink::sol::FixedBytes<8>,)>(&event.data) - .expect("encountered invalid contract event data buffer"); - assert_eq!(decoded_data.0, fixed_bytes); - } - - #[ink::test] - fn dyn_bytes_works() { - // given - let mut bytes = Bytes::new(); - - // when - let dyn_bytes = ink::sol::DynBytes::from(vec![0x1, 0x2, 0x3, 0x4]); - bytes.handle_dyn_bytes(dyn_bytes.clone()); - - // then - let emitted_events = ink::env::test::recorded_events(); - assert_eq!(1, emitted_events.len()); - - // then - let event = &emitted_events[0]; - let mut encoded = vec![0x0; 96]; - encoded[31] = 32; // offset - encoded[63] = dyn_bytes.len() as u8; // length - encoded.as_mut_slice()[64..64 + dyn_bytes.len()] - .copy_from_slice(dyn_bytes.as_ref()); - assert_eq!(encoded, event.data); - - // then - let decoded_data = - ink::sol::decode_sequence::<(ink::sol::DynBytes,)>(&event.data) - .expect("encountered invalid contract event data buffer"); - assert_eq!(decoded_data.0, dyn_bytes); - } - } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - - use ink_e2e::ContractsBackend; - - type E2EResult = std::result::Result>; - - #[ink_e2e::test] - async fn fixed_bytes_works(mut client: Client) -> E2EResult<()> { - // given - let mut constructor = BytesRef::new(); - let contract = client - .instantiate("bytes", &ink_e2e::alice(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = contract.call_builder::(); - - // when - let fixed_bytes = - ink::sol::FixedBytes::from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); - let handler = call_builder.handle_fixed_bytes(fixed_bytes); - let res = client - .call(&ink_e2e::bob(), &handler) - .submit() - .await - .expect("fixed bytes handler failed"); - - // then - let contract_events = res.contract_emitted_events()?; - assert_eq!(1, contract_events.len()); - - // then - let contract_event = &contract_events[0]; - let mut encoded = vec![0x0; 32]; - encoded.as_mut_slice()[..8].copy_from_slice(fixed_bytes.as_slice()); - assert_eq!(encoded, contract_event.event.data); - - // then - let decoded_data = ink::sol::decode_sequence::<(ink::sol::FixedBytes<8>,)>( - &contract_event.event.data, - ) - .expect("encountered invalid contract event data buffer"); - assert_eq!(decoded_data.0, fixed_bytes); - - Ok(()) - } - - #[ink_e2e::test] - async fn dyn_bytes_works(mut client: Client) -> E2EResult<()> { - // given - let mut constructor = BytesRef::new(); - let contract = client - .instantiate("bytes", &ink_e2e::alice(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = contract.call_builder::(); - - // when - let dyn_bytes = ink::sol::DynBytes::from(vec![0x1, 0x2, 0x3, 0x4]); - let handler = call_builder.handle_dyn_bytes(dyn_bytes.clone()); - let res = client - .call(&ink_e2e::bob(), &handler) - .submit() - .await - .expect("dyn bytes handler failed"); - - // then - let contract_events = res.contract_emitted_events()?; - assert_eq!(1, contract_events.len()); - - // then - let contract_event = &contract_events[0]; - let mut encoded = vec![0x0; 96]; - encoded[31] = 32; // offset - encoded[63] = dyn_bytes.len() as u8; // length - encoded.as_mut_slice()[64..64 + dyn_bytes.len()] - .copy_from_slice(dyn_bytes.as_ref()); - assert_eq!(encoded, contract_event.event.data); - - // then - let decoded_data = ink::sol::decode_sequence::<(ink::sol::DynBytes,)>( - &contract_event.event.data, - ) - .expect("encountered invalid contract event data buffer"); - assert_eq!(decoded_data.0, dyn_bytes); - - Ok(()) - } - } -} + mod tests; +} \ No newline at end of file diff --git a/integration-tests/public/bytes/tests.rs b/integration-tests/public/bytes/tests.rs new file mode 100644 index 0000000000..615bdac9a2 --- /dev/null +++ b/integration-tests/public/bytes/tests.rs @@ -0,0 +1,150 @@ +use super::*; + +#[ink::test] +fn fixed_bytes_works() { + // given + let mut bytes = Bytes::new(); + + // when + let fixed_bytes = + ink::sol::FixedBytes::from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); + bytes.handle_fixed_bytes(fixed_bytes); + + // then + let emitted_events = ink::env::test::recorded_events(); + assert_eq!(1, emitted_events.len()); + + // then + let event = &emitted_events[0]; + let mut encoded = vec![0x0; 32]; + encoded.as_mut_slice()[..8].copy_from_slice(fixed_bytes.as_slice()); + assert_eq!(encoded, event.data); + + // then + let decoded_data = + ink::sol::decode_sequence::<(ink::sol::FixedBytes<8>,)>(&event.data) + .expect("encountered invalid contract event data buffer"); + assert_eq!(decoded_data.0, fixed_bytes); +} + +#[ink::test] +fn dyn_bytes_works() { + // given + let mut bytes = Bytes::new(); + + // when + let dyn_bytes = ink::sol::DynBytes::from(vec![0x1, 0x2, 0x3, 0x4]); + bytes.handle_dyn_bytes(dyn_bytes.clone()); + + // then + let emitted_events = ink::env::test::recorded_events(); + assert_eq!(1, emitted_events.len()); + + // then + let event = &emitted_events[0]; + let mut encoded = vec![0x0; 96]; + encoded[31] = 32; // offset + encoded[63] = dyn_bytes.len() as u8; // length + encoded.as_mut_slice()[64..64 + dyn_bytes.len()] + .copy_from_slice(dyn_bytes.as_ref()); + assert_eq!(encoded, event.data); + + // then + let decoded_data = + ink::sol::decode_sequence::<(ink::sol::DynBytes,)>(&event.data) + .expect("encountered invalid contract event data buffer"); + assert_eq!(decoded_data.0, dyn_bytes); +} + +#[cfg(all(test, feature = "e2e-tests"))] +mod e2e_tests { + use super::*; + + use ink_e2e::ContractsBackend; + + type E2EResult = std::result::Result>; + + #[ink_e2e::test] + async fn fixed_bytes_works(mut client: Client) -> E2EResult<()> { + // given + let mut constructor = BytesRef::new(); + let contract = client + .instantiate("bytes", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = contract.call_builder::(); + + // when + let fixed_bytes = + ink::sol::FixedBytes::from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); + let handler = call_builder.handle_fixed_bytes(fixed_bytes); + let res = client + .call(&ink_e2e::bob(), &handler) + .submit() + .await + .expect("fixed bytes handler failed"); + + // then + let contract_events = res.contract_emitted_events()?; + assert_eq!(1, contract_events.len()); + + // then + let contract_event = &contract_events[0]; + let mut encoded = vec![0x0; 32]; + encoded.as_mut_slice()[..8].copy_from_slice(fixed_bytes.as_slice()); + assert_eq!(encoded, contract_event.event.data); + + // then + let decoded_data = ink::sol::decode_sequence::<(ink::sol::FixedBytes<8>,)>( + &contract_event.event.data, + ) + .expect("encountered invalid contract event data buffer"); + assert_eq!(decoded_data.0, fixed_bytes); + + Ok(()) + } + + #[ink_e2e::test] + async fn dyn_bytes_works(mut client: Client) -> E2EResult<()> { + // given + let mut constructor = BytesRef::new(); + let contract = client + .instantiate("bytes", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = contract.call_builder::(); + + // when + let dyn_bytes = ink::sol::DynBytes::from(vec![0x1, 0x2, 0x3, 0x4]); + let handler = call_builder.handle_dyn_bytes(dyn_bytes.clone()); + let res = client + .call(&ink_e2e::bob(), &handler) + .submit() + .await + .expect("dyn bytes handler failed"); + + // then + let contract_events = res.contract_emitted_events()?; + assert_eq!(1, contract_events.len()); + + // then + let contract_event = &contract_events[0]; + let mut encoded = vec![0x0; 96]; + encoded[31] = 32; // offset + encoded[63] = dyn_bytes.len() as u8; // length + encoded.as_mut_slice()[64..64 + dyn_bytes.len()] + .copy_from_slice(dyn_bytes.as_ref()); + assert_eq!(encoded, contract_event.event.data); + + // then + let decoded_data = ink::sol::decode_sequence::<(ink::sol::DynBytes,)>( + &contract_event.event.data, + ) + .expect("encountered invalid contract event data buffer"); + assert_eq!(decoded_data.0, dyn_bytes); + + Ok(()) + } +} \ No newline at end of file diff --git a/integration-tests/public/complex-storage-structures/lib.rs b/integration-tests/public/complex-storage-structures/lib.rs index 9c13407606..ce3f8dd957 100644 --- a/integration-tests/public/complex-storage-structures/lib.rs +++ b/integration-tests/public/complex-storage-structures/lib.rs @@ -107,3 +107,6 @@ pub mod complex_structures { } } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/complex-storage-structures/tests.rs b/integration-tests/public/complex-storage-structures/tests.rs new file mode 100644 index 0000000000..6fe9c7a7fa --- /dev/null +++ b/integration-tests/public/complex-storage-structures/tests.rs @@ -0,0 +1,25 @@ +use super::complex_structures::*; +use ink_e2e::ContractsBackend; + +type E2EResult = std::result::Result>; + +#[ink_e2e::test] +async fn deployment_works(mut client: Client) -> E2EResult<()> { + let mut constructor = ContractRef::new(); + + let contract = client + .instantiate("complex_storage_structures", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder:: let get_balance = call_builder.get_balances_state(); + let get_balance_result = client + .call(&ink_e2e::alice(), &get_balance) + .submit() + .await + .expect("call failed"); + + assert_eq!(get_balance_result.return_value(), 0); + + Ok(()) +} \ No newline at end of file diff --git a/integration-tests/public/conditional-compilation/tests.rs b/integration-tests/public/conditional-compilation/tests.rs new file mode 100644 index 0000000000..90b621b98e --- /dev/null +++ b/integration-tests/public/conditional-compilation/tests.rs @@ -0,0 +1,30 @@ +use super::Flip; +use super::conditional_compilation::ConditionalCompilation; + +#[ink::test] +fn default_works() { + let flipper = ConditionalCompilation::new(); + assert!(!flipper.get()); +} + +#[ink::test] +fn it_works() { + let mut flipper = ConditionalCompilation::new(); + // Can call using universal call syntax using the trait. + assert!(!::get(&flipper)); + ::flip(&mut flipper); + // Normal call syntax possible to as long as the trait is in scope. + assert!(flipper.get()); +} + +#[cfg(feature = "foo")] +#[ink::test] +fn foo_works() { + let mut flipper = ConditionalCompilation::new_foo(false); + + flipper.inherent_flip_foo(); + assert!(flipper.get()); + + ::push_foo(&mut flipper, false); + assert!(!flipper.get()) +} \ No newline at end of file diff --git a/integration-tests/public/contract-terminate/lib.rs b/integration-tests/public/contract-terminate/lib.rs index 965813bd61..640073aca1 100644 --- a/integration-tests/public/contract-terminate/lib.rs +++ b/integration-tests/public/contract-terminate/lib.rs @@ -23,71 +23,7 @@ pub mod just_terminates { self.env().terminate_contract(self.env().caller()); } } - - #[cfg(test)] - mod tests { - use super::*; - - #[ink::test] - fn terminating_works() { - // given - let accounts = ink::env::test::default_accounts(); - let contract_id = ink::env::test::callee(); - ink::env::test::set_caller(accounts.alice); - ink::env::test::set_contract_balance(contract_id, 100.into()); - let mut contract = JustTerminate::new(); - - // when - let should_terminate = move || contract.terminate_me(); - - // then - ink::env::test::assert_contract_termination::( - should_terminate, - accounts.alice, - 100.into(), - ); - } - } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink_e2e::ContractsBackend; - - type E2EResult = std::result::Result>; - - #[ink_e2e::test] - async fn e2e_contract_terminates(mut client: Client) -> E2EResult<()> { - // given - let mut constructor = JustTerminateRef::new(); - let contract = client - .instantiate("contract_terminate", &ink_e2e::alice(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = contract.call_builder::(); - - // when - let terminate_me = call_builder.terminate_me(); - let call_res = client - .call(&ink_e2e::alice(), &terminate_me) - .submit() - .await - .expect("terminate_me messages failed"); - - assert!( - call_res.return_data().is_empty(), - "Terminated contract never returns" - ); - - // then - assert!(call_res.contains_event("System", "KilledAccount")); - assert!(call_res.contains_event("Balances", "Withdraw")); - // todo this event below no longer exists, but we could try getting - // info for the contract and asserting that it fails. - // assert!(call_res.contains_event("Revive", "Terminated")); - - Ok(()) - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/contract-terminate/tests.rs b/integration-tests/public/contract-terminate/tests.rs new file mode 100644 index 0000000000..c44fa740de --- /dev/null +++ b/integration-tests/public/contract-terminate/tests.rs @@ -0,0 +1,63 @@ +use super::just_terminates::*; + +#[ink::test] +fn terminating_works() { + // given + let accounts = ink::env::test::default_accounts(); + let contract_id = ink::env::test::callee(); + ink::env::test::set_caller(accounts.alice); + ink::env::test::set_contract_balance(contract_id, 100.into()); + let mut contract = JustTerminate::new(); + + // when + let should_terminate = move || contract.terminate_me(); + + // then + ink::env::test::assert_contract_termination::( + should_terminate, + accounts.alice, + 100.into(), + ); +} + +#[cfg(all(test, feature = "e2e-tests"))] +mod e2e_tests { + use super::*; + use ink_e2e::ContractsBackend; + + type E2EResult = std::result::Result>; + + #[ink_e2e::test] + async fn e2e_contract_terminates(mut client: Client) -> E2EResult<()> { + // given + let mut constructor = JustTerminateRef::new(); + let contract = client + .instantiate("contract_terminate", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = contract.call_builder::(); + + // when + let terminate_me = call_builder.terminate_me(); + let call_res = client + .call(&ink_e2e::alice(), &terminate_me) + .submit() + .await + .expect("terminate_me messages failed"); + + assert!( + call_res.return_data().is_empty(), + "Terminated contract never returns" + ); + + // then + assert!(call_res.contains_event("System", "KilledAccount")); + assert!(call_res.contains_event("Balances", "Withdraw")); + // todo this event below no longer exists, but we could try getting + // info for the contract and asserting that it fails. + // assert!(call_res.contains_event("Revive", "Terminated")); + + Ok(()) + } +} \ No newline at end of file diff --git a/integration-tests/public/contract-transfer/lib.rs b/integration-tests/public/contract-transfer/lib.rs index bafdbbc25d..20803fd59f 100644 --- a/integration-tests/public/contract-transfer/lib.rs +++ b/integration-tests/public/contract-transfer/lib.rs @@ -14,6 +14,8 @@ pub mod give_me { impl GiveMe { /// Creates a new instance of this contract. + /// + /// This is a payable constructor, meaning it can receive initial funding. #[ink(constructor, payable)] pub fn new() -> Self { Self {} @@ -62,220 +64,8 @@ pub mod give_me { ); } } - - #[cfg(test)] - mod tests { - use super::*; - - #[ink::test] - fn transfer_works() { - // given - let contract_balance = 100.into(); - let accounts = default_accounts(); - let mut give_me = create_contract(contract_balance); - - // when - set_sender(accounts.eve); - set_balance(accounts.eve, 0.into()); - give_me.give_me(80.into()); - - // then - assert_eq!(get_balance(accounts.eve), 80.into()); - } - - #[ink::test] - #[should_panic(expected = "insufficient funds!")] - fn transfer_fails_insufficient_funds() { - // given - let contract_balance = 100.into(); - let accounts = default_accounts(); - let mut give_me = create_contract(contract_balance); - - // when - set_sender(accounts.eve); - give_me.give_me(120.into()); - - // then - // `give_me` must already have panicked here - } - - #[ink::test] - fn test_transferred_value() { - use ink::codegen::Env; - // given - let accounts = default_accounts(); - let mut give_me = create_contract(100.into()); - let contract_account = give_me.env().address(); - - // when - // Push the new execution context which sets initial balances and - // sets Eve as the caller - set_balance(accounts.eve, 100.into()); - set_balance(contract_account, 0.into()); - set_sender(accounts.eve); - - // then - // we use helper macro to emulate method invocation coming with payment, - // and there must be no panic - ink::env::pay_with_call!(give_me.was_it_ten(), 10.into()); - - // and - // balances should be changed properly - let contract_new_balance = get_balance(contract_account); - let caller_new_balance = get_balance(accounts.eve); - - assert_eq!(caller_new_balance, (100 - 10).into()); - assert_eq!(contract_new_balance, 10.into()); - } - - #[ink::test] - #[should_panic(expected = "payment was not ten")] - fn test_transferred_value_must_fail() { - // given - let accounts = default_accounts(); - let mut give_me = create_contract(100.into()); - - // when - // Push the new execution context which sets Eve as caller and - // the `mock_transferred_value` as the value which the contract - // will see as transferred to it. - set_sender(accounts.eve); - ink::env::test::set_value_transferred(13.into()); - - // then - give_me.was_it_ten(); - } - - /// Creates a new instance of `GiveMe` with `initial_balance`. - /// - /// Returns the `contract_instance`. - fn create_contract(initial_balance: U256) -> GiveMe { - let accounts = default_accounts(); - set_sender(accounts.alice); - set_balance(contract_id(), initial_balance); - GiveMe::new() - } - - fn contract_id() -> Address { - ink::env::test::callee() - } - - fn set_sender(sender: Address) { - ink::env::test::set_caller(sender); - } - - fn default_accounts() -> ink::env::test::DefaultAccounts { - ink::env::test::default_accounts() - } - - fn set_balance(addr: Address, balance: U256) { - ink::env::test::set_contract_balance(addr, balance) - } - - fn get_balance(addr: Address) -> U256 { - ink::env::test::get_contract_balance::(addr) - .expect("Cannot get contract balance") - } - } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink::env::Environment; - use ink_e2e::{ - ChainBackend, - ContractsBackend, - }; - - type E2EResult = std::result::Result>; - - #[ink_e2e::test] - async fn e2e_sending_value_to_give_me_must_fail( - mut client: Client, - ) -> E2EResult<()> { - // given - let mut constructor = GiveMeRef::new(); - let contract = client - .instantiate("contract_transfer", &ink_e2e::alice(), &mut constructor) - .value(1_000_000_000) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = contract.call_builder::(); - - // when - let transfer = call_builder.give_me(120_000_000.into()); - - let call_res = client - .call(&ink_e2e::bob(), &transfer) - .value(10_000_000) - .submit() - .await; - - // then - assert!(call_res.is_err(), "call must have errored"); - /* - // todo bug with wrong printing of message - if let Err(ink_e2e::Error::CallDryRun(dry_run)) = call_res { - assert!(dry_run.debug_message.contains("paid an unpayable message")) - } else { - panic!("Paying an unpayable message should fail") - } - */ - Ok(()) - } - - #[ink_e2e::test(runtime)] - async fn e2e_contract_must_transfer_value_to_sender( - mut client: Client, - ) -> E2EResult<()> { - // given - let mut constructor = GiveMeRef::new(); - let contract = client - .instantiate("contract_transfer", &ink_e2e::bob(), &mut constructor) - // todo convert the argument type to U256 - .value(1_337_000_000) - .submit() - .await - .expect("instantiate failed"); - let contract_addr = contract.addr; - - assert_eq!( - contract.trace.clone().unwrap().value, - Some(ink::env::DefaultEnvironment::native_to_eth(1_337_000_000)) - ); - let mut call_builder = contract.call_builder::(); - - let balance_before: Balance = client - .free_balance(contract.account_id) - .await - .expect("getting balance failed"); - - // when - let transfer = call_builder.give_me(U256::from(120_000_000_0)); - - let call_res = client - .call(&ink_e2e::eve(), &transfer) - .submit() - .await - .expect("call failed"); - - // then - let outgoing_trace = &call_res.trace.unwrap().calls[0]; - assert_eq!(outgoing_trace.value, Some(U256::from(120_000_000_0))); - assert_eq!(outgoing_trace.from, contract_addr); - assert_eq!( - outgoing_trace.to, - ink_e2e::address_from_keypair::(&ink_e2e::eve()) - ); - - let balance_after: Balance = client - .free_balance(contract.account_id) - .await - .expect("getting balance failed"); - assert_eq!(balance_before - balance_after, 12); - - Ok(()) - } - } } + +// Include the test file +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/contract-transfer/tests.rs b/integration-tests/public/contract-transfer/tests.rs new file mode 100644 index 0000000000..3eb47e1c08 --- /dev/null +++ b/integration-tests/public/contract-transfer/tests.rs @@ -0,0 +1,232 @@ +use super::give_me::*; +use ink::primitives::{Address, U256}; + +// ================================================================================= +// UNIT TESTS +// ================================================================================= +// These tests run in the off-chain environment provided by `ink::env::test`. +// They simulate the contract logic without spinning up a full node. + +#[ink::test] +fn transfer_works() { + // given + let contract_balance = 100.into(); + let accounts = default_accounts(); + let mut give_me = create_contract(contract_balance); + + // when + set_sender(accounts.eve); + set_balance(accounts.eve, 0.into()); + // Eve requests 80 tokens + give_me.give_me(80.into()); + + // then + assert_eq!(get_balance(accounts.eve), 80.into()); +} + +#[ink::test] +#[should_panic(expected = "insufficient funds!")] +fn transfer_fails_insufficient_funds() { + // given + let contract_balance = 100.into(); + let accounts = default_accounts(); + let mut give_me = create_contract(contract_balance); + + // when + set_sender(accounts.eve); + // Eve requests 120 tokens (more than contract has) + give_me.give_me(120.into()); + + // then + // `give_me` must already have panicked here +} + +#[ink::test] +fn test_transferred_value() { + use ink::codegen::Env; + // given + let accounts = default_accounts(); + let mut give_me = create_contract(100.into()); + let contract_account = give_me.env().address(); + + // when + // Push the new execution context which sets initial balances and + // sets Eve as the caller + set_balance(accounts.eve, 100.into()); + set_balance(contract_account, 0.into()); + set_sender(accounts.eve); + + // then + // we use helper macro to emulate method invocation coming with payment, + // and there must be no panic + ink::env::pay_with_call!(give_me.was_it_ten(), 10.into()); + + // and + // balances should be changed properly + let contract_new_balance = get_balance(contract_account); + let caller_new_balance = get_balance(accounts.eve); + + assert_eq!(caller_new_balance, (100 - 10).into()); + assert_eq!(contract_new_balance, 10.into()); +} + +#[ink::test] +#[should_panic(expected = "payment was not ten")] +fn test_transferred_value_must_fail() { + // given + let accounts = default_accounts(); + let mut give_me = create_contract(100.into()); + + // when + // Push the new execution context which sets Eve as caller and + // the `mock_transferred_value` as the value which the contract + // will see as transferred to it. + set_sender(accounts.eve); + ink::env::test::set_value_transferred(13.into()); + + // then + // Expect panic because we sent 13, but contract expects 10 + give_me.was_it_ten(); +} + +// --- Helper Functions for Unit Tests --- + +/// Creates a new instance of `GiveMe` with `initial_balance`. +/// Returns the `contract_instance`. +fn create_contract(initial_balance: U256) -> GiveMe { + let accounts = default_accounts(); + set_sender(accounts.alice); + set_balance(contract_id(), initial_balance); + GiveMe::new() +} + +fn contract_id() -> Address { + ink::env::test::callee() +} + +fn set_sender(sender: Address) { + ink::env::test::set_caller(sender); +} + +fn default_accounts() -> ink::env::test::DefaultAccounts { + ink::env::test::default_accounts() +} + +fn set_balance(addr: Address, balance: U256) { + ink::env::test::set_contract_balance(addr, balance) +} + +fn get_balance(addr: Address) -> U256 { + ink::env::test::get_contract_balance::(addr) + .expect("Cannot get contract balance") +} + +// ================================================================================= +// END-TO-END (E2E) TESTS +// ================================================================================= +// These tests run against a simulated node (sandbox) using `ink_e2e`. + +#[cfg(all(test, feature = "e2e-tests"))] +mod e2e_tests { + use super::*; + use ink::env::Environment; + use ink_e2e::{ + ChainBackend, + ContractsBackend, + AccountId, + Balance, + }; + + type E2EResult = std::result::Result>; + + /// Tests that paying a method that isn't `payable` results in an error. + #[ink_e2e::test] + async fn e2e_sending_value_to_give_me_must_fail( + mut client: Client, + ) -> E2EResult<()> { + // given + let mut constructor = GiveMeRef::new(); + let contract = client + .instantiate("contract_transfer", &ink_e2e::alice(), &mut constructor) + .value(1_000_000_000) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = contract.call_builder::(); + + // when + // We try to call `give_me` (which is NOT payable) but we attach value. + let transfer = call_builder.give_me(120_000_000.into()); + + let call_res = client + .call(&ink_e2e::bob(), &transfer) + .value(10_000_000) // This is the illegal payment + .submit() + .await; + + // then + assert!(call_res.is_err(), "call must have errored"); + + Ok(()) + } + + /// Tests that the contract can successfully transfer funds back to the caller. + #[ink_e2e::test(runtime)] + async fn e2e_contract_must_transfer_value_to_sender( + mut client: Client, + ) -> E2EResult<()> { + // given + let mut constructor = GiveMeRef::new(); + let contract = client + .instantiate("contract_transfer", &ink_e2e::bob(), &mut constructor) + .value(1_337_000_000) // Initial endowment to the contract + .submit() + .await + .expect("instantiate failed"); + let contract_addr = contract.addr; + + // Check trace to verify initial value transfer + assert_eq!( + contract.trace.clone().unwrap().value, + Some(ink::env::DefaultEnvironment::native_to_eth(1_337_000_000)) + ); + let mut call_builder = contract.call_builder::(); + + let balance_before: Balance = client + .free_balance(contract.account_id) + .await + .expect("getting balance failed"); + + // when + // Eve calls the contract asking for funds + let transfer = call_builder.give_me(U256::from(120_000_000_0)); + + let call_res = client + .call(&ink_e2e::eve(), &transfer) + .submit() + .await + .expect("call failed"); + + // then + // Verify trace data + let outgoing_trace = &call_res.trace.unwrap().calls[0]; + assert_eq!(outgoing_trace.value, Some(U256::from(120_000_000_0))); + assert_eq!(outgoing_trace.from, contract_addr); + assert_eq!( + outgoing_trace.to, + ink_e2e::address_from_keypair::(&ink_e2e::eve()) + ); + + // Verify balance changes + let balance_after: Balance = client + .free_balance(contract.account_id) + .await + .expect("getting balance failed"); + + // Note: The difference includes gas costs + transferred value. + // In this specific test setup, we check the rough difference or exact if gas is handled. + assert_eq!(balance_before - balance_after, 12); + + Ok(()) + } +} \ No newline at end of file diff --git a/integration-tests/public/contract-xcm/lib.rs b/integration-tests/public/contract-xcm/lib.rs index 0f3d8e5e4b..76bdcee80a 100644 --- a/integration-tests/public/contract-xcm/lib.rs +++ b/integration-tests/public/contract-xcm/lib.rs @@ -25,24 +25,29 @@ mod contract_xcm { Default::default() } - /// Tries to transfer `value` from the contract's balance to `receiver`. + /// Tries to transfer `value` from the contract's balance to `receiver` + /// on the SAME chain using XCM execution. /// - /// Fails if: - /// - called in the off-chain environment - /// - the chain is not configured to support XCM - /// - the XCM program executed failed (e.g contract doesn't have enough balance) + /// This demonstrates `xcm_execute`, which executes an XCM message locally. #[ink(message)] pub fn transfer_through_xcm( &mut self, receiver: AccountId, value: Balance, ) -> Result<(), RuntimeError> { + // Define the asset as the native currency (Parent) with amount `value`. let asset: Asset = (Parent, value).into(); + + // Define beneficiary on the current network. let beneficiary = AccountId32 { network: None, id: *receiver.as_ref(), }; + // Build the XCM message: + // 1. Withdraw asset from contract's account. + // 2. Buy execution time (gas) for the XCM VM. + // 3. Deposit the asset into the beneficiary's account. let message: ink::xcm::v5::Xcm<()> = Xcm::builder() .withdraw_asset(asset.clone()) .buy_execution(asset.clone(), Unlimited) @@ -50,56 +55,48 @@ mod contract_xcm { .build(); let msg = VersionedXcm::V5(message); + // Calculate the weight (gas) required for this XCM message. let weight = self.env().xcm_weigh(&msg).expect("weight should work"); + // Execute the XCM message locally. self.env() .xcm_execute(&msg, weight) .map_err(|_| RuntimeError::XcmExecuteFailed) } - /// Transfer some funds to the relay chain via XCM from the contract's derivative - /// account to the caller's account. + /// Transfer some funds to the relay chain via XCM using `xcm_send`. /// - /// Fails if: - /// - called in the off-chain environment - /// - the chain is not configured to support XCM - /// - the XCM program executed failed (e.g. contract doesn't have enough balance) + /// This sends an XCM message to another chain (the Parent/Relay Chain). #[ink(message)] pub fn send_funds( &mut self, value: Balance, fee: Balance, ) -> Result<(), RuntimeError> { - // The destination of the XCM message. Assuming we run the contract - // on a parachain, the parent will be the relay chain. + // Target destination: The Parent chain (Relay Chain). let destination: ink::xcm::v5::Location = ink::xcm::v5::Parent.into(); - // The asset to be sent, since we are sending the XCM to the relay chain, - // this represents `value` amount of the relay chain's native asset. + // Asset: Native token of the relay chain (represented as Here relative to Parent). let asset: Asset = (Here, value).into(); - // The beneficiary of the asset. - // Here, the beneficiary is the caller's account on the relay chain. + // Beneficiary: The caller's account on the Relay Chain. let caller_account_id = self.env().to_account_id(self.env().caller()); let beneficiary = AccountId32 { network: None, id: caller_account_id.0, }; - // Create an XCM message + // Build XCM: + // 1. Withdraw asset from this chain's sovereign account on the Relay Chain. + // 2. Buy execution on the Relay Chain using the withdrawn asset. + // 3. Deposit asset to the caller's account on the Relay Chain. let message: Xcm<()> = Xcm::builder() - // Withdraw the asset from the origin (the sovereign account of the - // contract on the relay chain) .withdraw_asset(asset.clone()) - - // Buy execution to pay the fee on the relay chain .buy_execution((Here, fee), WeightLimit::Unlimited) - - // Deposit the asset to the caller's account on the relay chain .deposit_asset(asset, beneficiary) .build(); - // Send the constructed XCM message to the relay chain. + // Send the message to the Relay Chain. self.env() .xcm_send( &VersionedLocation::V5(destination), @@ -108,14 +105,13 @@ mod contract_xcm { .map_err(|_| RuntimeError::XcmSendFailed) } + /// Initiates a reserve transfer, burning tokens here and releasing them on the Parent chain. #[ink(message)] pub fn reserve_transfer( &mut self, amount: Balance, fee: Balance, ) -> Result<(), RuntimeError> { - // The beneficiary of the transfer. - // Here, the beneficiary is the caller's account on the relay chain. let caller_account_id = self.env().to_account_id(self.env().caller()); let beneficiary: Location = AccountId32 { network: None, @@ -123,19 +119,13 @@ mod contract_xcm { } .into(); - // Create an XCM message. + // Build XCM using `builder_unsafe` for advanced operations like reserve transfers. let message: Xcm<()> = Xcm::builder_unsafe() - // Withdraw the relay's native token derivative from the - // contract's account. + // Withdraw the derivative token (Parent) from contract's local account. .withdraw_asset((Parent, amount)) - // The `initiate_reserve_withdraw` instruction takes the - // derivative token from the holding register and burns it. - // It then sends the nested XCM to the reserve in this - // example, the relay chain. - // Upon receiving the XCM, the reserve will withdraw the - // asset from our chain's sovereign account, and deposit - // on the caller's account. + // Burn the local derivative and send an instruction to the Reserve (Parent) + // to release the real asset to the beneficiary. .initiate_reserve_withdraw( All, Parent, @@ -148,159 +138,13 @@ mod contract_xcm { let msg = VersionedXcm::V5(message); let weight = self.env().xcm_weigh(&msg).expect("`xcm_weigh` failed"); + self.env() .xcm_execute(&msg, weight) .map_err(|_| RuntimeError::XcmExecuteFailed) } } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink::primitives::AccountId; - use ink_e2e::{ - ChainBackend, - ContractsBackend, - }; - - type E2EResult = Result>; - - #[ink_e2e::test] - async fn xcm_execute_works(mut client: Client) -> E2EResult<()> { - // given - let mut constructor = ContractXcmRef::new(); - let contract = client - .instantiate("contract_xcm", &ink_e2e::alice(), &mut constructor) - .value(100_000_000_000) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = contract.call_builder::(); - - let receiver = AccountId::from(ink_e2e::bob().public_key().0); - - let contract_balance_before = client - .free_balance(contract.account_id) - .await - .expect("Failed to get account balance"); - let receiver_balance_before = client - .free_balance(receiver) - .await - .expect("Failed to get account balance"); - - // when - let amount = 100_000_000; - let transfer_message = call_builder.transfer_through_xcm(receiver, amount); - let call_res = client - .call(&ink_e2e::alice(), &transfer_message) - .submit() - .await - .expect("call failed"); - assert!(call_res.return_value().is_ok()); - - // then - let contract_balance_after = client - .free_balance(contract.account_id) - .await - .expect("Failed to get account balance"); - let receiver_balance_after = client - .free_balance(receiver) - .await - .expect("Failed to get account balance"); - - assert_eq!(contract_balance_after, contract_balance_before - amount); - assert_eq!(receiver_balance_after, receiver_balance_before + amount); - - Ok(()) - } - - #[ink_e2e::test] - async fn xcm_execute_failure_detection_works( - mut client: Client, - ) -> E2EResult<()> { - // todo @cmichi: This sleep is necessary until we have our `ink-node` - // support a parachain/relaychain setup. For the moment we use the - // Rococo runtime for testing the examples locally. That runtime - // only has Alice and Bob endowed. Due to the nature of the tests - // we have to use Alice for sending the transactions. If the tests - // run at the same time, we'll get an error because the nonce - // of Alice is the same for all transactions. - std::thread::sleep(std::time::Duration::from_secs(10)); - - // given - let mut constructor = ContractXcmRef::new(); - let contract = client - .instantiate("contract_xcm", &ink_e2e::alice(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = contract.call_builder::(); - - // when - let receiver = AccountId::from(ink_e2e::bob().public_key().0); - let amount = u128::MAX; - let transfer_message = call_builder.transfer_through_xcm(receiver, amount); - - // then - let call_res = client - .call(&ink_e2e::alice(), &transfer_message) - .submit() - .await; - assert!(call_res.is_err()); - - let expected = "revert: XCM execute failed: message may be invalid or execution constraints not satisfied"; - assert!(format!("{:?}", call_res).contains(expected)); - - Ok(()) - } - - #[ink_e2e::test] - async fn xcm_send_works(mut client: Client) -> E2EResult<()> { - // todo @cmichi: This sleep is necessary until we have our `ink-node` - // support a parachain/relaychain setup. For the moment we use the - // Rococo runtime for testing the examples locally. That runtime - // only has Alice and Bob endowed. Due to the nature of the tests - // we have to use Alice for sending the transactions. If the tests - // run at the same time, we'll get an error because the nonce - // of Alice is the same for all transactions. - std::thread::sleep(std::time::Duration::from_secs(30)); - - // given - let mut constructor = ContractXcmRef::new(); - let contract = client - .instantiate("contract_xcm", &ink_e2e::alice(), &mut constructor) - .value(100_000_000_000) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = contract.call_builder::(); - - let contract_balance_before = client - .free_balance(contract.account_id) - .await - .expect("Failed to get account balance"); - - // when - let amount = 100_000_000; - let transfer_message = call_builder.send_funds(amount, amount / 2); - let call_res = client - .call(&ink_e2e::alice(), &transfer_message) - .submit() - .await - .expect("call failed"); - assert!(call_res.return_value().is_ok()); - - // then - let contract_balance_after = client - .free_balance(contract.account_id) - .await - .expect("Failed to get account balance"); - - assert!( - contract_balance_after <= contract_balance_before - amount - (amount / 2) - ); - - Ok(()) - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/contract-xcm/tests.rs b/integration-tests/public/contract-xcm/tests.rs new file mode 100644 index 0000000000..1b7388accd --- /dev/null +++ b/integration-tests/public/contract-xcm/tests.rs @@ -0,0 +1,142 @@ +#[cfg(all(test, feature = "e2e-tests"))] +mod e2e_tests { + use super::contract_xcm::{ContractXcm, ContractXcmRef}; + use ink::primitives::AccountId; + use ink_e2e::{ + ChainBackend, + ContractsBackend, + }; + + type E2EResult = Result>; + + /// Tests that `xcm_execute` correctly transfers funds locally via XCM instructions. + #[ink_e2e::test] + async fn xcm_execute_works(mut client: Client) -> E2EResult<()> { + // Given: Instantiate contract with an initial endowment. + let mut constructor = ContractXcmRef::new(); + let contract = client + .instantiate("contract_xcm", &ink_e2e::alice(), &mut constructor) + .value(100_000_000_000) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = contract.call_builder::(); + + let receiver = AccountId::from(ink_e2e::bob().public_key().0); + + let contract_balance_before = client + .free_balance(contract.account_id) + .await + .expect("Failed to get account balance"); + let receiver_balance_before = client + .free_balance(receiver) + .await + .expect("Failed to get account balance"); + + // When: Execute XCM transfer of 100_000_000 units to receiver. + let amount = 100_000_000; + let transfer_message = call_builder.transfer_through_xcm(receiver, amount); + let call_res = client + .call(&ink_e2e::alice(), &transfer_message) + .submit() + .await + .expect("call failed"); + assert!(call_res.return_value().is_ok()); + + // Then: Verify balances updated correctly. + let contract_balance_after = client + .free_balance(contract.account_id) + .await + .expect("Failed to get account balance"); + let receiver_balance_after = client + .free_balance(receiver) + .await + .expect("Failed to get account balance"); + + assert_eq!(contract_balance_after, contract_balance_before - amount); + assert_eq!(receiver_balance_after, receiver_balance_before + amount); + + Ok(()) + } + + /// Tests that `xcm_execute` fails gracefully when funds are insufficient. + #[ink_e2e::test] + async fn xcm_execute_failure_detection_works( + mut client: Client, + ) -> E2EResult<()> { + // Sleep to avoid nonce collision with other tests using Alice. + std::thread::sleep(std::time::Duration::from_secs(10)); + + // Given: Instantiate contract. + let mut constructor = ContractXcmRef::new(); + let contract = client + .instantiate("contract_xcm", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = contract.call_builder::(); + + // When: Try to transfer `u128::MAX` (impossible amount). + let receiver = AccountId::from(ink_e2e::bob().public_key().0); + let amount = u128::MAX; + let transfer_message = call_builder.transfer_through_xcm(receiver, amount); + + // Then: The call should return an error indicating execution failure. + let call_res = client + .call(&ink_e2e::alice(), &transfer_message) + .submit() + .await; + assert!(call_res.is_err()); + + // Verify the specific error message. + let expected = "revert: XCM execute failed: message may be invalid or execution constraints not satisfied"; + assert!(format!("{:?}", call_res).contains(expected)); + + Ok(()) + } + + /// Tests that `xcm_send` successfully dispatches a message to the Relay Chain. + /// Note: This tests the *dispatch*, not the successful execution on the remote chain. + #[ink_e2e::test] + async fn xcm_send_works(mut client: Client) -> E2EResult<()> { + // Sleep to avoid nonce collision. + std::thread::sleep(std::time::Duration::from_secs(30)); + + // Given: Instantiate contract with funds. + let mut constructor = ContractXcmRef::new(); + let contract = client + .instantiate("contract_xcm", &ink_e2e::alice(), &mut constructor) + .value(100_000_000_000) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = contract.call_builder::(); + + let contract_balance_before = client + .free_balance(contract.account_id) + .await + .expect("Failed to get account balance"); + + // When: Send funds via XCM to the Relay Chain. + let amount = 100_000_000; + let transfer_message = call_builder.send_funds(amount, amount / 2); + let call_res = client + .call(&ink_e2e::alice(), &transfer_message) + .submit() + .await + .expect("call failed"); + assert!(call_res.return_value().is_ok()); + + // Then: Contract balance should decrease (amount sent + execution fees). + let contract_balance_after = client + .free_balance(contract.account_id) + .await + .expect("Failed to get account balance"); + + assert!( + contract_balance_after <= contract_balance_before - amount - (amount / 2) + ); + + Ok(()) + } +} \ No newline at end of file diff --git a/integration-tests/public/custom-allocator/lib.rs b/integration-tests/public/custom-allocator/lib.rs index f77aec9ffe..1c1e89f64e 100755 --- a/integration-tests/public/custom-allocator/lib.rs +++ b/integration-tests/public/custom-allocator/lib.rs @@ -2,7 +2,7 @@ //! //! This example demonstrates how to opt-out of the ink! provided global memory allocator. //! -//! We will use [`dlmalloc`](https://github.com/alexcrichton/dlmalloc-rs) instead. +//! We will use a custom bump allocator implementation as an example. //! //! ## Warning! //! @@ -30,15 +30,6 @@ #![feature(sync_unsafe_cell)] #![feature(allocator_api)] -// todo -// Here we set `dlmalloc` to be the global memory allocator. -// -// The [`GlobalAlloc`](https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html) trait is -// important to understand if you're swapping our your allocator. -//#[cfg(not(feature = "std"))] -//#[global_allocator] -//static ALLOC: dlmalloc::GlobalDlmalloc = dlmalloc::GlobalDlmalloc; - use core::{ alloc::{ GlobalAlloc, @@ -139,86 +130,7 @@ mod custom_allocator { self.value[0] } } - - #[cfg(test)] - mod tests { - use super::*; - - #[ink::test] - fn default_works() { - let custom_allocator = CustomAllocator::default(); - assert!(!custom_allocator.get()); - } - - #[ink::test] - fn it_works() { - let mut custom_allocator = CustomAllocator::new(false); - assert!(!custom_allocator.get()); - custom_allocator.flip(); - assert!(custom_allocator.get()); - } - } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink_e2e::ContractsBackend; - - type E2EResult = std::result::Result>; - - /// We test that we can upload and instantiate the contract using its default - /// constructor. - #[ink_e2e::test] - async fn default_works(mut client: Client) -> E2EResult<()> { - // Given - let mut constructor = CustomAllocatorRef::default(); - - // When - let contract = client - .instantiate("custom_allocator", &ink_e2e::alice(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let call_builder = contract.call_builder::(); - - // Then - let get = call_builder.get(); - let get_result = client.call(&ink_e2e::alice(), &get).dry_run().await?; - assert!(!get_result.return_value()); - - Ok(()) - } - - /// We test that we can read and write a value from the on-chain contract. - #[ink_e2e::test] - async fn it_works(mut client: Client) -> E2EResult<()> { - // Given - let mut constructor = CustomAllocatorRef::new(false); - let contract = client - .instantiate("custom_allocator", &ink_e2e::bob(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = contract.call_builder::(); - - let get = call_builder.get(); - let get_result = client.call(&ink_e2e::bob(), &get).dry_run().await?; - assert!(!get_result.return_value()); - - // When - let flip = call_builder.flip(); - let _flip_result = client - .call(&ink_e2e::bob(), &flip) - .submit() - .await - .expect("flip failed"); - - // Then - let get = call_builder.get(); - let get_result = client.call(&ink_e2e::bob(), &get).dry_run().await?; - assert!(get_result.return_value()); - - Ok(()) - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/custom-allocator/tests.rs b/integration-tests/public/custom-allocator/tests.rs new file mode 100644 index 0000000000..341f92fef1 --- /dev/null +++ b/integration-tests/public/custom-allocator/tests.rs @@ -0,0 +1,78 @@ +use super::custom_allocator::*; + +#[ink::test] +fn default_works() { + let custom_allocator = CustomAllocator::default(); + assert!(!custom_allocator.get()); +} + +#[ink::test] +fn it_works() { + let mut custom_allocator = CustomAllocator::new(false); + assert!(!custom_allocator.get()); + custom_allocator.flip(); + assert!(custom_allocator.get()); +} + +#[cfg(all(test, feature = "e2e-tests"))] +mod e2e_tests { + use super::*; + use ink_e2e::ContractsBackend; + + type E2EResult = std::result::Result>; + + /// We test that we can upload and instantiate the contract using its default + /// constructor. + #[ink_e2e::test] + async fn default_works(mut client: Client) -> E2EResult<()> { + // Given + let mut constructor = CustomAllocatorRef::default(); + + // When + let contract = client + .instantiate("custom_allocator", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // Then + let get = call_builder.get(); + let get_result = client.call(&ink_e2e::alice(), &get).dry_run().await?; + assert!(!get_result.return_value()); + + Ok(()) + } + + /// We test that we can read and write a value from the on-chain contract. + #[ink_e2e::test] + async fn it_works(mut client: Client) -> E2EResult<()> { + // Given + let mut constructor = CustomAllocatorRef::new(false); + let contract = client + .instantiate("custom_allocator", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = contract.call_builder::(); + + let get = call_builder.get(); + let get_result = client.call(&ink_e2e::bob(), &get).dry_run().await?; + assert!(!get_result.return_value()); + + // When + let flip = call_builder.flip(); + let _flip_result = client + .call(&ink_e2e::bob(), &flip) + .submit() + .await + .expect("flip failed"); + + // Then + let get = call_builder.get(); + let get_result = client.call(&ink_e2e::bob(), &get).dry_run().await?; + assert!(get_result.return_value()); + + Ok(()) + } +} \ No newline at end of file diff --git a/integration-tests/public/custom-environment/lib.rs b/integration-tests/public/custom-environment/lib.rs index 52ad4034e0..03f7dd8615 100644 --- a/integration-tests/public/custom-environment/lib.rs +++ b/integration-tests/public/custom-environment/lib.rs @@ -57,84 +57,7 @@ mod runtime_call { self.env().emit_event(EventWithTopics::default()); } } - - #[cfg(test)] - mod tests { - use super::*; - - #[ink::test] - fn emits_event_with_many_topics() { - let mut contract = Topics::new(); - contract.trigger(); - - let emitted_events = ink::env::test::recorded_events(); - assert_eq!(emitted_events.len(), 1); - - let emitted_event = ::decode( - &mut &emitted_events[0].data[..], - ); - - assert!(emitted_event.is_ok()); - } - } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink_e2e::ContractsBackend; - - type E2EResult = Result>; - - #[cfg(feature = "permissive-node")] - #[ink_e2e::test(environment = crate::EnvironmentWithManyTopics)] - async fn calling_custom_environment_works(mut client: Client) -> E2EResult<()> { - // given - let mut constructor = TopicsRef::new(); - let contract = client - .instantiate("custom-environment", &ink_e2e::alice(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = contract.call_builder::(); - - // when - let message = call_builder.trigger(); - - let call_res = client - .call(&ink_e2e::alice(), &message) - .submit() - .await - .expect("call failed"); - - // then - assert!(call_res.contains_event("Revive", "ContractEmitted")); - - Ok(()) - } - - #[cfg(not(feature = "permissive-node"))] - #[ink_e2e::test(environment = crate::EnvironmentWithManyTopics)] - async fn calling_custom_environment_fails_if_incompatible_with_node( - mut client: Client, - ) -> E2EResult<()> { - // given - let mut constructor = TopicsRef::new(); - let contract = client - .instantiate("custom-environment", &ink_e2e::alice(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = contract.call_builder::(); - - let message = call_builder.trigger(); - - // when - let call_res = client.call(&ink_e2e::alice(), &message).dry_run().await; - - // then - assert!(call_res.is_err()); - - Ok(()) - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/custom-environment/tests.rs b/integration-tests/public/custom-environment/tests.rs new file mode 100644 index 0000000000..e7f158e6c7 --- /dev/null +++ b/integration-tests/public/custom-environment/tests.rs @@ -0,0 +1,77 @@ +use super::runtime_call::{EventWithTopics, Topics, TopicsRef}; +use super::EnvironmentWithManyTopics; + +#[ink::test] +fn emits_event_with_many_topics() { + let mut contract = Topics::new(); + contract.trigger(); + + let emitted_events = ink::env::test::recorded_events(); + assert_eq!(emitted_events.len(), 1); + + let emitted_event = ::decode( + &mut &emitted_events[0].data[..], + ); + + assert!(emitted_event.is_ok()); +} + +#[cfg(all(test, feature = "e2e-tests"))] +mod e2e_tests { + use super::*; + use ink_e2e::ContractsBackend; + + type E2EResult = Result>; + + #[cfg(feature = "permissive-node")] + #[ink_e2e::test(environment = crate::EnvironmentWithManyTopics)] + async fn calling_custom_environment_works(mut client: Client) -> E2EResult<()> { + // given + let mut constructor = TopicsRef::new(); + let contract = client + .instantiate("custom-environment", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = contract.call_builder::(); + + // when + let message = call_builder.trigger(); + + let call_res = client + .call(&ink_e2e::alice(), &message) + .submit() + .await + .expect("call failed"); + + // then + assert!(call_res.contains_event("Revive", "ContractEmitted")); + + Ok(()) + } + + #[cfg(not(feature = "permissive-node"))] + #[ink_e2e::test(environment = crate::EnvironmentWithManyTopics)] + async fn calling_custom_environment_fails_if_incompatible_with_node( + mut client: Client, + ) -> E2EResult<()> { + // given + let mut constructor = TopicsRef::new(); + let contract = client + .instantiate("custom-environment", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = contract.call_builder::(); + + let message = call_builder.trigger(); + + // when + let call_res = client.call(&ink_e2e::alice(), &message).dry_run().await; + + // then + assert!(call_res.is_err()); + + Ok(()) + } +} \ No newline at end of file diff --git a/integration-tests/public/debugging-strategies/lib.rs b/integration-tests/public/debugging-strategies/lib.rs index 7e12f097d3..4b999016c6 100755 --- a/integration-tests/public/debugging-strategies/lib.rs +++ b/integration-tests/public/debugging-strategies/lib.rs @@ -108,206 +108,7 @@ mod debugging_strategies { .unwrap_or_else(|lang_err| panic!("Received a `LangError`: {lang_err:?}")) } } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink::env::Environment; - use ink_e2e::ContractsBackend; - - type E2EResult = std::result::Result>; - - /// This test illustrates how to use debugging events. - /// - /// The contract is build with the `debug` feature enabled, thus - /// we can have code in the contract that is utilized purely - /// for testing, but not for release builds. - #[ink_e2e::test(features = ["debug"])] - async fn e2e_debugging_event_emitted(mut client: Client) -> E2EResult<()> { - // given - let mut constructor = DebuggingStrategiesRef::new(); - let contract = client - .instantiate("debugging_strategies", &ink_e2e::alice(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let call_builder = contract.call_builder::(); - - // when - let call_res = client - .call(&ink_e2e::alice(), &call_builder.get()) - .submit() - .await - .expect("calling `get` message failed"); - - // then - // the contract will have emitted an event - assert!(call_res.contains_event("Revive", "ContractEmitted")); - let contract_events = call_res.contract_emitted_events()?; - assert_eq!(1, contract_events.len()); - let contract_event = &contract_events[0]; - let debug_event: DebugEvent = - ink::scale::Decode::decode(&mut &contract_event.event.data[..]) - .expect("encountered invalid contract event data buffer"); - assert_eq!(debug_event.message, "received 0"); - - Ok(()) - } - - /// This test illustrates how to decode a `Revive::ContractReverted`. - #[ink_e2e::test(features = ["debug"])] - async fn e2e_decode_intentional_revert(mut client: Client) -> E2EResult<()> { - // given - let mut constructor = DebuggingStrategiesRef::new(); - let contract = client - .instantiate("debugging_strategies", &ink_e2e::bob(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let call_builder = contract.call_builder::(); - - // when - let call_res = client - .call(&ink_e2e::alice(), &call_builder.intentional_revert()) - .dry_run() - .await - .expect("calling `get` message failed"); - - let return_data = call_res.return_data(); - assert!(call_res.did_revert()); - let revert_msg = String::from_utf8_lossy(return_data); - assert!(revert_msg.contains("reverting with info: 0")); - - Ok(()) - } - - /// This test illustrates how to decode a `Revive::ContractReverted`. - #[ink_e2e::test] - async fn e2e_decode_revert(mut client: Client) -> E2EResult<()> { - // given - let mut constructor = DebuggingStrategiesRef::new(); - let contract = client - .instantiate("debugging_strategies", &ink_e2e::bob(), &mut constructor) - .value(1_337_000_000) - .dry_run() - //.submit() - .await - .expect("instantiate failed"); - - // when - let return_data = contract.return_data(); - assert!(contract.did_revert()); - let revert_msg = String::from_utf8_lossy(return_data); - assert!(revert_msg.contains("paid an unpayable message")); - - // todo show same for call - let contract = client - .instantiate("debugging_strategies", &ink_e2e::bob(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let call_builder = contract.call_builder::(); - - // when - let call_res = client - .call(&ink_e2e::alice(), &call_builder.get()) - .value(1_337_000_000) - .dry_run() - .await - .expect("calling `get` message failed"); - - let return_data = call_res.return_data(); - assert!(call_res.did_revert()); - let revert_msg = String::from_utf8_lossy(return_data); - assert!( - revert_msg.contains( - "dispatching ink! message failed: paid an unpayable message" - ) - ); - - Ok(()) - } - - /// This test illustrates how to use the `pallet-revive` tracing functionality. - #[ink_e2e::test] - async fn e2e_tracing(mut client: Client) -> E2EResult<()> { - // given - let mut constructor = DebuggingStrategiesRef::new(); - let contract = client - .instantiate("debugging_strategies", &ink_e2e::bob(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = contract.call_builder::(); - - let call = call_builder.instantiate_and_call(contract.code_hash); - let call_res = client - .call(&ink_e2e::alice(), &call) - .value(1_337_000_000) - .submit() - .await?; - - // when - let trace: ink_e2e::CallTrace = call_res.trace.expect("trace must exist"); - assert_eq!(trace.calls.len(), 2); - // This is how the object looks: - // ``` - // CallTrace { - // from: 0x9621dde636de098b43efb0fa9b61facfe328f99d, - // gas: 1497105168000, - // gas_used: 1548337586000, - // to: 0xd71ff7085ed0e3e8b6c8e95eb6094f4311ae8e2f, - // input: Bytes( - // 0x829da98747d85e35d0b3ca3c7ceeac09b63ec2754e6a05eb6d2d5b92fb916da126364dd4, - // ), - // output: Bytes(0x0001), - // error: None, - // revert_reason: None, - // calls: [ - // CallTrace { - // from: 0xd71ff7085ed0e3e8b6c8e95eb6094f4311ae8e2f, - // gas: 711404887000, - // gas_used: 205987649000, - // to: 0xfd8bf44f34a2d2cec42b8ab31ede1bb1bc366e8e, - // input: Bytes(0x9bae9d5e), - // output: Bytes(0x0000), - // error: None, - // revert_reason: None, - // calls: [], - // logs: [], - // value: Some(0), - // call_type: Call, - // }, - // CallTrace { - // from: 0xd71ff7085ed0e3e8b6c8e95eb6094f4311ae8e2f, - // gas: 124370129000, - // gas_used: 163567881000, - // to: 0xfd8bf44f34a2d2cec42b8ab31ede1bb1bc366e8e, - // input: Bytes(0x2f865bd9), - // output: Bytes(0x0001), - // error: None, - // revert_reason: None, - // calls: [], - // logs: [], - // value: Some(0), - // call_type: Call, - // }, - // ], - // logs: [], - // value: Some(0), - // call_type: Call, - // } - // ``` - - // then - assert_eq!( - trace.value, - Some(ink::env::DefaultEnvironment::native_to_eth(1_337_000_000)) - ); - - Ok(()) - } - - // todo add the same above, but for the runtime backend - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/debugging-strategies/tests.rs b/integration-tests/public/debugging-strategies/tests.rs new file mode 100644 index 0000000000..889ebca52d --- /dev/null +++ b/integration-tests/public/debugging-strategies/tests.rs @@ -0,0 +1,157 @@ +use super::debugging_strategies::*; +use ink::env::Environment; +use ink_e2e::ContractsBackend; +#[cfg(feature = "debug")] +use ink::prelude::string::String; + +type E2EResult = std::result::Result>; + +/// This test illustrates how to use debugging events. +/// +/// The contract is build with the `debug` feature enabled, thus +/// we can have code in the contract that is utilized purely +/// for testing, but not for release builds. +#[cfg(feature = "debug")] +#[ink_e2e::test(features = ["debug"])] +async fn e2e_debugging_event_emitted(mut client: Client) -> E2EResult<()> { + // Given + let mut constructor = DebuggingStrategiesRef::new(); + let contract = client + .instantiate("debugging_strategies", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // When + let call_res = client + .call(&ink_e2e::alice(), &call_builder.get()) + .submit() + .await + .expect("calling `get` message failed"); + + // Then + // the contract will have emitted an event + assert!(call_res.contains_event("Revive", "ContractEmitted")); + let contract_events = call_res.contract_emitted_events()?; + assert_eq!(1, contract_events.len()); + let contract_event = &contract_events[0]; + let debug_event: DebugEvent = + ink::scale::Decode::decode(&mut &contract_event.event.data[..]) + .expect("encountered invalid contract event data buffer"); + assert_eq!(debug_event.message, "received 0"); + + Ok(()) +} + +/// This test illustrates how to decode a `Revive::ContractReverted`. +#[cfg(feature = "debug")] +#[ink_e2e::test(features = ["debug"])] +async fn e2e_decode_intentional_revert(mut client: Client) -> E2EResult<()> { + // Given + let mut constructor = DebuggingStrategiesRef::new(); + let contract = client + .instantiate("debugging_strategies", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // When + let call_res = client + .call(&ink_e2e::alice(), &call_builder.intentional_revert()) + .dry_run() + .await + .expect("calling `get` message failed"); + + // Then + let return_data = call_res.return_data(); + assert!(call_res.did_revert()); + let revert_msg = String::from_utf8_lossy(return_data); + assert!(revert_msg.contains("reverting with info: 0")); + + Ok(()) +} + +/// This test illustrates how to decode a `Revive::ContractReverted`. +#[ink_e2e::test] +async fn e2e_decode_revert(mut client: Client) -> E2EResult<()> { + // Given + let mut constructor = DebuggingStrategiesRef::new(); + let contract = client + .instantiate("debugging_strategies", &ink_e2e::bob(), &mut constructor) + .value(1_337_000_000) + .dry_run() + //.submit() + .await + .expect("instantiate failed"); + + // When + let return_data = contract.return_data(); + assert!(contract.did_revert()); + let revert_msg = String::from_utf8_lossy(return_data); + assert!(revert_msg.contains("paid an unpayable message")); + + // todo show same for call + let contract = client + .instantiate("debugging_strategies", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // When + let call_res = client + .call(&ink_e2e::alice(), &call_builder.get()) + .value(1_337_000_000) + .dry_run() + .await + .expect("calling `get` message failed"); + + // Then + let return_data = call_res.return_data(); + assert!(call_res.did_revert()); + let revert_msg = String::from_utf8_lossy(return_data); + assert!( + revert_msg.contains( + "dispatching ink! message failed: paid an unpayable message" + ) + ); + + Ok(()) +} + +/// This test illustrates how to use the `pallet-revive` tracing functionality. +#[ink_e2e::test] +async fn e2e_tracing(mut client: Client) -> E2EResult<()> { + // Given + let mut constructor = DebuggingStrategiesRef::new(); + let contract = client + .instantiate("debugging_strategies", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = contract.call_builder::(); + + let call = call_builder.instantiate_and_call(contract.code_hash); + let call_res = client + .call(&ink_e2e::alice(), &call) + .value(1_337_000_000) + .submit() + .await?; + + // When + let trace: ink_e2e::CallTrace = call_res.trace.expect("trace must exist"); + + // Check that we have 2 calls (one top level, one nested instantiation/call) + assert_eq!(trace.calls.len(), 2); + + // Then + // Verify the value matches what we sent + assert_eq!( + trace.value, + Some(ink::env::DefaultEnvironment::native_to_eth(1_337_000_000)) + ); + + Ok(()) +} \ No newline at end of file diff --git a/integration-tests/public/dns/lib.rs b/integration-tests/public/dns/lib.rs index 164df92fdc..4bd5b0f909 100644 --- a/integration-tests/public/dns/lib.rs +++ b/integration-tests/public/dns/lib.rs @@ -188,77 +188,7 @@ mod dns { fn zero_address() -> Address { [0u8; 20].into() } - - #[cfg(test)] - mod tests { - use super::*; - - fn default_accounts() -> ink::env::test::DefaultAccounts { - ink::env::test::default_accounts() - } - - fn set_next_caller(caller: Address) { - ink::env::test::set_caller(caller); - } - - #[ink::test] - fn register_works() { - let default_accounts = default_accounts(); - let name = H256::from([0x99; 32]); - - set_next_caller(default_accounts.alice); - let mut contract = DomainNameService::new(); - - assert_eq!(contract.register(name), Ok(())); - assert_eq!(contract.register(name), Err(Error::NameAlreadyExists)); - } - - #[ink::test] - fn set_address_works() { - let accounts = default_accounts(); - let name = H256::from([0x99; 32]); - - set_next_caller(accounts.alice); - - let mut contract = DomainNameService::new(); - assert_eq!(contract.register(name), Ok(())); - - // Caller is not owner, `set_address` should fail. - set_next_caller(accounts.bob); - assert_eq!( - contract.set_address(name, accounts.bob), - Err(Error::CallerIsNotOwner) - ); - - // Caller is owner, set_address will be successful - set_next_caller(accounts.alice); - assert_eq!(contract.set_address(name, accounts.bob), Ok(())); - assert_eq!(contract.get_address(name), accounts.bob); - } - - #[ink::test] - fn transfer_works() { - let accounts = default_accounts(); - let name = H256::from([0x99; 32]); - - set_next_caller(accounts.alice); - - let mut contract = DomainNameService::new(); - assert_eq!(contract.register(name), Ok(())); - - // Test transfer of owner. - assert_eq!(contract.transfer(name, accounts.bob), Ok(())); - - // Owner is bob, alice `set_address` should fail. - assert_eq!( - contract.set_address(name, accounts.bob), - Err(Error::CallerIsNotOwner) - ); - - set_next_caller(accounts.bob); - // Now owner is bob, `set_address` should be successful. - assert_eq!(contract.set_address(name, accounts.bob), Ok(())); - assert_eq!(contract.get_address(name), accounts.bob); - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/dns/tests.rs b/integration-tests/public/dns/tests.rs new file mode 100644 index 0000000000..00617d9996 --- /dev/null +++ b/integration-tests/public/dns/tests.rs @@ -0,0 +1,82 @@ +use super::dns::*; +use ink::{H256, primitives::Address}; + +// Helper to get default test accounts +fn default_accounts() -> ink::env::test::DefaultAccounts { + ink::env::test::default_accounts() +} + +// Helper to set the caller for the next contract execution +fn set_next_caller(caller: Address) { + ink::env::test::set_caller(caller); +} + +#[ink::test] +fn register_works() { + // Given + let default_accounts = default_accounts(); + let name = H256::from([0x99; 32]); + + set_next_caller(default_accounts.alice); + let mut contract = DomainNameService::new(); + + // When / Then + // Registering a new name should succeed + assert_eq!(contract.register(name), Ok(())); + + // Trying to register the same name again should fail + assert_eq!(contract.register(name), Err(Error::NameAlreadyExists)); +} + +#[ink::test] +fn set_address_works() { + // Given + let accounts = default_accounts(); + let name = H256::from([0x99; 32]); + + set_next_caller(accounts.alice); + let mut contract = DomainNameService::new(); + assert_eq!(contract.register(name), Ok(())); + + // When / Then + // Caller is not owner (Bob), `set_address` should fail. + set_next_caller(accounts.bob); + assert_eq!( + contract.set_address(name, accounts.bob), + Err(Error::CallerIsNotOwner) + ); + + // Caller is owner (Alice), `set_address` should be successful + set_next_caller(accounts.alice); + assert_eq!(contract.set_address(name, accounts.bob), Ok(())); + assert_eq!(contract.get_address(name), accounts.bob); +} + +#[ink::test] +fn transfer_works() { + // Given + let accounts = default_accounts(); + let name = H256::from([0x99; 32]); + + set_next_caller(accounts.alice); + let mut contract = DomainNameService::new(); + assert_eq!(contract.register(name), Ok(())); + + // When + // Test transfer of owner from Alice to Bob. + assert_eq!(contract.transfer(name, accounts.bob), Ok(())); + + // Then + // Owner is now Bob, so Alice calling `set_address` should fail. + assert_eq!( + contract.set_address(name, accounts.bob), + Err(Error::CallerIsNotOwner) + ); + + // Switch caller to Bob + set_next_caller(accounts.bob); + + // Now owner is Bob, `set_address` should be successful. + assert_eq!(contract.set_address(name, accounts.bob), Ok(())); + assert_eq!(contract.get_address(name), accounts.bob); +} \ No newline at end of file diff --git a/integration-tests/public/e2e-call-runtime/lib.rs b/integration-tests/public/e2e-call-runtime/lib.rs index fa69b8b010..f0b1574228 100644 --- a/integration-tests/public/e2e-call-runtime/lib.rs +++ b/integration-tests/public/e2e-call-runtime/lib.rs @@ -17,83 +17,7 @@ pub mod e2e_call_runtime { self.env().balance() } } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink::env::Environment; - use ink_e2e::{ - ChainBackend, - ContractsBackend, - subxt::dynamic::Value, - }; - use static_assertions::assert_type_eq_all; - - type E2EResult = std::result::Result>; - - #[ink_e2e::test] - async fn call_runtime_works(mut client: Client) -> E2EResult<()> { - // given - let mut constructor = ContractRef::new(); - let contract = client - .instantiate("e2e_call_runtime", &ink_e2e::alice(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - - let account_id = client.to_account_id(&contract.addr).await?; - assert_eq!(account_id, contract.account_id); - - let mut call_builder = contract.call_builder::(); - - // The generic `Environment::Balance` type must be `u128` - // for this test to work. This is because we encode `Value::u128` - // in the `call_data`. - assert_type_eq_all!(Balance, u128); - let transfer_amount: u128 = 100_000_000_000; - - // when - let call_data = vec![ - // A value representing a `MultiAddress`. We want the - // "Id" variant, and that will ultimately contain the - // bytes for our destination address - Value::unnamed_variant("Id", [Value::from_bytes(contract.account_id)]), - // A value representing the amount we'd like to transfer. - Value::u128(transfer_amount), - ]; - - let get_balance = call_builder.get_contract_balance(); - let pre_balance = client - .call(&ink_e2e::alice(), &get_balance) - .dry_run() - .await? - .return_value(); - - // Send funds from Alice to the contract using Balances::transfer - client - .runtime_call( - &ink_e2e::alice(), - "Balances", - "transfer_allow_death", - call_data, - ) - .await - .expect("runtime call failed"); - - // then - let get_balance = call_builder.get_contract_balance(); - let get_balance_res = client - .call(&ink_e2e::alice(), &get_balance) - .dry_run() - .await?; - - assert_eq!( - get_balance_res.return_value(), - pre_balance - + ink::env::DefaultEnvironment::native_to_eth(transfer_amount) - ); - - Ok(()) - } - } } + +#[cfg(all(test, feature = "e2e-tests"))] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/e2e-call-runtime/tests.rs b/integration-tests/public/e2e-call-runtime/tests.rs new file mode 100644 index 0000000000..51e65ffcde --- /dev/null +++ b/integration-tests/public/e2e-call-runtime/tests.rs @@ -0,0 +1,75 @@ +use crate::e2e_call_runtime::{Contract, ContractRef}; +use ink::env::Environment; +use ink_e2e::{ + ChainBackend, + ContractsBackend, + subxt::dynamic::Value, +}; +use static_assertions::assert_type_eq_all; + +type E2EResult = std::result::Result>; + +#[ink_e2e::test] +async fn call_runtime_works(mut client: Client) -> E2EResult<()> { + // given + let mut constructor = ContractRef::new(); + let contract = client + .instantiate("e2e_call_runtime", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + + let account_id = client.to_account_id(&contract.addr).await?; + assert_eq!(account_id, contract.account_id); + + let mut call_builder = contract.call_builder::(); + + // The generic `Environment::Balance` type must be `u128` + // for this test to work. This is because we encode `Value::u128` + // in the `call_data`. + assert_type_eq_all!(Balance, u128); + let transfer_amount: u128 = 100_000_000_000; + + // when + let call_data = vec![ + // A value representing a `MultiAddress`. We want the + // "Id" variant, and that will ultimately contain the + // bytes for our destination address + Value::unnamed_variant("Id", [Value::from_bytes(contract.account_id)]), + // A value representing the amount we'd like to transfer. + Value::u128(transfer_amount), + ]; + + let get_balance = call_builder.get_contract_balance(); + let pre_balance = client + .call(&ink_e2e::alice(), &get_balance) + .dry_run() + .await? + .return_value(); + + // Send funds from Alice to the contract using Balances::transfer + client + .runtime_call( + &ink_e2e::alice(), + "Balances", + "transfer_allow_death", + call_data, + ) + .await + .expect("runtime call failed"); + + // then + let get_balance = call_builder.get_contract_balance(); + let get_balance_res = client + .call(&ink_e2e::alice(), &get_balance) + .dry_run() + .await?; + + assert_eq!( + get_balance_res.return_value(), + pre_balance + + ink::env::DefaultEnvironment::native_to_eth(transfer_amount) + ); + + Ok(()) +} \ No newline at end of file From 98a10c91138f665ed258ca884a99f7ae620fe50f Mon Sep 17 00:00:00 2001 From: Alhibb <63309522+Alhibb@users.noreply.github.com> Date: Fri, 5 Dec 2025 11:31:01 +0100 Subject: [PATCH 3/5] Test refcactored --- integration-tests/public/erc1155/lib.rs | 216 +---------- integration-tests/public/erc1155/tests.rs | 247 ++++++++++++ integration-tests/public/erc20/lib.rs | 450 +--------------------- integration-tests/public/erc20/tests.rs | 447 +++++++++++++++++++++ integration-tests/public/erc721/lib.rs | 311 +-------------- integration-tests/public/erc721/tests.rs | 301 +++++++++++++++ 6 files changed, 1007 insertions(+), 965 deletions(-) create mode 100644 integration-tests/public/erc1155/tests.rs create mode 100644 integration-tests/public/erc20/tests.rs create mode 100644 integration-tests/public/erc721/tests.rs diff --git a/integration-tests/public/erc1155/lib.rs b/integration-tests/public/erc1155/lib.rs index 744422331a..57007de15f 100644 --- a/integration-tests/public/erc1155/lib.rs +++ b/integration-tests/public/erc1155/lib.rs @@ -188,7 +188,7 @@ pub trait Erc1155TokenReceiver { } #[ink::contract] -mod erc1155 { +pub mod erc1155 { use super::*; use ink::{ @@ -630,215 +630,7 @@ mod erc1155 { fn zero_address() -> Address { [0u8; 20].into() } - - #[cfg(test)] - mod tests { - /// Imports all the definitions from the outer scope so we can use them here. - use super::*; - use crate::Erc1155; - - fn set_sender(sender: Address) { - ink::env::test::set_caller(sender); - } - - fn default_accounts() -> ink::env::test::DefaultAccounts { - ink::env::test::default_accounts() - } - - fn alice() -> Address { - default_accounts().alice - } - - fn bob() -> Address { - default_accounts().bob - } - - fn charlie() -> Address { - default_accounts().charlie - } - - fn init_contract() -> Contract { - set_sender(alice()); - let mut erc = Contract::new(); - erc.balances.insert((alice(), 1), &U256::from(10)); - erc.balances.insert((alice(), 2), &U256::from(20)); - erc.balances.insert((bob(), 1), &U256::from(10)); - - erc - } - - #[ink::test] - fn can_get_correct_balance_of() { - let erc = init_contract(); - - assert_eq!(erc.balance_of(alice(), 1), U256::from(10)); - assert_eq!(erc.balance_of(alice(), 2), U256::from(20)); - assert_eq!(erc.balance_of(alice(), 3), U256::zero()); - assert_eq!(erc.balance_of(bob(), 2), U256::zero()); - } - - #[ink::test] - fn can_get_correct_batch_balance_of() { - let erc = init_contract(); - - assert_eq!( - erc.balance_of_batch(vec![alice()], vec![1, 2, 3]), - vec![U256::from(10), 20.into(), 0.into()] - ); - assert_eq!( - erc.balance_of_batch(vec![alice(), bob()], vec![1]), - vec![U256::from(10), 10.into()] - ); - - assert_eq!( - erc.balance_of_batch(vec![alice(), bob(), charlie()], vec![1, 2]), - vec![ - U256::from(10), - 20.into(), - 10.into(), - 0.into(), - 0.into(), - 0.into() - ] - ); - } - - #[ink::test] - fn can_send_tokens_between_accounts() { - let mut erc = init_contract(); - - assert!( - erc.safe_transfer_from(alice(), bob(), 1, 5.into(), vec![]) - .is_ok() - ); - assert_eq!(erc.balance_of(alice(), 1), U256::from(5)); - assert_eq!(erc.balance_of(bob(), 1), U256::from(15)); - - assert!( - erc.safe_transfer_from(alice(), bob(), 2, 5.into(), vec![]) - .is_ok() - ); - assert_eq!(erc.balance_of(alice(), 2), U256::from(15)); - assert_eq!(erc.balance_of(bob(), 2), U256::from(5)); - } - - #[ink::test] - fn sending_too_many_tokens_fails() { - let mut erc = init_contract(); - let res = erc.safe_transfer_from(alice(), bob(), 1, 99.into(), vec![]); - assert_eq!(res.unwrap_err(), Error::InsufficientU256); - } - - #[ink::test] - fn sending_tokens_to_zero_address_fails() { - let burn: Address = [0; 20].into(); - - let mut erc = init_contract(); - let res = erc.safe_transfer_from(alice(), burn, 1, 10.into(), vec![]); - assert_eq!(res.unwrap_err(), Error::ZeroAddressTransfer); - } - - #[ink::test] - fn can_send_batch_tokens() { - let mut erc = init_contract(); - assert!( - erc.safe_batch_transfer_from( - alice(), - bob(), - vec![1, 2], - vec![U256::from(5), U256::from(10)], - vec![] - ) - .is_ok() - ); - - let balances = erc.balance_of_batch(vec![alice(), bob()], vec![1, 2]); - assert_eq!( - balances, - vec![U256::from(5), 10.into(), 15.into(), 10.into()] - ); - } - - #[ink::test] - fn rejects_batch_if_lengths_dont_match() { - let mut erc = init_contract(); - let res = erc.safe_batch_transfer_from( - alice(), - bob(), - vec![1, 2, 3], - vec![U256::from(5)], - vec![], - ); - assert_eq!(res.unwrap_err(), Error::BatchTransferMismatch); - } - - #[ink::test] - fn batch_transfers_fail_if_len_is_zero() { - let mut erc = init_contract(); - let res = - erc.safe_batch_transfer_from(alice(), bob(), vec![], vec![], vec![]); - assert_eq!(res.unwrap_err(), Error::BatchTransferMismatch); - } - - #[ink::test] - fn operator_can_send_tokens() { - let mut erc = init_contract(); - - let owner = alice(); - let operator = bob(); - - set_sender(owner); - assert!(erc.set_approval_for_all(operator, true).is_ok()); - - set_sender(operator); - assert!( - erc.safe_transfer_from(owner, charlie(), 1, 5.into(), vec![]) - .is_ok() - ); - assert_eq!(erc.balance_of(alice(), 1), U256::from(5)); - assert_eq!(erc.balance_of(charlie(), 1), U256::from(5)); - } - - #[ink::test] - fn approvals_work() { - let mut erc = init_contract(); - let owner = alice(); - let operator = bob(); - let another_operator = charlie(); - - // Note: All of these tests are from the context of the owner who is either - // allowing or disallowing an operator to control their funds. - set_sender(owner); - assert!(!erc.is_approved_for_all(owner, operator)); - - assert!(erc.set_approval_for_all(operator, true).is_ok()); - assert!(erc.is_approved_for_all(owner, operator)); - - assert!(erc.set_approval_for_all(another_operator, true).is_ok()); - assert!(erc.is_approved_for_all(owner, another_operator)); - - assert!(erc.set_approval_for_all(operator, false).is_ok()); - assert!(!erc.is_approved_for_all(owner, operator)); - } - - #[ink::test] - fn minting_tokens_works() { - let mut erc = Contract::new(); - - set_sender(alice()); - assert_eq!(erc.create(0.into()), 1); - assert_eq!(erc.balance_of(alice(), 1), U256::zero()); - - assert!(erc.mint(1, 123.into()).is_ok()); - assert_eq!(erc.balance_of(alice(), 1), U256::from(123)); - } - - #[ink::test] - fn minting_not_allowed_for_nonexistent_tokens() { - let mut erc = Contract::new(); - - let res = erc.mint(1, 123.into()); - assert_eq!(res.unwrap_err(), Error::UnexistentToken); - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/erc1155/tests.rs b/integration-tests/public/erc1155/tests.rs new file mode 100644 index 0000000000..2a56ae2d92 --- /dev/null +++ b/integration-tests/public/erc1155/tests.rs @@ -0,0 +1,247 @@ +use crate::erc1155::*; +use crate::{Erc1155, Error}; +use ink::{Address, U256}; + +fn set_sender(sender: Address) { + ink::env::test::set_caller(sender); +} + +fn default_accounts() -> ink::env::test::DefaultAccounts { + ink::env::test::default_accounts() +} + +fn alice() -> Address { + default_accounts().alice +} + +fn bob() -> Address { + default_accounts().bob +} + +fn charlie() -> Address { + default_accounts().charlie +} + +fn init_contract() -> Contract { + set_sender(alice()); + let mut erc = Contract::new(); + // We can access internal storage directly for setup because the test module + // is part of the crate hierarchy, though typically fields should be pub + // or accessed via methods. In ink! storage structs are usually generated + // with pub fields by the macro or we are checking behavior via public API. + // + // Note: In the refactor, we are using the public API where possible, + // but here we are manually inserting into storage which requires visibility. + // Ensure Contract fields are pub or accessible if this fails, but + // usually #[ink::contract] makes storage fields private by default unless + // explicitly pub. In the original code, they were inside the module. + // Since we moved tests out, we might need to rely on public methods + // like `create` or `mint` to set up state, OR make Contract fields pub. + // + // However, looking at the provided lib.rs, `balances` is NOT pub. + // We should use `mint` or `create` to set up state instead of manipulating + // private storage, which is a better testing practice anyway. + + // Let's use public API to replicate the setup: + // Original: + // erc.balances.insert((alice(), 1), &U256::from(10)); + // erc.balances.insert((alice(), 2), &U256::from(20)); + // erc.balances.insert((bob(), 1), &U256::from(10)); + + // New way using public API: + let _ = erc.create(10.into()); // Token ID 1 for Alice + let _ = erc.create(20.into()); // Token ID 2 for Alice + + // Setup Bob's balance: Alice transfers to Bob, or we temporarily switch caller + // Since create/mint assigns to caller. + set_sender(bob()); + // Bob needs Token ID 1. But Token ID 1 is already created by Alice. + // Bob can mint more if logic allows, or Alice transfers. + // The `mint` function allows minting existing tokens. + // But `mint` checks if token exists. Token 1 exists (nonce is incremented). + let _ = erc.mint(1, 10.into()); // Bob mints 10 of Token ID 1 + + // Reset sender to Alice + set_sender(alice()); + + erc +} + +#[ink::test] +fn can_get_correct_balance_of() { + let erc = init_contract(); + + assert_eq!(erc.balance_of(alice(), 1), U256::from(10)); + assert_eq!(erc.balance_of(alice(), 2), U256::from(20)); + assert_eq!(erc.balance_of(alice(), 3), U256::zero()); + assert_eq!(erc.balance_of(bob(), 1), U256::from(10)); // Bob has 10 of ID 1 + assert_eq!(erc.balance_of(bob(), 2), U256::zero()); +} + +#[ink::test] +fn can_get_correct_batch_balance_of() { + let erc = init_contract(); + + assert_eq!( + erc.balance_of_batch(vec![alice()], vec![1, 2, 3]), + vec![U256::from(10), 20.into(), 0.into()] + ); + // Modified expectation: Bob has 10 of Token 1 from init_contract() + assert_eq!( + erc.balance_of_batch(vec![alice(), bob()], vec![1]), + vec![U256::from(10), 10.into()] + ); + + assert_eq!( + erc.balance_of_batch(vec![alice(), bob(), charlie()], vec![1, 2]), + vec![ + U256::from(10), + 20.into(), + 10.into(), // Bob has 10 of Token 1 + 0.into(), + 0.into(), + 0.into() + ] + ); +} + +#[ink::test] +fn can_send_tokens_between_accounts() { + let mut erc = init_contract(); + + assert!( + erc.safe_transfer_from(alice(), bob(), 1, 5.into(), vec![]) + .is_ok() + ); + assert_eq!(erc.balance_of(alice(), 1), U256::from(5)); + // Bob started with 10, got 5 more + assert_eq!(erc.balance_of(bob(), 1), U256::from(15)); + + assert!( + erc.safe_transfer_from(alice(), bob(), 2, 5.into(), vec![]) + .is_ok() + ); + assert_eq!(erc.balance_of(alice(), 2), U256::from(15)); + assert_eq!(erc.balance_of(bob(), 2), U256::from(5)); +} + +#[ink::test] +fn sending_too_many_tokens_fails() { + let mut erc = init_contract(); + let res = erc.safe_transfer_from(alice(), bob(), 1, 99.into(), vec![]); + assert_eq!(res.unwrap_err(), Error::InsufficientU256); +} + +#[ink::test] +fn sending_tokens_to_zero_address_fails() { + let burn: Address = [0; 20].into(); + + let mut erc = init_contract(); + let res = erc.safe_transfer_from(alice(), burn, 1, 10.into(), vec![]); + assert_eq!(res.unwrap_err(), Error::ZeroAddressTransfer); +} + +#[ink::test] +fn can_send_batch_tokens() { + let mut erc = init_contract(); + assert!( + erc.safe_batch_transfer_from( + alice(), + bob(), + vec![1, 2], + vec![U256::from(5), U256::from(10)], + vec![] + ) + .is_ok() + ); + + let balances = erc.balance_of_batch(vec![alice(), bob()], vec![1, 2]); + assert_eq!( + balances, + // Alice: 10-5=5, 20-10=10 + // Bob: 10+5=15, 0+10=10 + vec![U256::from(5), 10.into(), 15.into(), 10.into()] + ); +} + +#[ink::test] +fn rejects_batch_if_lengths_dont_match() { + let mut erc = init_contract(); + let res = erc.safe_batch_transfer_from( + alice(), + bob(), + vec![1, 2, 3], + vec![U256::from(5)], + vec![], + ); + assert_eq!(res.unwrap_err(), Error::BatchTransferMismatch); +} + +#[ink::test] +fn batch_transfers_fail_if_len_is_zero() { + let mut erc = init_contract(); + let res = + erc.safe_batch_transfer_from(alice(), bob(), vec![], vec![], vec![]); + assert_eq!(res.unwrap_err(), Error::BatchTransferMismatch); +} + +#[ink::test] +fn operator_can_send_tokens() { + let mut erc = init_contract(); + + let owner = alice(); + let operator = bob(); + + set_sender(owner); + assert!(erc.set_approval_for_all(operator, true).is_ok()); + + set_sender(operator); + assert!( + erc.safe_transfer_from(owner, charlie(), 1, 5.into(), vec![]) + .is_ok() + ); + assert_eq!(erc.balance_of(alice(), 1), U256::from(5)); + assert_eq!(erc.balance_of(charlie(), 1), U256::from(5)); +} + +#[ink::test] +fn approvals_work() { + let mut erc = init_contract(); + let owner = alice(); + let operator = bob(); + let another_operator = charlie(); + + // Note: All of these tests are from the context of the owner who is either + // allowing or disallowing an operator to control their funds. + set_sender(owner); + assert!(!erc.is_approved_for_all(owner, operator)); + + assert!(erc.set_approval_for_all(operator, true).is_ok()); + assert!(erc.is_approved_for_all(owner, operator)); + + assert!(erc.set_approval_for_all(another_operator, true).is_ok()); + assert!(erc.is_approved_for_all(owner, another_operator)); + + assert!(erc.set_approval_for_all(operator, false).is_ok()); + assert!(!erc.is_approved_for_all(owner, operator)); +} + +#[ink::test] +fn minting_tokens_works() { + let mut erc = Contract::new(); + + set_sender(alice()); + assert_eq!(erc.create(0.into()), 1); + assert_eq!(erc.balance_of(alice(), 1), U256::zero()); + + assert!(erc.mint(1, 123.into()).is_ok()); + assert_eq!(erc.balance_of(alice(), 1), U256::from(123)); +} + +#[ink::test] +fn minting_not_allowed_for_nonexistent_tokens() { + let mut erc = Contract::new(); + + let res = erc.mint(1, 123.into()); + assert_eq!(res.unwrap_err(), Error::UnexistentToken); +} \ No newline at end of file diff --git a/integration-tests/public/erc20/lib.rs b/integration-tests/public/erc20/lib.rs index fd41432384..e2d20c6ff4 100644 --- a/integration-tests/public/erc20/lib.rs +++ b/integration-tests/public/erc20/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] #[ink::contract] -mod erc20 { +pub mod erc20 { use ink::{ U256, storage::Mapping, @@ -220,449 +220,7 @@ mod erc20 { Ok(()) } } - - #[cfg(test)] - fn set_caller(sender: Address) { - ink::env::test::set_caller(sender); - } - - #[cfg(test)] - mod tests { - use super::*; - - use ink::primitives::{ - Clear, - Hash, - }; - - fn assert_transfer_event( - event: &ink::env::test::EmittedEvent, - expected_from: Option
, - expected_to: Option
, - expected_value: U256, - ) { - let decoded_event = - ::decode(&mut &event.data[..]) - .expect("encountered invalid contract event data buffer"); - let Transfer { from, to, value } = decoded_event; - assert_eq!(from, expected_from, "encountered invalid Transfer.from"); - assert_eq!(to, expected_to, "encountered invalid Transfer.to"); - assert_eq!(value, expected_value, "encountered invalid Transfer.value"); - - let mut expected_topics = Vec::new(); - expected_topics.push( - ink::blake2x256!("Transfer(Option
,Option
,U256)").into(), - ); - if let Some(from) = expected_from { - expected_topics.push(encoded_into_hash(from)); - } else { - expected_topics.push(Hash::CLEAR_HASH); - } - if let Some(to) = expected_to { - expected_topics.push(encoded_into_hash(to)); - } else { - expected_topics.push(Hash::CLEAR_HASH); - } - expected_topics.push(encoded_into_hash(value)); - - let topics = event.topics.clone(); - for (n, (actual_topic, expected_topic)) in - topics.iter().zip(expected_topics).enumerate() - { - let mut topic_hash = Hash::CLEAR_HASH; - let len = actual_topic.len(); - topic_hash.as_mut()[0..len].copy_from_slice(&actual_topic[0..len]); - - assert_eq!( - topic_hash, expected_topic, - "encountered invalid topic at {n}" - ); - } - } - - /// The default constructor does its job. - #[ink::test] - fn new_works() { - // Constructor works. - set_caller(Address::from([0x01; 20])); - let _erc20 = Erc20::new(100.into()); - - // Transfer event triggered during initial construction. - let emitted_events = ink::env::test::recorded_events(); - assert_eq!(1, emitted_events.len()); - - assert_transfer_event( - &emitted_events[0], - None, - Some(Address::from([0x01; 20])), - 100.into(), - ); - } - - /// The total supply was applied. - #[ink::test] - fn total_supply_works() { - // Constructor works. - set_caller(Address::from([0x01; 20])); - let erc20 = Erc20::new(100.into()); - // Transfer event triggered during initial construction. - let emitted_events = ink::env::test::recorded_events(); - assert_transfer_event( - &emitted_events[0], - None, - Some(Address::from([0x01; 20])), - 100.into(), - ); - // Get the token total supply. - assert_eq!(erc20.total_supply(), U256::from(100)); - } - - /// Get the actual balance of an account. - #[ink::test] - fn balance_of_works() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - - // Constructor works - let erc20 = Erc20::new(100.into()); - // Transfer event triggered during initial construction - let emitted_events = ink::env::test::recorded_events(); - assert_transfer_event( - &emitted_events[0], - None, - Some(accounts.alice), - 100.into(), - ); - let accounts = ink::env::test::default_accounts(); - // Alice owns all the tokens on contract instantiation - assert_eq!(erc20.balance_of(accounts.alice), U256::from(100)); - // Bob does not owns tokens - assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); - } - - #[ink::test] - fn transfer_works() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - - // Constructor works. - let mut erc20 = Erc20::new(100.into()); - // Transfer event triggered during initial construction. - assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); - // Alice transfers 10 tokens to Bob. - assert_eq!(erc20.transfer(accounts.bob, U256::from(10)), Ok(())); - // Bob owns 10 tokens. - assert_eq!(erc20.balance_of(accounts.bob), U256::from(10)); - - let emitted_events = ink::env::test::recorded_events(); - assert_eq!(emitted_events.len(), 2); - // Check first transfer event related to ERC-20 instantiation. - assert_transfer_event( - &emitted_events[0], - None, - Some(accounts.alice), - 100.into(), - ); - // Check the second transfer event relating to the actual transfer. - assert_transfer_event( - &emitted_events[1], - Some(accounts.alice), - Some(accounts.bob), - 10.into(), - ); - } - - #[ink::test] - fn invalid_transfer_should_fail() { - // Constructor works. - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - - let initial_supply = 100.into(); - let mut erc20 = Erc20::new(initial_supply); - - assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); - - // Set the contract as callee and Bob as caller. - let contract = ink::env::address(); - ink::env::test::set_callee(contract); - set_caller(accounts.bob); - - // Bob fails to transfer 10 tokens to Eve. - assert_eq!( - erc20.transfer(accounts.eve, 10.into()), - Err(Error::InsufficientBalance) - ); - // Alice owns all the tokens. - assert_eq!(erc20.balance_of(accounts.alice), U256::from(100)); - assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); - assert_eq!(erc20.balance_of(accounts.eve), U256::zero()); - - // Transfer event triggered during initial construction. - let emitted_events = ink::env::test::recorded_events(); - assert_eq!(emitted_events.len(), 1); - assert_transfer_event( - &emitted_events[0], - None, - Some(accounts.alice), - 100.into(), - ); - } - - #[ink::test] - fn transfer_from_works() { - // Constructor works. - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - - let mut erc20 = Erc20::new(100.into()); - - // Bob fails to transfer tokens owned by Alice. - assert_eq!( - erc20.transfer_from(accounts.alice, accounts.eve, 10.into()), - Err(Error::InsufficientAllowance) - ); - // Alice approves Bob for token transfers on her behalf. - assert_eq!(erc20.approve(accounts.bob, 10.into()), Ok(())); - - // The approve event takes place. - assert_eq!(ink::env::test::recorded_events().len(), 2); - - // Set the contract as callee and Bob as caller. - let contract = ink::env::address(); - ink::env::test::set_callee(contract); - ink::env::test::set_caller(accounts.bob); - - // Bob transfers tokens from Alice to Eve. - assert_eq!( - erc20.transfer_from(accounts.alice, accounts.eve, 10.into()), - Ok(()) - ); - // Eve owns tokens. - assert_eq!(erc20.balance_of(accounts.eve), U256::from(10)); - - // Check all transfer events that happened during the previous calls: - let emitted_events = ink::env::test::recorded_events(); - assert_eq!(emitted_events.len(), 3); - assert_transfer_event( - &emitted_events[0], - None, - Some(accounts.alice), - 100.into(), - ); - // The second event `emitted_events[1]` is an Approve event that we skip - // checking. - assert_transfer_event( - &emitted_events[2], - Some(accounts.alice), - Some(accounts.eve), - 10.into(), - ); - } - - #[ink::test] - fn allowance_must_not_change_on_failed_transfer() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - let mut erc20 = Erc20::new(100.into()); - - // Alice approves Bob for token transfers on her behalf. - let alice_balance = erc20.balance_of(accounts.alice); - let initial_allowance = alice_balance + 2; - assert_eq!(erc20.approve(accounts.bob, initial_allowance), Ok(())); - - // Get contract address. - let callee = ink::env::address(); - ink::env::test::set_callee(callee); - ink::env::test::set_caller(accounts.bob); - - // Bob tries to transfer tokens from Alice to Eve. - let emitted_events_before = ink::env::test::recorded_events().len(); - assert_eq!( - erc20.transfer_from( - accounts.alice, - accounts.eve, - alice_balance + U256::from(1) - ), - Err(Error::InsufficientBalance) - ); - // Allowance must have stayed the same - assert_eq!( - erc20.allowance(accounts.alice, accounts.bob), - initial_allowance - ); - // No more events must have been emitted - assert_eq!( - emitted_events_before, - ink::env::test::recorded_events().len() - ) - } - - fn encoded_into_hash(entity: T) -> Hash - where - T: ink::scale::Encode, - { - use ink::{ - env::hash::{ - Blake2x256, - CryptoHash, - HashOutput, - }, - primitives::Clear, - }; - - let mut result = Hash::CLEAR_HASH; - let len_result = result.as_ref().len(); - let encoded = entity.encode(); - let len_encoded = encoded.len(); - if len_encoded <= len_result { - result.as_mut()[..len_encoded].copy_from_slice(&encoded); - return result - } - let mut hash_output = - <::Type as Default>::default(); - ::hash(&encoded, &mut hash_output); - let copy_len = core::cmp::min(hash_output.len(), len_result); - result.as_mut()[0..copy_len].copy_from_slice(&hash_output[0..copy_len]); - result - } - } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink_e2e::ContractsBackend; - - type E2EResult = std::result::Result>; - - #[ink_e2e::test] - async fn e2e_transfer(mut client: Client) -> E2EResult<()> { - // given - let total_supply = U256::from(1_000_000_000); - let mut constructor = Erc20Ref::new(total_supply); - let erc20 = client - .instantiate("erc20", &ink_e2e::alice(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = erc20.call_builder::(); - - // when - let total_supply_msg = call_builder.total_supply(); - let total_supply_res = client - .call(&ink_e2e::bob(), &total_supply_msg) - .dry_run() - .await?; - - let bob_account = ink_e2e::address::( - ink_e2e::Sr25519Keyring::Bob, - ); - let transfer_to_bob = U256::from(500_000_000); - let transfer = call_builder.transfer(bob_account, transfer_to_bob); - let _transfer_res = client - .call(&ink_e2e::alice(), &transfer) - .submit() - .await - .expect("transfer failed"); - - let balance_of = call_builder.balance_of(bob_account); - let balance_of_res = client - .call(&ink_e2e::alice(), &balance_of) - .dry_run() - .await?; - - // then - assert_eq!( - total_supply, - total_supply_res.return_value(), - "total_supply" - ); - assert_eq!(transfer_to_bob, balance_of_res.return_value(), "balance_of"); - - Ok(()) - } - - #[ink_e2e::test] - async fn e2e_allowances(mut client: Client) -> E2EResult<()> { - // given - let total_supply = U256::from(1_000_000_000); - let mut constructor = Erc20Ref::new(total_supply); - let erc20 = client - .instantiate("erc20", &ink_e2e::bob(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = erc20.call_builder::(); - - // when - - let bob_account = ink_e2e::address::( - ink_e2e::Sr25519Keyring::Bob, - ); - let charlie_account = ink_e2e::address::( - ink_e2e::Sr25519Keyring::Charlie, - ); - - let amount = U256::from(500_000_000); - // tx - let transfer_from = - call_builder.transfer_from(bob_account, charlie_account, amount); - let transfer_from_result = client - .call(&ink_e2e::charlie(), &transfer_from) - .submit() - .await; - - assert!( - transfer_from_result.is_err(), - "unapproved transfer_from should fail" - ); - - // Bob approves Charlie to transfer up to amount on his behalf - let approved_value = U256::from(1_000); - let approve_call = call_builder.approve(charlie_account, approved_value); - client - .call(&ink_e2e::bob(), &approve_call) - .submit() - .await - .expect("approve failed"); - - // `transfer_from` the approved amount - let transfer_from = - call_builder.transfer_from(bob_account, charlie_account, approved_value); - let transfer_from_result = client - .call(&ink_e2e::charlie(), &transfer_from) - .submit() - .await; - assert!( - transfer_from_result.is_ok(), - "approved transfer_from should succeed" - ); - - let balance_of = call_builder.balance_of(bob_account); - let balance_of_res = client - .call(&ink_e2e::alice(), &balance_of) - .dry_run() - .await?; - - // `transfer_from` again, this time exceeding the approved amount - let transfer_from = - call_builder.transfer_from(bob_account, charlie_account, 1.into()); - let transfer_from_result = client - .call(&ink_e2e::charlie(), &transfer_from) - .submit() - .await; - assert!( - transfer_from_result.is_err(), - "transfer_from exceeding the approved amount should fail" - ); - - assert_eq!( - total_supply - approved_value, - balance_of_res.return_value(), - "balance_of" - ); - - Ok(()) - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/erc20/tests.rs b/integration-tests/public/erc20/tests.rs new file mode 100644 index 0000000000..92de1f4cff --- /dev/null +++ b/integration-tests/public/erc20/tests.rs @@ -0,0 +1,447 @@ +use crate::erc20::*; +use ink::primitives::{ + Address, + Clear, + Hash, +}; +use ink::U256; + +// --- Helper Functions for Unit Tests --- + +fn set_caller(sender: Address) { + ink::env::test::set_caller(sender); +} + +fn encoded_into_hash(entity: T) -> Hash +where + T: ink::scale::Encode, +{ + use ink::{ + env::hash::{ + Blake2x256, + CryptoHash, + HashOutput, + }, + primitives::Clear, + }; + + let mut result = Hash::CLEAR_HASH; + let len_result = result.as_ref().len(); + let encoded = entity.encode(); + let len_encoded = encoded.len(); + if len_encoded <= len_result { + result.as_mut()[..len_encoded].copy_from_slice(&encoded); + return result + } + let mut hash_output = + <::Type as Default>::default(); + ::hash(&encoded, &mut hash_output); + let copy_len = core::cmp::min(hash_output.len(), len_result); + result.as_mut()[0..copy_len].copy_from_slice(&hash_output[0..copy_len]); + result +} + +fn assert_transfer_event( + event: &ink::env::test::EmittedEvent, + expected_from: Option
, + expected_to: Option
, + expected_value: U256, +) { + let decoded_event = + ::decode(&mut &event.data[..]) + .expect("encountered invalid contract event data buffer"); + let Transfer { from, to, value } = decoded_event; + assert_eq!(from, expected_from, "encountered invalid Transfer.from"); + assert_eq!(to, expected_to, "encountered invalid Transfer.to"); + assert_eq!(value, expected_value, "encountered invalid Transfer.value"); + + let mut expected_topics = Vec::new(); + expected_topics.push( + ink::blake2x256!("Transfer(Option
,Option
,U256)").into(), + ); + if let Some(from) = expected_from { + expected_topics.push(encoded_into_hash(from)); + } else { + expected_topics.push(Hash::CLEAR_HASH); + } + if let Some(to) = expected_to { + expected_topics.push(encoded_into_hash(to)); + } else { + expected_topics.push(Hash::CLEAR_HASH); + } + expected_topics.push(encoded_into_hash(value)); + + let topics = event.topics.clone(); + for (n, (actual_topic, expected_topic)) in + topics.iter().zip(expected_topics).enumerate() + { + let mut topic_hash = Hash::CLEAR_HASH; + let len = actual_topic.len(); + topic_hash.as_mut()[0..len].copy_from_slice(&actual_topic[0..len]); + + assert_eq!( + topic_hash, expected_topic, + "encountered invalid topic at {n}" + ); + } +} + +// --- Unit Tests --- + +/// The default constructor does its job. +#[ink::test] +fn new_works() { + // Constructor works. + set_caller(Address::from([0x01; 20])); + let _erc20 = Erc20::new(100.into()); + + // Transfer event triggered during initial construction. + let emitted_events = ink::env::test::recorded_events(); + assert_eq!(1, emitted_events.len()); + + assert_transfer_event( + &emitted_events[0], + None, + Some(Address::from([0x01; 20])), + 100.into(), + ); +} + +/// The total supply was applied. +#[ink::test] +fn total_supply_works() { + // Constructor works. + set_caller(Address::from([0x01; 20])); + let erc20 = Erc20::new(100.into()); + // Transfer event triggered during initial construction. + let emitted_events = ink::env::test::recorded_events(); + assert_transfer_event( + &emitted_events[0], + None, + Some(Address::from([0x01; 20])), + 100.into(), + ); + // Get the token total supply. + assert_eq!(erc20.total_supply(), U256::from(100)); +} + +/// Get the actual balance of an account. +#[ink::test] +fn balance_of_works() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + + // Constructor works + let erc20 = Erc20::new(100.into()); + // Transfer event triggered during initial construction + let emitted_events = ink::env::test::recorded_events(); + assert_transfer_event( + &emitted_events[0], + None, + Some(accounts.alice), + 100.into(), + ); + let accounts = ink::env::test::default_accounts(); + // Alice owns all the tokens on contract instantiation + assert_eq!(erc20.balance_of(accounts.alice), U256::from(100)); + // Bob does not owns tokens + assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); +} + +#[ink::test] +fn transfer_works() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + + // Constructor works. + let mut erc20 = Erc20::new(100.into()); + // Transfer event triggered during initial construction. + assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); + // Alice transfers 10 tokens to Bob. + assert_eq!(erc20.transfer(accounts.bob, U256::from(10)), Ok(())); + // Bob owns 10 tokens. + assert_eq!(erc20.balance_of(accounts.bob), U256::from(10)); + + let emitted_events = ink::env::test::recorded_events(); + assert_eq!(emitted_events.len(), 2); + // Check first transfer event related to ERC-20 instantiation. + assert_transfer_event( + &emitted_events[0], + None, + Some(accounts.alice), + 100.into(), + ); + // Check the second transfer event relating to the actual transfer. + assert_transfer_event( + &emitted_events[1], + Some(accounts.alice), + Some(accounts.bob), + 10.into(), + ); +} + +#[ink::test] +fn invalid_transfer_should_fail() { + // Constructor works. + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + + let initial_supply = 100.into(); + let mut erc20 = Erc20::new(initial_supply); + + assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); + + // Set the contract as callee and Bob as caller. + let contract = ink::env::address(); + ink::env::test::set_callee(contract); + set_caller(accounts.bob); + + // Bob fails to transfer 10 tokens to Eve. + assert_eq!( + erc20.transfer(accounts.eve, 10.into()), + Err(Error::InsufficientBalance) + ); + // Alice owns all the tokens. + assert_eq!(erc20.balance_of(accounts.alice), U256::from(100)); + assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); + assert_eq!(erc20.balance_of(accounts.eve), U256::zero()); + + // Transfer event triggered during initial construction. + let emitted_events = ink::env::test::recorded_events(); + assert_eq!(emitted_events.len(), 1); + assert_transfer_event( + &emitted_events[0], + None, + Some(accounts.alice), + 100.into(), + ); +} + +#[ink::test] +fn transfer_from_works() { + // Constructor works. + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + + let mut erc20 = Erc20::new(100.into()); + + // Bob fails to transfer tokens owned by Alice. + assert_eq!( + erc20.transfer_from(accounts.alice, accounts.eve, 10.into()), + Err(Error::InsufficientAllowance) + ); + // Alice approves Bob for token transfers on her behalf. + assert_eq!(erc20.approve(accounts.bob, 10.into()), Ok(())); + + // The approve event takes place. + assert_eq!(ink::env::test::recorded_events().len(), 2); + + // Set the contract as callee and Bob as caller. + let contract = ink::env::address(); + ink::env::test::set_callee(contract); + ink::env::test::set_caller(accounts.bob); + + // Bob transfers tokens from Alice to Eve. + assert_eq!( + erc20.transfer_from(accounts.alice, accounts.eve, 10.into()), + Ok(()) + ); + // Eve owns tokens. + assert_eq!(erc20.balance_of(accounts.eve), U256::from(10)); + + // Check all transfer events that happened during the previous calls: + let emitted_events = ink::env::test::recorded_events(); + assert_eq!(emitted_events.len(), 3); + assert_transfer_event( + &emitted_events[0], + None, + Some(accounts.alice), + 100.into(), + ); + // The second event `emitted_events[1]` is an Approve event that we skip + // checking. + assert_transfer_event( + &emitted_events[2], + Some(accounts.alice), + Some(accounts.eve), + 10.into(), + ); +} + +#[ink::test] +fn allowance_must_not_change_on_failed_transfer() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + let mut erc20 = Erc20::new(100.into()); + + // Alice approves Bob for token transfers on her behalf. + let alice_balance = erc20.balance_of(accounts.alice); + let initial_allowance = alice_balance + 2; + assert_eq!(erc20.approve(accounts.bob, initial_allowance), Ok(())); + + // Get contract address. + let callee = ink::env::address(); + ink::env::test::set_callee(callee); + ink::env::test::set_caller(accounts.bob); + + // Bob tries to transfer tokens from Alice to Eve. + let emitted_events_before = ink::env::test::recorded_events().len(); + assert_eq!( + erc20.transfer_from( + accounts.alice, + accounts.eve, + alice_balance + U256::from(1) + ), + Err(Error::InsufficientBalance) + ); + // Allowance must have stayed the same + assert_eq!( + erc20.allowance(accounts.alice, accounts.bob), + initial_allowance + ); + // No more events must have been emitted + assert_eq!( + emitted_events_before, + ink::env::test::recorded_events().len() + ) +} + +// --- E2E Tests --- + +#[cfg(feature = "e2e-tests")] +mod e2e_tests { + use super::*; + use ink_e2e::ContractsBackend; + + type E2EResult = std::result::Result>; + + #[ink_e2e::test] + async fn e2e_transfer(mut client: Client) -> E2EResult<()> { + // given + let total_supply = U256::from(1_000_000_000); + let mut constructor = Erc20Ref::new(total_supply); + let erc20 = client + .instantiate("erc20", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = erc20.call_builder::(); + + // when + let total_supply_msg = call_builder.total_supply(); + let total_supply_res = client + .call(&ink_e2e::bob(), &total_supply_msg) + .dry_run() + .await?; + + let bob_account = ink_e2e::address::( + ink_e2e::Sr25519Keyring::Bob, + ); + let transfer_to_bob = U256::from(500_000_000); + let transfer = call_builder.transfer(bob_account, transfer_to_bob); + let _transfer_res = client + .call(&ink_e2e::alice(), &transfer) + .submit() + .await + .expect("transfer failed"); + + let balance_of = call_builder.balance_of(bob_account); + let balance_of_res = client + .call(&ink_e2e::alice(), &balance_of) + .dry_run() + .await?; + + // then + assert_eq!( + total_supply, + total_supply_res.return_value(), + "total_supply" + ); + assert_eq!(transfer_to_bob, balance_of_res.return_value(), "balance_of"); + + Ok(()) + } + + #[ink_e2e::test] + async fn e2e_allowances(mut client: Client) -> E2EResult<()> { + // given + let total_supply = U256::from(1_000_000_000); + let mut constructor = Erc20Ref::new(total_supply); + let erc20 = client + .instantiate("erc20", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = erc20.call_builder::(); + + // when + + let bob_account = ink_e2e::address::( + ink_e2e::Sr25519Keyring::Bob, + ); + let charlie_account = ink_e2e::address::( + ink_e2e::Sr25519Keyring::Charlie, + ); + + let amount = U256::from(500_000_000); + // tx + let transfer_from = + call_builder.transfer_from(bob_account, charlie_account, amount); + let transfer_from_result = client + .call(&ink_e2e::charlie(), &transfer_from) + .submit() + .await; + + assert!( + transfer_from_result.is_err(), + "unapproved transfer_from should fail" + ); + + // Bob approves Charlie to transfer up to amount on his behalf + let approved_value = U256::from(1_000); + let approve_call = call_builder.approve(charlie_account, approved_value); + client + .call(&ink_e2e::bob(), &approve_call) + .submit() + .await + .expect("approve failed"); + + // `transfer_from` the approved amount + let transfer_from = + call_builder.transfer_from(bob_account, charlie_account, approved_value); + let transfer_from_result = client + .call(&ink_e2e::charlie(), &transfer_from) + .submit() + .await; + assert!( + transfer_from_result.is_ok(), + "approved transfer_from should succeed" + ); + + let balance_of = call_builder.balance_of(bob_account); + let balance_of_res = client + .call(&ink_e2e::alice(), &balance_of) + .dry_run() + .await?; + + // `transfer_from` again, this time exceeding the approved amount + let transfer_from = + call_builder.transfer_from(bob_account, charlie_account, 1.into()); + let transfer_from_result = client + .call(&ink_e2e::charlie(), &transfer_from) + .submit() + .await; + assert!( + transfer_from_result.is_err(), + "transfer_from exceeding the approved amount should fail" + ); + + assert_eq!( + total_supply - approved_value, + balance_of_res.return_value(), + "balance_of" + ); + + Ok(()) + } +} \ No newline at end of file diff --git a/integration-tests/public/erc721/lib.rs b/integration-tests/public/erc721/lib.rs index dfa0b5ea60..1123c480be 100644 --- a/integration-tests/public/erc721/lib.rs +++ b/integration-tests/public/erc721/lib.rs @@ -53,7 +53,7 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] #[ink::contract] -mod erc721 { +pub mod erc721 { use ink::storage::Mapping; /// A token ID. @@ -389,310 +389,7 @@ mod erc721 { || self.approved_for_all(owner, from)) } } - - /// Unit tests - #[cfg(test)] - mod tests { - /// Imports all the definitions from the outer scope so we can use them here. - use super::*; - - #[ink::test] - fn mint_works() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - // Create a new contract instance. - let mut erc721 = Erc721::new(); - // Token 1 does not exists. - assert_eq!(erc721.owner_of(1), None); - // Alice does not owns tokens. - assert_eq!(erc721.balance_of(accounts.alice), 0); - // Create token Id 1. - assert_eq!(erc721.mint(1), Ok(())); - // Alice owns 1 token. - assert_eq!(erc721.balance_of(accounts.alice), 1); - } - - #[ink::test] - fn mint_existing_should_fail() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - // Create a new contract instance. - let mut erc721 = Erc721::new(); - // Create token Id 1. - assert_eq!(erc721.mint(1), Ok(())); - // The first Transfer event takes place - assert_eq!(1, ink::env::test::recorded_events().len()); - // Alice owns 1 token. - assert_eq!(erc721.balance_of(accounts.alice), 1); - // Alice owns token Id 1. - assert_eq!(erc721.owner_of(1), Some(accounts.alice)); - // Cannot create token Id if it exists. - // Bob cannot own token Id 1. - assert_eq!(erc721.mint(1), Err(Error::TokenExists)); - } - - #[ink::test] - fn transfer_works() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - // Create a new contract instance. - let mut erc721 = Erc721::new(); - // Create token Id 1 for Alice - assert_eq!(erc721.mint(1), Ok(())); - // Alice owns token 1 - assert_eq!(erc721.balance_of(accounts.alice), 1); - // Bob does not owns any token - assert_eq!(erc721.balance_of(accounts.bob), 0); - // The first Transfer event takes place - assert_eq!(1, ink::env::test::recorded_events().len()); - // Alice transfers token 1 to Bob - assert_eq!(erc721.transfer(accounts.bob, 1), Ok(())); - // The second Transfer event takes place - assert_eq!(2, ink::env::test::recorded_events().len()); - // Bob owns token 1 - assert_eq!(erc721.balance_of(accounts.bob), 1); - } - - #[ink::test] - fn invalid_transfer_should_fail() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - // Create a new contract instance. - let mut erc721 = Erc721::new(); - // Transfer token fails if it does not exists. - assert_eq!(erc721.transfer(accounts.bob, 2), Err(Error::TokenNotFound)); - // Token Id 2 does not exists. - assert_eq!(erc721.owner_of(2), None); - // Create token Id 2. - assert_eq!(erc721.mint(2), Ok(())); - // Alice owns 1 token. - assert_eq!(erc721.balance_of(accounts.alice), 1); - // Token Id 2 is owned by Alice. - assert_eq!(erc721.owner_of(2), Some(accounts.alice)); - // Set Bob as caller - set_caller(accounts.bob); - // Bob cannot transfer not owned tokens. - assert_eq!(erc721.transfer(accounts.eve, 2), Err(Error::NotApproved)); - } - - #[ink::test] - fn approved_transfer_works() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - // Create a new contract instance. - let mut erc721 = Erc721::new(); - // Create token Id 1. - assert_eq!(erc721.mint(1), Ok(())); - // Token Id 1 is owned by Alice. - assert_eq!(erc721.owner_of(1), Some(accounts.alice)); - // Approve token Id 1 transfer for Bob on behalf of Alice. - assert_eq!(erc721.approve(accounts.bob, 1), Ok(())); - // Set Bob as caller - set_caller(accounts.bob); - // Bob transfers token Id 1 from Alice to Eve. - assert_eq!( - erc721.transfer_from(accounts.alice, accounts.eve, 1), - Ok(()) - ); - // TokenId 3 is owned by Eve. - assert_eq!(erc721.owner_of(1), Some(accounts.eve)); - // Alice does not owns tokens. - assert_eq!(erc721.balance_of(accounts.alice), 0); - // Bob does not owns tokens. - assert_eq!(erc721.balance_of(accounts.bob), 0); - // Eve owns 1 token. - assert_eq!(erc721.balance_of(accounts.eve), 1); - } - - #[ink::test] - fn approved_for_all_works() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - // Create a new contract instance. - let mut erc721 = Erc721::new(); - // Create token Id 1. - assert_eq!(erc721.mint(1), Ok(())); - // Create token Id 2. - assert_eq!(erc721.mint(2), Ok(())); - // Alice owns 2 tokens. - assert_eq!(erc721.balance_of(accounts.alice), 2); - // Approve token Id 1 transfer for Bob on behalf of Alice. - assert_eq!(erc721.set_approval_for_all(accounts.bob, true), Ok(())); - // Bob is an approved operator for Alice - assert!(erc721.is_approved_for_all(accounts.alice, accounts.bob)); - // Set Bob as caller - set_caller(accounts.bob); - // Bob transfers token Id 1 from Alice to Eve. - assert_eq!( - erc721.transfer_from(accounts.alice, accounts.eve, 1), - Ok(()) - ); - // TokenId 1 is owned by Eve. - assert_eq!(erc721.owner_of(1), Some(accounts.eve)); - // Alice owns 1 token. - assert_eq!(erc721.balance_of(accounts.alice), 1); - // Bob transfers token Id 2 from Alice to Eve. - assert_eq!( - erc721.transfer_from(accounts.alice, accounts.eve, 2), - Ok(()) - ); - // Bob does not own tokens. - assert_eq!(erc721.balance_of(accounts.bob), 0); - // Eve owns 2 tokens. - assert_eq!(erc721.balance_of(accounts.eve), 2); - // Remove operator approval for Bob on behalf of Alice. - set_caller(accounts.alice); - assert_eq!(erc721.set_approval_for_all(accounts.bob, false), Ok(())); - // Bob is not an approved operator for Alice. - assert!(!erc721.is_approved_for_all(accounts.alice, accounts.bob)); - } - - #[ink::test] - fn approve_nonexistent_token_should_fail() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - // Create a new contract instance. - let mut erc721 = Erc721::new(); - // Approve transfer of nonexistent token id 1 - assert_eq!(erc721.approve(accounts.bob, 1), Err(Error::TokenNotFound)); - } - - #[ink::test] - fn not_approved_transfer_should_fail() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - // Create a new contract instance. - let mut erc721 = Erc721::new(); - // Create token Id 1. - assert_eq!(erc721.mint(1), Ok(())); - // Alice owns 1 token. - assert_eq!(erc721.balance_of(accounts.alice), 1); - // Bob does not owns tokens. - assert_eq!(erc721.balance_of(accounts.bob), 0); - // Eve does not owns tokens. - assert_eq!(erc721.balance_of(accounts.eve), 0); - // Set Eve as caller - set_caller(accounts.eve); - // Eve is not an approved operator by Alice. - assert_eq!( - erc721.transfer_from(accounts.alice, accounts.frank, 1), - Err(Error::NotApproved) - ); - // Alice owns 1 token. - assert_eq!(erc721.balance_of(accounts.alice), 1); - // Bob does not owns tokens. - assert_eq!(erc721.balance_of(accounts.bob), 0); - // Eve does not owns tokens. - assert_eq!(erc721.balance_of(accounts.eve), 0); - } - - #[ink::test] - fn burn_works() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - // Create a new contract instance. - let mut erc721 = Erc721::new(); - // Create token Id 1 for Alice - assert_eq!(erc721.mint(1), Ok(())); - // Alice owns 1 token. - assert_eq!(erc721.balance_of(accounts.alice), 1); - // Alice owns token Id 1. - assert_eq!(erc721.owner_of(1), Some(accounts.alice)); - // Destroy token Id 1. - assert_eq!(erc721.burn(1), Ok(())); - // Alice does not owns tokens. - assert_eq!(erc721.balance_of(accounts.alice), 0); - // Token Id 1 does not exists - assert_eq!(erc721.owner_of(1), None); - } - - #[ink::test] - fn burn_fails_token_not_found() { - // Create a new contract instance. - let mut erc721 = Erc721::new(); - // Try burning a non existent token - assert_eq!(erc721.burn(1), Err(Error::TokenNotFound)); - } - - #[ink::test] - fn burn_fails_not_owner() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - // Create a new contract instance. - let mut erc721 = Erc721::new(); - // Create token Id 1 for Alice - assert_eq!(erc721.mint(1), Ok(())); - // Try burning this token with a different account - set_caller(accounts.eve); - assert_eq!(erc721.burn(1), Err(Error::NotOwner)); - } - - #[ink::test] - fn burn_clears_approval() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - // Create a new contract instance. - let mut erc721 = Erc721::new(); - // Create token Id 1 for Alice - assert_eq!(erc721.mint(1), Ok(())); - // Alice gives approval to Bob to transfer token Id 1 - assert_eq!(erc721.approve(accounts.bob, 1), Ok(())); - // Alice burns token - assert_eq!(erc721.burn(1), Ok(())); - // Set caller to Frank - set_caller(accounts.frank); - // Frank mints token Id 1 - assert_eq!(erc721.mint(1), Ok(())); - // Set caller to Bob - set_caller(accounts.bob); - // Bob tries to transfer token Id 1 from Frank to himself - assert_eq!( - erc721.transfer_from(accounts.frank, accounts.bob, 1), - Err(Error::NotApproved) - ); - } - - #[ink::test] - fn transfer_from_fails_not_owner() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - // Create a new contract instance. - let mut erc721 = Erc721::new(); - // Create token Id 1 for Alice - assert_eq!(erc721.mint(1), Ok(())); - // Bob can transfer alice's tokens - assert_eq!(erc721.set_approval_for_all(accounts.bob, true), Ok(())); - // Set caller to Frank - set_caller(accounts.frank); - // Create token Id 2 for Frank - assert_eq!(erc721.mint(2), Ok(())); - // Set caller to Bob - set_caller(accounts.bob); - // Bob makes invalid call to transfer_from (Alice is token owner, not Frank) - assert_eq!( - erc721.transfer_from(accounts.frank, accounts.bob, 1), - Err(Error::NotOwner) - ); - } - - #[ink::test] - fn transfer_fails_not_owner() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - // Create a new contract instance. - let mut erc721 = Erc721::new(); - // Create token Id 1 for Alice - assert_eq!(erc721.mint(1), Ok(())); - // Bob can transfer alice's tokens - assert_eq!(erc721.set_approval_for_all(accounts.bob, true), Ok(())); - // Set caller to bob - set_caller(accounts.bob); - // Bob makes invalid call to transfer (he is not token owner, Alice is) - assert_eq!(erc721.transfer(accounts.bob, 1), Err(Error::NotOwner)); - } - - fn set_caller(sender: Address) { - ink::env::test::set_caller(sender); - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/erc721/tests.rs b/integration-tests/public/erc721/tests.rs new file mode 100644 index 0000000000..0991f6bbc7 --- /dev/null +++ b/integration-tests/public/erc721/tests.rs @@ -0,0 +1,301 @@ +use crate::erc721::*; +use ink::primitives::Address; + +fn set_caller(sender: Address) { + ink::env::test::set_caller(sender); +} + +#[ink::test] +fn mint_works() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + // Create a new contract instance. + let mut erc721 = Erc721::new(); + // Token 1 does not exists. + assert_eq!(erc721.owner_of(1), None); + // Alice does not owns tokens. + assert_eq!(erc721.balance_of(accounts.alice), 0); + // Create token Id 1. + assert_eq!(erc721.mint(1), Ok(())); + // Alice owns 1 token. + assert_eq!(erc721.balance_of(accounts.alice), 1); +} + +#[ink::test] +fn mint_existing_should_fail() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + // Create a new contract instance. + let mut erc721 = Erc721::new(); + // Create token Id 1. + assert_eq!(erc721.mint(1), Ok(())); + // The first Transfer event takes place + assert_eq!(1, ink::env::test::recorded_events().len()); + // Alice owns 1 token. + assert_eq!(erc721.balance_of(accounts.alice), 1); + // Alice owns token Id 1. + assert_eq!(erc721.owner_of(1), Some(accounts.alice)); + // Cannot create token Id if it exists. + // Bob cannot own token Id 1. + assert_eq!(erc721.mint(1), Err(Error::TokenExists)); +} + +#[ink::test] +fn transfer_works() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + // Create a new contract instance. + let mut erc721 = Erc721::new(); + // Create token Id 1 for Alice + assert_eq!(erc721.mint(1), Ok(())); + // Alice owns token 1 + assert_eq!(erc721.balance_of(accounts.alice), 1); + // Bob does not owns any token + assert_eq!(erc721.balance_of(accounts.bob), 0); + // The first Transfer event takes place + assert_eq!(1, ink::env::test::recorded_events().len()); + // Alice transfers token 1 to Bob + assert_eq!(erc721.transfer(accounts.bob, 1), Ok(())); + // The second Transfer event takes place + assert_eq!(2, ink::env::test::recorded_events().len()); + // Bob owns token 1 + assert_eq!(erc721.balance_of(accounts.bob), 1); +} + +#[ink::test] +fn invalid_transfer_should_fail() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + // Create a new contract instance. + let mut erc721 = Erc721::new(); + // Transfer token fails if it does not exists. + assert_eq!(erc721.transfer(accounts.bob, 2), Err(Error::TokenNotFound)); + // Token Id 2 does not exists. + assert_eq!(erc721.owner_of(2), None); + // Create token Id 2. + assert_eq!(erc721.mint(2), Ok(())); + // Alice owns 1 token. + assert_eq!(erc721.balance_of(accounts.alice), 1); + // Token Id 2 is owned by Alice. + assert_eq!(erc721.owner_of(2), Some(accounts.alice)); + // Set Bob as caller + set_caller(accounts.bob); + // Bob cannot transfer not owned tokens. + assert_eq!(erc721.transfer(accounts.eve, 2), Err(Error::NotApproved)); +} + +#[ink::test] +fn approved_transfer_works() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + // Create a new contract instance. + let mut erc721 = Erc721::new(); + // Create token Id 1. + assert_eq!(erc721.mint(1), Ok(())); + // Token Id 1 is owned by Alice. + assert_eq!(erc721.owner_of(1), Some(accounts.alice)); + // Approve token Id 1 transfer for Bob on behalf of Alice. + assert_eq!(erc721.approve(accounts.bob, 1), Ok(())); + // Set Bob as caller + set_caller(accounts.bob); + // Bob transfers token Id 1 from Alice to Eve. + assert_eq!( + erc721.transfer_from(accounts.alice, accounts.eve, 1), + Ok(()) + ); + // TokenId 3 is owned by Eve. + assert_eq!(erc721.owner_of(1), Some(accounts.eve)); + // Alice does not owns tokens. + assert_eq!(erc721.balance_of(accounts.alice), 0); + // Bob does not owns tokens. + assert_eq!(erc721.balance_of(accounts.bob), 0); + // Eve owns 1 token. + assert_eq!(erc721.balance_of(accounts.eve), 1); +} + +#[ink::test] +fn approved_for_all_works() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + // Create a new contract instance. + let mut erc721 = Erc721::new(); + // Create token Id 1. + assert_eq!(erc721.mint(1), Ok(())); + // Create token Id 2. + assert_eq!(erc721.mint(2), Ok(())); + // Alice owns 2 tokens. + assert_eq!(erc721.balance_of(accounts.alice), 2); + // Approve token Id 1 transfer for Bob on behalf of Alice. + assert_eq!(erc721.set_approval_for_all(accounts.bob, true), Ok(())); + // Bob is an approved operator for Alice + assert!(erc721.is_approved_for_all(accounts.alice, accounts.bob)); + // Set Bob as caller + set_caller(accounts.bob); + // Bob transfers token Id 1 from Alice to Eve. + assert_eq!( + erc721.transfer_from(accounts.alice, accounts.eve, 1), + Ok(()) + ); + // TokenId 1 is owned by Eve. + assert_eq!(erc721.owner_of(1), Some(accounts.eve)); + // Alice owns 1 token. + assert_eq!(erc721.balance_of(accounts.alice), 1); + // Bob transfers token Id 2 from Alice to Eve. + assert_eq!( + erc721.transfer_from(accounts.alice, accounts.eve, 2), + Ok(()) + ); + // Bob does not own tokens. + assert_eq!(erc721.balance_of(accounts.bob), 0); + // Eve owns 2 tokens. + assert_eq!(erc721.balance_of(accounts.eve), 2); + // Remove operator approval for Bob on behalf of Alice. + set_caller(accounts.alice); + assert_eq!(erc721.set_approval_for_all(accounts.bob, false), Ok(())); + // Bob is not an approved operator for Alice. + assert!(!erc721.is_approved_for_all(accounts.alice, accounts.bob)); +} + +#[ink::test] +fn approve_nonexistent_token_should_fail() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + // Create a new contract instance. + let mut erc721 = Erc721::new(); + // Approve transfer of nonexistent token id 1 + assert_eq!(erc721.approve(accounts.bob, 1), Err(Error::TokenNotFound)); +} + +#[ink::test] +fn not_approved_transfer_should_fail() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + // Create a new contract instance. + let mut erc721 = Erc721::new(); + // Create token Id 1. + assert_eq!(erc721.mint(1), Ok(())); + // Alice owns 1 token. + assert_eq!(erc721.balance_of(accounts.alice), 1); + // Bob does not owns tokens. + assert_eq!(erc721.balance_of(accounts.bob), 0); + // Eve does not owns tokens. + assert_eq!(erc721.balance_of(accounts.eve), 0); + // Set Eve as caller + set_caller(accounts.eve); + // Eve is not an approved operator by Alice. + assert_eq!( + erc721.transfer_from(accounts.alice, accounts.frank, 1), + Err(Error::NotApproved) + ); + // Alice owns 1 token. + assert_eq!(erc721.balance_of(accounts.alice), 1); + // Bob does not owns tokens. + assert_eq!(erc721.balance_of(accounts.bob), 0); + // Eve does not owns tokens. + assert_eq!(erc721.balance_of(accounts.eve), 0); +} + +#[ink::test] +fn burn_works() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + // Create a new contract instance. + let mut erc721 = Erc721::new(); + // Create token Id 1 for Alice + assert_eq!(erc721.mint(1), Ok(())); + // Alice owns 1 token. + assert_eq!(erc721.balance_of(accounts.alice), 1); + // Alice owns token Id 1. + assert_eq!(erc721.owner_of(1), Some(accounts.alice)); + // Destroy token Id 1. + assert_eq!(erc721.burn(1), Ok(())); + // Alice does not owns tokens. + assert_eq!(erc721.balance_of(accounts.alice), 0); + // Token Id 1 does not exists + assert_eq!(erc721.owner_of(1), None); +} + +#[ink::test] +fn burn_fails_token_not_found() { + // Create a new contract instance. + let mut erc721 = Erc721::new(); + // Try burning a non existent token + assert_eq!(erc721.burn(1), Err(Error::TokenNotFound)); +} + +#[ink::test] +fn burn_fails_not_owner() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + // Create a new contract instance. + let mut erc721 = Erc721::new(); + // Create token Id 1 for Alice + assert_eq!(erc721.mint(1), Ok(())); + // Try burning this token with a different account + set_caller(accounts.eve); + assert_eq!(erc721.burn(1), Err(Error::NotOwner)); +} + +#[ink::test] +fn burn_clears_approval() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + // Create a new contract instance. + let mut erc721 = Erc721::new(); + // Create token Id 1 for Alice + assert_eq!(erc721.mint(1), Ok(())); + // Alice gives approval to Bob to transfer token Id 1 + assert_eq!(erc721.approve(accounts.bob, 1), Ok(())); + // Alice burns token + assert_eq!(erc721.burn(1), Ok(())); + // Set caller to Frank + set_caller(accounts.frank); + // Frank mints token Id 1 + assert_eq!(erc721.mint(1), Ok(())); + // Set caller to Bob + set_caller(accounts.bob); + // Bob tries to transfer token Id 1 from Frank to himself + assert_eq!( + erc721.transfer_from(accounts.frank, accounts.bob, 1), + Err(Error::NotApproved) + ); +} + +#[ink::test] +fn transfer_from_fails_not_owner() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + // Create a new contract instance. + let mut erc721 = Erc721::new(); + // Create token Id 1 for Alice + assert_eq!(erc721.mint(1), Ok(())); + // Bob can transfer alice's tokens + assert_eq!(erc721.set_approval_for_all(accounts.bob, true), Ok(())); + // Set caller to Frank + set_caller(accounts.frank); + // Create token Id 2 for Frank + assert_eq!(erc721.mint(2), Ok(())); + // Set caller to Bob + set_caller(accounts.bob); + // Bob makes invalid call to transfer_from (Alice is token owner, not Frank) + assert_eq!( + erc721.transfer_from(accounts.frank, accounts.bob, 1), + Err(Error::NotOwner) + ); +} + +#[ink::test] +fn transfer_fails_not_owner() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + // Create a new contract instance. + let mut erc721 = Erc721::new(); + // Create token Id 1 for Alice + assert_eq!(erc721.mint(1), Ok(())); + // Bob can transfer alice's tokens + assert_eq!(erc721.set_approval_for_all(accounts.bob, true), Ok(())); + // Set caller to bob + set_caller(accounts.bob); + // Bob makes invalid call to transfer (he is not token owner, Alice is) + assert_eq!(erc721.transfer(accounts.bob, 1), Err(Error::NotOwner)); +} \ No newline at end of file From 05fd5c7f8cdbf947dcffddb001fe21a68df610ac Mon Sep 17 00:00:00 2001 From: Alhibb <63309522+Alhibb@users.noreply.github.com> Date: Fri, 5 Dec 2025 12:47:22 +0100 Subject: [PATCH 4/5] Test refactored --- .../public/fallible-setter/lib.rs | 80 +-- .../public/fallible-setter/tests.rs | 76 +++ integration-tests/public/flipper/lib.rs | 109 +--- integration-tests/public/flipper/tests.rs | 102 ++++ integration-tests/public/fuzz-testing/lib.rs | 88 +--- .../public/fuzz-testing/tests.rs | 77 +++ integration-tests/public/incrementer/lib.rs | 26 +- integration-tests/public/incrementer/tests.rs | 29 ++ integration-tests/public/lazyvec/lib.rs | 72 +-- integration-tests/public/lazyvec/tests.rs | 57 +++ .../public/multi-contract-caller/lib.rs | 104 +--- .../public/multi-contract-caller/tests.rs | 97 ++++ integration-tests/public/multisig/lib.rs | 468 +----------------- integration-tests/public/multisig/tests.rs | 374 ++++++++++++++ .../public/payment-channel/Cargo.toml | 13 +- .../public/payment-channel/lib.rs | 310 +----------- .../public/payment-channel/tests.rs | 301 +++++++++++ .../public/precompile-demo/lib.rs | 40 +- .../public/precompile-demo/tests.rs | 31 ++ .../trait-dyn-cross-contract-calls/lib.rs | 92 +--- .../trait-dyn-cross-contract-calls/tests.rs | 87 ++++ integration-tests/public/trait-erc20/lib.rs | 315 +----------- integration-tests/public/trait-erc20/tests.rs | 294 +++++++++++ integration-tests/public/trait-flipper/lib.rs | 24 +- .../public/trait-flipper/tests.rs | 28 ++ .../public/trait-incrementer/lib.rs | 24 +- .../public/trait-incrementer/tests.rs | 28 ++ .../public/wildcard-selector/lib.rs | 153 +----- .../public/wildcard-selector/tests.rs | 127 +++++ 29 files changed, 1786 insertions(+), 1840 deletions(-) create mode 100644 integration-tests/public/fallible-setter/tests.rs create mode 100644 integration-tests/public/flipper/tests.rs create mode 100644 integration-tests/public/fuzz-testing/tests.rs create mode 100644 integration-tests/public/incrementer/tests.rs create mode 100644 integration-tests/public/lazyvec/tests.rs create mode 100644 integration-tests/public/multi-contract-caller/tests.rs create mode 100644 integration-tests/public/multisig/tests.rs create mode 100644 integration-tests/public/payment-channel/tests.rs create mode 100644 integration-tests/public/precompile-demo/tests.rs create mode 100644 integration-tests/public/trait-dyn-cross-contract-calls/tests.rs create mode 100644 integration-tests/public/trait-erc20/tests.rs create mode 100644 integration-tests/public/trait-flipper/tests.rs create mode 100644 integration-tests/public/trait-incrementer/tests.rs create mode 100644 integration-tests/public/wildcard-selector/tests.rs diff --git a/integration-tests/public/fallible-setter/lib.rs b/integration-tests/public/fallible-setter/lib.rs index e32c487a4a..dd4aac85c4 100644 --- a/integration-tests/public/fallible-setter/lib.rs +++ b/integration-tests/public/fallible-setter/lib.rs @@ -54,81 +54,7 @@ pub mod fallible_setter { self.value } } - - #[cfg(test)] - mod tests { - use super::*; - - #[ink::test] - fn it_works() { - // given - let mut fallible_setter = FallibleSetter::new(0).expect("init failed"); - assert_eq!(fallible_setter.get(), 0); - - // when - let res = fallible_setter.try_set(1); - assert!(res.is_ok()); - - // when - let res = fallible_setter.try_set(1); - assert_eq!(res, Err(Error::NoChange)); - - // when - let res = fallible_setter.try_set(101); - assert_eq!(res, Err(Error::TooLarge)); - - // then - assert_eq!(fallible_setter.get(), 1); - } - } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink_e2e::ContractsBackend; - - type E2EResult = std::result::Result>; - - #[ink_e2e::test] - async fn it_works(mut client: Client) -> E2EResult<()> { - // given - let mut constructor = FallibleSetterRef::new(0); - let contract = client - .instantiate("fallible_setter", &ink_e2e::bob(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = contract.call_builder::(); - - let get = call_builder.get(); - let get_res = client.call(&ink_e2e::bob(), &get).submit().await?; - assert_eq!(get_res.return_value(), 0); - - // when - let set = call_builder.try_set(1); - let set_res = client - .call(&ink_e2e::bob(), &set) - .submit() - .await - .expect("set failed"); - assert!(set_res.return_value().is_ok()); - - // when - let set = call_builder.try_set(1); - let set_res = client.call(&ink_e2e::bob(), &set).submit().await; - assert!(matches!(set_res, Err(ink_e2e::Error::CallExtrinsic(_, _)))); - - // when - let set = call_builder.try_set(101); - let set_res = client.call(&ink_e2e::bob(), &set).submit().await; - assert!(matches!(set_res, Err(ink_e2e::Error::CallExtrinsic(_, _)))); - - // then - let get = call_builder.get(); - let get_res = client.call(&ink_e2e::bob(), &get).dry_run().await?; - assert_eq!(get_res.return_value(), 1); - - Ok(()) - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/fallible-setter/tests.rs b/integration-tests/public/fallible-setter/tests.rs new file mode 100644 index 0000000000..d6c6e71b86 --- /dev/null +++ b/integration-tests/public/fallible-setter/tests.rs @@ -0,0 +1,76 @@ +use super::Error; +use super::fallible_setter::*; + +#[ink::test] +fn it_works() { + // given + let mut fallible_setter = FallibleSetter::new(0).expect("init failed"); + assert_eq!(fallible_setter.get(), 0); + + // when + let res = fallible_setter.try_set(1); + assert!(res.is_ok()); + + // when: trying to set same value + let res = fallible_setter.try_set(1); + assert_eq!(res, Err(Error::NoChange)); + + // when: trying to set value > 100 + let res = fallible_setter.try_set(101); + assert_eq!(res, Err(Error::TooLarge)); + + // then + assert_eq!(fallible_setter.get(), 1); +} + +#[cfg(all(test, feature = "e2e-tests"))] +mod e2e_tests { + use super::*; + use ink_e2e::ContractsBackend; + + type E2EResult = std::result::Result>; + + #[ink_e2e::test] + async fn it_works(mut client: Client) -> E2EResult<()> { + // given + let mut constructor = FallibleSetterRef::new(0); + let contract = client + .instantiate("fallible_setter", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = contract.call_builder::(); + + let get = call_builder.get(); + let get_res = client.call(&ink_e2e::bob(), &get).submit().await?; + assert_eq!(get_res.return_value(), 0); + + // when + let set = call_builder.try_set(1); + let set_res = client + .call(&ink_e2e::bob(), &set) + .submit() + .await + .expect("set failed"); + assert!(set_res.return_value().is_ok()); + + // when: trying to set same value (should fail) + let set = call_builder.try_set(1); + let set_res = client.call(&ink_e2e::bob(), &set).submit().await; + // In this specific e2e environment configuration, the contract returning Result::Err + // is surfacing as a CallExtrinsic error. + assert!(matches!(set_res, Err(ink_e2e::Error::CallExtrinsic(_, _)))); + + // when: trying to set value > 100 (should fail) + let set = call_builder.try_set(101); + let set_res = client.call(&ink_e2e::bob(), &set).submit().await; + assert!(matches!(set_res, Err(ink_e2e::Error::CallExtrinsic(_, _)))); + + // then + let get = call_builder.get(); + let get_res = client.call(&ink_e2e::bob(), &get).dry_run().await?; + assert_eq!(get_res.return_value(), 1); + + Ok(()) + } +} \ No newline at end of file diff --git a/integration-tests/public/flipper/lib.rs b/integration-tests/public/flipper/lib.rs index 9ab3412ed1..fda6c5fd1e 100644 --- a/integration-tests/public/flipper/lib.rs +++ b/integration-tests/public/flipper/lib.rs @@ -26,110 +26,7 @@ pub mod flipper { self.value } } - - #[cfg(test)] - mod tests { - use super::*; - - #[ink::test] - fn it_works() { - let mut flipper = Flipper::new(false); - assert!(!flipper.get()); - flipper.flip(); - assert!(flipper.get()); - } - } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink_e2e::ContractsBackend; - - type E2EResult = std::result::Result>; - - #[ink_e2e::test] - async fn it_works(mut client: Client) -> E2EResult<()> { - // given - let mut constructor = FlipperRef::new(false); - let contract = client - .instantiate("flipper", &ink_e2e::bob(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = contract.call_builder::(); - - let get = call_builder.get(); - let get_res = client.call(&ink_e2e::bob(), &get).submit().await?; - assert!(!get_res.return_value()); - - // when - let flip = call_builder.flip(); - let _flip_res = client - .call(&ink_e2e::bob(), &flip) - .submit() - .await - .expect("flip failed"); - - // then - let get = call_builder.get(); - let get_res = client.call(&ink_e2e::bob(), &get).dry_run().await?; - assert!(get_res.return_value()); - - Ok(()) - } - - /// This test illustrates how to test an existing on-chain contract. - /// - /// You can utilize this to e.g. create a snapshot of a production chain - /// and run the E2E tests against a deployed contract there. - /// This process is explained [here](https://use.ink/5.x/basics/contract-testing/chain-snapshot). - /// - /// Before executing the test: - /// * Make sure you have a node running in the background, - /// * Supply the environment variable `CONTRACT_HEX` that points to a deployed - /// flipper contract. You can take the SS58 address which `cargo contract - /// instantiate` gives you and convert it to hex using `subkey inspect - /// `. - /// - /// The test is then run like this: - /// - /// ``` - /// # The env variable needs to be set, otherwise `ink_e2e` will spawn a new - /// # node process for each test. - /// $ export CONTRACTS_NODE_URL=ws://127.0.0.1:9944 - /// - /// $ export CONTRACT_ADDR_HEX=0x2c75f0aa09dbfbfd49e6286a0f2edd3b4913f04a58b13391c79e96782f5713e3 - /// $ cargo test --features e2e-tests e2e_test_deployed_contract -- --ignored - /// ``` - /// - /// # Developer Note - /// - /// The test is marked as ignored, as it has the above pre-conditions to succeed. - #[ink_e2e::test] - #[ignore] - async fn e2e_test_deployed_contract(mut client: Client) -> E2EResult<()> { - // given - use ink::Address; - let addr = std::env::var("CONTRACT_ADDR_HEX") - .unwrap() - .replace("0x", ""); - let addr_bytes: Vec = hex::decode(addr).unwrap(); - let addr = Address::from_slice(&addr_bytes[..]); - - use std::str::FromStr; - let suri = ink_e2e::subxt_signer::SecretUri::from_str("//Alice").unwrap(); - let caller = ink_e2e::Keypair::from_uri(&suri).unwrap(); - - // when - // Invoke `Flipper::get()` from `caller`'s account - let call_builder = ink_e2e::create_call_builder::(addr); - let get = call_builder.get(); - let get_res = client.call(&caller, &get).dry_run().await?; - - // then - assert!(get_res.return_value()); - - Ok(()) - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/flipper/tests.rs b/integration-tests/public/flipper/tests.rs new file mode 100644 index 0000000000..ce92ed696b --- /dev/null +++ b/integration-tests/public/flipper/tests.rs @@ -0,0 +1,102 @@ +use super::flipper::*; + +#[ink::test] +fn it_works() { + let mut flipper = Flipper::new(false); + assert!(!flipper.get()); + flipper.flip(); + assert!(flipper.get()); +} + +#[cfg(all(test, feature = "e2e-tests"))] +mod e2e_tests { + use super::*; + use ink_e2e::ContractsBackend; + + type E2EResult = std::result::Result>; + + #[ink_e2e::test] + async fn it_works(mut client: Client) -> E2EResult<()> { + // given + let mut constructor = FlipperRef::new(false); + let contract = client + .instantiate("flipper", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = contract.call_builder::(); + + let get = call_builder.get(); + let get_res = client.call(&ink_e2e::bob(), &get).submit().await?; + assert!(!get_res.return_value()); + + // when + let flip = call_builder.flip(); + let _flip_res = client + .call(&ink_e2e::bob(), &flip) + .submit() + .await + .expect("flip failed"); + + // then + let get = call_builder.get(); + let get_res = client.call(&ink_e2e::bob(), &get).dry_run().await?; + assert!(get_res.return_value()); + + Ok(()) + } + + /// This test illustrates how to test an existing on-chain contract. + /// + /// You can utilize this to e.g. create a snapshot of a production chain + /// and run the E2E tests against a deployed contract there. + /// This process is explained [here](https://use.ink/5.x/basics/contract-testing/chain-snapshot). + /// + /// Before executing the test: + /// * Make sure you have a node running in the background, + /// * Supply the environment variable `CONTRACT_HEX` that points to a deployed + /// flipper contract. You can take the SS58 address which `cargo contract + /// instantiate` gives you and convert it to hex using `subkey inspect + /// `. + /// + /// The test is then run like this: + /// + /// ``` + /// # The env variable needs to be set, otherwise `ink_e2e` will spawn a new + /// # node process for each test. + /// $ export CONTRACTS_NODE_URL=ws://127.0.0.1:9944 + /// + /// $ export CONTRACT_ADDR_HEX=0x2c75f0aa09dbfbfd49e6286a0f2edd3b4913f04a58b13391c79e96782f5713e3 + /// $ cargo test --features e2e-tests e2e_test_deployed_contract -- --ignored + /// ``` + /// + /// # Developer Note + /// + /// The test is marked as ignored, as it has the above pre-conditions to succeed. + #[ink_e2e::test] + #[ignore] + async fn e2e_test_deployed_contract(mut client: Client) -> E2EResult<()> { + // given + use ink::Address; + let addr = std::env::var("CONTRACT_ADDR_HEX") + .unwrap() + .replace("0x", ""); + let addr_bytes: Vec = hex::decode(addr).unwrap(); + let addr = Address::from_slice(&addr_bytes[..]); + + use std::str::FromStr; + let suri = ink_e2e::subxt_signer::SecretUri::from_str("//Alice").unwrap(); + let caller = ink_e2e::Keypair::from_uri(&suri).unwrap(); + + // when + // Invoke `Flipper::get()` from `caller`'s account + let call_builder = ink_e2e::create_call_builder::(addr); + let get = call_builder.get(); + let get_res = client.call(&caller, &get).dry_run().await?; + + // then + assert!(get_res.return_value()); + + Ok(()) + } +} \ No newline at end of file diff --git a/integration-tests/public/fuzz-testing/lib.rs b/integration-tests/public/fuzz-testing/lib.rs index ab57f64b41..6542d79a2c 100644 --- a/integration-tests/public/fuzz-testing/lib.rs +++ b/integration-tests/public/fuzz-testing/lib.rs @@ -12,8 +12,8 @@ pub mod fuzz_testing { #[derive(Clone, Debug)] #[ink::scale_derive(Encode, Decode, TypeInfo)] pub struct Point { - x: i32, - y: i32, + pub x: i32, + pub y: i32, } impl FuzzTesting { @@ -35,85 +35,7 @@ pub mod fuzz_testing { pt.x } } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink_e2e::ContractsBackend; - use quickcheck_macros::quickcheck; - - /// We use `#[ink_e2e::test(runtime)]` here. It doesn't start a node for each - /// test, but instead interacts with an in-process `pallet-revive`. - /// - /// See - /// for more details. - #[ink_e2e::test(runtime, replace_test_attr = "#[quickcheck]")] - async fn fuzzing_works_runtime(val: bool) -> bool { - let mut constructor = FuzzTestingRef::new(val); - let contract = client - .instantiate("fuzz_testing", &ink_e2e::bob(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let call_builder = contract.call_builder::(); - - let get = call_builder.get(); - let get_res = client.call(&ink_e2e::bob(), &get).submit().await.unwrap(); - get_res.return_value() == val - } - - /// It's also possible to fuzz with a "real" node as the backend. - /// - /// This means that, by default, for every test run a node process will - /// be spawned. You can work around this by setting the env variable - /// `CONTRACTS_NODE_URL`. But still, interactions with a real node will - /// always be more heavy-weight than "just" interacting with an in-process - /// `pallet-revive`. - #[ink_e2e::test(runtime, replace_test_attr = "#[quickcheck]")] - async fn fuzzing_works_node(val: bool) -> bool { - let mut constructor = FuzzTestingRef::new(val); - let contract = client - .instantiate("fuzz_testing", &ink_e2e::bob(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let call_builder = contract.call_builder::(); - - let get = call_builder.get(); - let get_res = client.call(&ink_e2e::bob(), &get).submit().await.unwrap(); - get_res.return_value() == val - } - - // We need to implement `Arbitrary` for `Point`, so `quickcheck` - // knows how to fuzz the struct. - use quickcheck::{ - Arbitrary, - Gen, - }; - impl Arbitrary for Point { - fn arbitrary(g: &mut Gen) -> Point { - Point { - x: i32::arbitrary(g), - y: i32::arbitrary(g), - } - } - } - - #[ink_e2e::test(runtime, replace_test_attr = "#[quickcheck]")] - async fn fuzzing_custom_struct_works(val: Point) -> bool { - ink_e2e::tracing::info!("fuzzing with value {val:?}"); - - let mut constructor = FuzzTestingRef::new(true); - let contract = client - .instantiate("fuzz_testing", &ink_e2e::bob(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let call_builder = contract.call_builder::(); - - let get = call_builder.extract_x(val.clone()); - let get_res = client.call(&ink_e2e::bob(), &get).submit().await.unwrap(); - get_res.return_value() == val.x - } - } } + +#[cfg(all(test, feature = "e2e-tests"))] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/fuzz-testing/tests.rs b/integration-tests/public/fuzz-testing/tests.rs new file mode 100644 index 0000000000..375719aa91 --- /dev/null +++ b/integration-tests/public/fuzz-testing/tests.rs @@ -0,0 +1,77 @@ +use super::fuzz_testing::{FuzzTesting, FuzzTestingRef, Point}; +use ink_e2e::ContractsBackend; +use quickcheck_macros::quickcheck; +use quickcheck::{ + Arbitrary, + Gen, +}; + +// We need to implement `Arbitrary` for `Point`, so `quickcheck` +// knows how to fuzz the struct. +impl Arbitrary for Point { + fn arbitrary(g: &mut Gen) -> Point { + Point { + x: i32::arbitrary(g), + y: i32::arbitrary(g), + } + } +} + +/// We use `#[ink_e2e::test(runtime)]` here. It doesn't start a node for each +/// test, but instead interacts with an in-process `pallet-revive`. +/// +/// See +/// for more details. +#[ink_e2e::test(runtime, replace_test_attr = "#[quickcheck]")] +async fn fuzzing_works_runtime(val: bool) -> bool { + let mut constructor = FuzzTestingRef::new(val); + let contract = client + .instantiate("fuzz_testing", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + let get = call_builder.get(); + let get_res = client.call(&ink_e2e::bob(), &get).submit().await.unwrap(); + get_res.return_value() == val +} + +/// It's also possible to fuzz with a "real" node as the backend. +/// +/// This means that, by default, for every test run a node process will +/// be spawned. You can work around this by setting the env variable +/// `CONTRACTS_NODE_URL`. But still, interactions with a real node will +/// always be more heavy-weight than "just" interacting with an in-process +/// `pallet-revive`. +#[ink_e2e::test(runtime, replace_test_attr = "#[quickcheck]")] +async fn fuzzing_works_node(val: bool) -> bool { + let mut constructor = FuzzTestingRef::new(val); + let contract = client + .instantiate("fuzz_testing", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + let get = call_builder.get(); + let get_res = client.call(&ink_e2e::bob(), &get).submit().await.unwrap(); + get_res.return_value() == val +} + +#[ink_e2e::test(runtime, replace_test_attr = "#[quickcheck]")] +async fn fuzzing_custom_struct_works(val: Point) -> bool { + ink_e2e::tracing::info!("fuzzing with value {val:?}"); + + let mut constructor = FuzzTestingRef::new(true); + let contract = client + .instantiate("fuzz_testing", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + let get = call_builder.extract_x(val.clone()); + let get_res = client.call(&ink_e2e::bob(), &get).submit().await.unwrap(); + get_res.return_value() == val.x +} \ No newline at end of file diff --git a/integration-tests/public/incrementer/lib.rs b/integration-tests/public/incrementer/lib.rs index ccb4ea093d..53c353765b 100644 --- a/integration-tests/public/incrementer/lib.rs +++ b/integration-tests/public/incrementer/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] #[ink::contract] -mod incrementer { +pub mod incrementer { #[ink(storage)] pub struct Incrementer { value: i32, @@ -28,25 +28,7 @@ mod incrementer { self.value } } - - #[cfg(test)] - mod tests { - use super::*; - - #[ink::test] - fn default_works() { - let contract = Incrementer::new_default(); - assert_eq!(contract.get(), 0); - } - - #[ink::test] - fn it_works() { - let mut contract = Incrementer::new(42); - assert_eq!(contract.get(), 42); - contract.inc(5); - assert_eq!(contract.get(), 47); - contract.inc(-50); - assert_eq!(contract.get(), -3); - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/incrementer/tests.rs b/integration-tests/public/incrementer/tests.rs new file mode 100644 index 0000000000..63036a5d1f --- /dev/null +++ b/integration-tests/public/incrementer/tests.rs @@ -0,0 +1,29 @@ +use super::incrementer::Incrementer; + +#[ink::test] +fn default_works() { + // Given + let contract = Incrementer::new_default(); + + // Then + assert_eq!(contract.get(), 0); +} + +#[ink::test] +fn it_works() { + // Given + let mut contract = Incrementer::new(42); + assert_eq!(contract.get(), 42); + + // When + contract.inc(5); + + // Then + assert_eq!(contract.get(), 47); + + // When + contract.inc(-50); + + // Then + assert_eq!(contract.get(), -3); +} \ No newline at end of file diff --git a/integration-tests/public/lazyvec/lib.rs b/integration-tests/public/lazyvec/lib.rs index 8635334bd8..5b3842ef8a 100755 --- a/integration-tests/public/lazyvec/lib.rs +++ b/integration-tests/public/lazyvec/lib.rs @@ -3,7 +3,7 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] #[ink::contract] -mod lazyvec { +pub mod lazyvec { use ink::{ prelude::vec::Vec, storage::StorageVec, @@ -11,10 +11,10 @@ mod lazyvec { #[ink::storage_item(packed)] pub struct Proposal { - data: Vec, - until: BlockNumber, - approvals: u32, - min_approvals: u32, + pub data: Vec, + pub until: BlockNumber, + pub approvals: u32, + pub min_approvals: u32, } impl Proposal { @@ -87,63 +87,7 @@ mod lazyvec { self.proposals.get(at) } } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink_e2e::ContractsBackend; - - type E2EResult = std::result::Result>; - - #[ink_e2e::test] - async fn create_and_vote(mut client: Client) -> E2EResult<()> { - // given - let mut constructor = LazyVectorRef::default(); - let contract = client - .instantiate("lazyvec", &ink_e2e::alice(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = contract.call_builder::(); - - // when - let create = call_builder.create_proposal(vec![0x41], 15, 1); - let _ = client - .call(&ink_e2e::alice(), &create) - .submit() - .await - .expect("Calling `create_proposal` failed"); - - let approve = call_builder.approve(); - let _ = client - .call(&ink_e2e::alice(), &approve) - .submit() - .await - .expect("Voting failed"); - let _ = client - .call(&ink_e2e::bob(), &approve) - .submit() - .await - .expect("Voting failed"); - - // then - let value = client - .call(&ink_e2e::alice(), &create) - .dry_run() - .await - .expect("create trapped when it shouldn't") - .return_value(); - assert_eq!(value, None); - - let value = client - .call(&ink_e2e::alice(), &call_builder.get(0)) - .dry_run() - .await - .expect("get trapped when it shouldn't") - .return_value(); - assert_eq!(value.unwrap().approvals, 2); - - Ok(()) - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/lazyvec/tests.rs b/integration-tests/public/lazyvec/tests.rs new file mode 100644 index 0000000000..338e949393 --- /dev/null +++ b/integration-tests/public/lazyvec/tests.rs @@ -0,0 +1,57 @@ +use super::lazyvec::{LazyVector, LazyVectorRef}; +use ink_e2e::ContractsBackend; + +type E2EResult = std::result::Result>; + +#[ink_e2e::test] +async fn create_and_vote(mut client: Client) -> E2EResult<()> { + // Given + let mut constructor = LazyVectorRef::default(); + let contract = client + .instantiate("lazyvec", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = contract.call_builder::(); + + // When + let create = call_builder.create_proposal(vec![0x41], 15, 1); + let _ = client + .call(&ink_e2e::alice(), &create) + .submit() + .await + .expect("Calling `create_proposal` failed"); + + let approve = call_builder.approve(); + let _ = client + .call(&ink_e2e::alice(), &approve) + .submit() + .await + .expect("Voting failed"); + let _ = client + .call(&ink_e2e::bob(), &approve) + .submit() + .await + .expect("Voting failed"); + + // Then + let value = client + .call(&ink_e2e::alice(), &create) + .dry_run() + .await + .expect("create trapped when it shouldn't") + .return_value(); + assert_eq!(value, None); + + let value = client + .call(&ink_e2e::alice(), &call_builder.get(0)) + .dry_run() + .await + .expect("get trapped when it shouldn't") + .return_value(); + + // We can access .approvals here because we made the struct fields pub in lib.rs + assert_eq!(value.unwrap().approvals, 2); + + Ok(()) +} \ No newline at end of file diff --git a/integration-tests/public/multi-contract-caller/lib.rs b/integration-tests/public/multi-contract-caller/lib.rs index e86369943c..4441b52c60 100644 --- a/integration-tests/public/multi-contract-caller/lib.rs +++ b/integration-tests/public/multi-contract-caller/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] #[ink::contract] -mod multi_contract_caller { +pub mod multi_contract_caller { use accumulator::AccumulatorRef; use adder::AdderRef; use subber::SubberRef; @@ -117,103 +117,7 @@ mod multi_contract_caller { salt[..4].copy_from_slice(&version); Some(salt) } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink_e2e::ContractsBackend; - - type E2EResult = std::result::Result>; - - #[ink_e2e::test] - async fn e2e_multi_contract_caller(mut client: Client) -> E2EResult<()> { - // given - let accumulator_hash = client - .upload("accumulator", &ink_e2e::alice()) - .submit() - .await - .expect("uploading `accumulator` failed") - .code_hash; - - let adder_hash = client - .upload("adder", &ink_e2e::alice()) - .submit() - .await - .expect("uploading `adder` failed") - .code_hash; - - let subber_hash = client - .upload("subber", &ink_e2e::alice()) - .submit() - .await - .expect("uploading `subber` failed") - .code_hash; - - let mut constructor = MultiContractCallerRef::new( - 1234, // initial value - 1337, // salt - accumulator_hash, - adder_hash, - subber_hash, - ); - - let multi_contract_caller = client - .instantiate("multi_contract_caller", &ink_e2e::alice(), &mut constructor) - .value(100_000_000_000) - .submit() - .await - .expect("instantiate failed"); - let mut call_builder = - multi_contract_caller.call_builder::(); - - // when - let get = call_builder.get(); - let value = client - .call(&ink_e2e::bob(), &get) - .dry_run() - .await? - .return_value(); - assert_eq!(value, 1234); - let change = call_builder.change(6); - let _ = client - .call(&ink_e2e::bob(), &change) - .submit() - .await - .expect("calling `change` failed"); - - // then - let get = call_builder.get(); - let value = client - .call(&ink_e2e::bob(), &get) - .dry_run() - .await? - .return_value(); - assert_eq!(value, 1234 + 6); - - // when - let switch = call_builder.switch(); - let _ = client - .call(&ink_e2e::bob(), &switch) - .submit() - .await - .expect("calling `switch` failed"); - let change = call_builder.change(3); - let _ = client - .call(&ink_e2e::bob(), &change) - .submit() - .await - .expect("calling `change` failed"); - - // then - let get = call_builder.get(); - let value = client - .call(&ink_e2e::bob(), &get) - .dry_run() - .await? - .return_value(); - assert_eq!(value, 1234 + 6 - 3); - - Ok(()) - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/multi-contract-caller/tests.rs b/integration-tests/public/multi-contract-caller/tests.rs new file mode 100644 index 0000000000..3d7d74b956 --- /dev/null +++ b/integration-tests/public/multi-contract-caller/tests.rs @@ -0,0 +1,97 @@ +use super::multi_contract_caller::{MultiContractCaller, MultiContractCallerRef}; +use ink_e2e::ContractsBackend; + +type E2EResult = std::result::Result>; + +#[ink_e2e::test] +async fn e2e_multi_contract_caller(mut client: Client) -> E2EResult<()> { + // Given + let accumulator_hash = client + .upload("accumulator", &ink_e2e::alice()) + .submit() + .await + .expect("uploading `accumulator` failed") + .code_hash; + + let adder_hash = client + .upload("adder", &ink_e2e::alice()) + .submit() + .await + .expect("uploading `adder` failed") + .code_hash; + + let subber_hash = client + .upload("subber", &ink_e2e::alice()) + .submit() + .await + .expect("uploading `subber` failed") + .code_hash; + + let mut constructor = MultiContractCallerRef::new( + 1234, // initial value + 1337, // salt + accumulator_hash, + adder_hash, + subber_hash, + ); + + let multi_contract_caller = client + .instantiate("multi_contract_caller", &ink_e2e::alice(), &mut constructor) + .value(100_000_000_000) + .submit() + .await + .expect("instantiate failed"); + let mut call_builder = + multi_contract_caller.call_builder::(); + + // When + let get = call_builder.get(); + let value = client + .call(&ink_e2e::bob(), &get) + .dry_run() + .await? + .return_value(); + assert_eq!(value, 1234); + + let change = call_builder.change(6); + let _ = client + .call(&ink_e2e::bob(), &change) + .submit() + .await + .expect("calling `change` failed"); + + // Then + let get = call_builder.get(); + let value = client + .call(&ink_e2e::bob(), &get) + .dry_run() + .await? + .return_value(); + assert_eq!(value, 1234 + 6); + + // When + let switch = call_builder.switch(); + let _ = client + .call(&ink_e2e::bob(), &switch) + .submit() + .await + .expect("calling `switch` failed"); + + let change = call_builder.change(3); + let _ = client + .call(&ink_e2e::bob(), &change) + .submit() + .await + .expect("calling `change` failed"); + + // Then + let get = call_builder.get(); + let value = client + .call(&ink_e2e::bob(), &get) + .dry_run() + .await? + .return_value(); + assert_eq!(value, 1234 + 6 - 3); + + Ok(()) +} \ No newline at end of file diff --git a/integration-tests/public/multisig/lib.rs b/integration-tests/public/multisig/lib.rs index 66b5721ed2..361a3ebfee 100755 --- a/integration-tests/public/multisig/lib.rs +++ b/integration-tests/public/multisig/lib.rs @@ -61,7 +61,7 @@ pub use self::multisig::{ }; #[ink::contract] -mod multisig { +pub mod multisig { use ink::{ U256, env::{ @@ -145,11 +145,11 @@ mod multisig { #[ink::storage_item(packed)] pub struct Transactions { /// Just store all transaction ids packed. - transactions: Vec, + pub transactions: Vec, /// We just increment this whenever a new transaction is created. /// We never decrement or defragment. For now, the contract becomes defunct /// when the ids are exhausted. - next_id: TransactionId, + pub next_id: TransactionId, } /// Emitted when an owner confirms a transaction. @@ -235,23 +235,23 @@ mod multisig { pub struct Multisig { /// Every entry in this map represents the confirmation of an owner for a /// transaction. This is effectively a set rather than a map. - confirmations: Mapping<(TransactionId, Address), ()>, + pub confirmations: Mapping<(TransactionId, Address), ()>, /// The amount of confirmations for every transaction. This is a redundant /// information and is kept in order to prevent iterating through the /// confirmation set to check if a transaction is confirmed. - confirmation_count: Mapping, + pub confirmation_count: Mapping, /// Map the transaction id to its not-executed transaction. - transactions: Mapping, + pub transactions: Mapping, /// We need to hold a list of all transactions so that we can clean up storage /// when an owner is removed. - transaction_list: Transactions, + pub transaction_list: Transactions, /// The list is a vector because iterating over it is necessary when cleaning /// up the confirmation set. - owners: Vec
, + pub owners: Vec
, /// Redundant information to speed up the check whether a caller is an owner. - is_owner: Mapping, + pub is_owner: Mapping, /// Minimum number of owners that have to confirm a transaction to be executed. - requirement: u32, + pub requirement: u32, } impl Multisig { @@ -287,74 +287,6 @@ mod multisig { /// # Panics /// /// If the owner already exists. - /// - /// # Examples - /// - /// Since this message must be send by the wallet itself it has to be build as a - /// `Transaction` and dispatched through `submit_transaction` and - /// `invoke_transaction`: - /// ```should_panic - /// use ink::{ - /// env::{ - /// DefaultEnvironment as Env, - /// Environment, - /// call::{ - /// Call, - /// CallParams, - /// ExecutionInput, - /// Selector, - /// utils::ArgumentList, - /// }, - /// }, - /// scale::Encode, - /// selector_bytes, - /// }; - /// use multisig::{ - /// ConfirmationStatus, - /// Transaction, - /// }; - /// - /// // address of an existing `Multisig` contract - /// let wallet_id: ink::Address = [7u8; 20].into(); - /// - /// // first create the transaction that adds `alice` through `add_owner` - /// let alice: ink::Address = [1u8; 20].into(); - /// let add_owner_args = ArgumentList::empty().push_arg(&alice); - /// - /// let transaction_candidate = Transaction { - /// callee: wallet_id, - /// selector: selector_bytes!(Abi::Ink, "add_owner"), - /// input: add_owner_args.encode(), - /// transferred_value: ink::U256::zero(), - /// ref_time_limit: 0, - /// allow_reentry: true, - /// }; - /// - /// // Submit the transaction for confirmation - /// // - /// // Note that the selector bytes of the `submit_transaction` method - /// // are `[86, 244, 13, 223]`. - /// let (id, _status) = ink::env::call::build_call::() - /// .call_type(Call::new(wallet_id)) - /// .ref_time_limit(0) - /// .exec_input( - /// ExecutionInput::new(Selector::new([86, 244, 13, 223])) - /// .push_arg(&transaction_candidate), - /// ) - /// .returns::<(u32, ConfirmationStatus)>() - /// .invoke(); - /// - /// // Wait until all owners have confirmed and then execute the tx. - /// // - /// // Note that the selector bytes of the `invoke_transaction` method - /// // are `[185, 50, 225, 236]`. - /// ink::env::call::build_call::() - /// .call_type(Call::new(wallet_id)) - /// .ref_time_limit(0) - /// .exec_input(ExecutionInput::new(Selector::new([185, 50, 225, 236])).push_arg(&id)) - /// .returns::<()>() - /// .invoke(); - /// ``` #[ink(message)] pub fn add_owner(&mut self, new_owner: Address) { self.ensure_from_wallet(); @@ -727,381 +659,7 @@ mod multisig { fn ensure_requirement_is_valid(owners: u32, requirement: u32) { assert!(0 < requirement && requirement <= owners && owners <= MAX_OWNERS); } - - #[cfg(test)] - mod tests { - use super::*; - use ink::env::{ - call::utils::ArgumentList, - test, - }; - - const WALLET: [u8; 20] = [7; 20]; - - impl Transaction { - fn change_requirement(requirement: u32) -> Self { - use ink::scale::Encode; - let call_args = ArgumentList::empty().push_arg(&requirement); - - // Multisig::change_requirement() - Self { - callee: Address::from(WALLET), - selector: ink::selector_bytes!(Abi::Ink, "change_requirement"), - input: call_args.encode(), - transferred_value: U256::zero(), - ref_time_limit: 1000000, - allow_reentry: false, - } - } - } - - fn set_caller(sender: Address) { - ink::env::test::set_caller(sender); - } - - fn set_from_wallet() { - let callee = Address::from(WALLET); - set_caller(callee); - } - - fn set_from_owner() { - let accounts = default_accounts(); - set_caller(accounts.alice); - } - - fn set_from_no_owner() { - let accounts = default_accounts(); - set_caller(accounts.django); - } - - fn default_accounts() -> test::DefaultAccounts { - ink::env::test::default_accounts() - } - - fn build_contract() -> Multisig { - // Set the contract's address as `WALLET`. - let callee: Address = Address::from(WALLET); - ink::env::test::set_callee(callee); - - let accounts = default_accounts(); - let owners = vec![accounts.alice, accounts.bob, accounts.eve]; - Multisig::new(2, owners) - } - - fn submit_transaction() -> Multisig { - let mut contract = build_contract(); - let accounts = default_accounts(); - set_from_owner(); - contract.submit_transaction(Transaction::change_requirement(1)); - assert_eq!(contract.transaction_list.transactions.len(), 1); - assert_eq!(test::recorded_events().len(), 2); - let transaction = contract.transactions.get(0).unwrap(); - assert_eq!(transaction, Transaction::change_requirement(1)); - contract.confirmations.get((0, accounts.alice)).unwrap(); - assert_eq!(contract.confirmation_count.get(0).unwrap(), 1); - contract - } - - #[ink::test] - fn construction_works() { - let accounts = default_accounts(); - let owners = [accounts.alice, accounts.bob, accounts.eve]; - let contract = build_contract(); - - assert_eq!(contract.owners.len(), 3); - assert_eq!(contract.requirement, 2); - use ink::prelude::collections::HashSet; - assert_eq!( - HashSet::<&Address>::from_iter(contract.owners.iter()), - HashSet::from_iter(owners.iter()), - ); - assert!(contract.is_owner.contains(accounts.alice)); - assert!(contract.is_owner.contains(accounts.bob)); - assert!(contract.is_owner.contains(accounts.eve)); - assert!(!contract.is_owner.contains(accounts.charlie)); - assert!(!contract.is_owner.contains(accounts.django)); - assert!(!contract.is_owner.contains(accounts.frank)); - assert_eq!(contract.transaction_list.transactions.len(), 0); - } - - #[ink::test] - #[should_panic] - fn empty_owner_construction_fails() { - Multisig::new(0, vec![]); - } - - #[ink::test] - #[should_panic] - fn zero_requirement_construction_fails() { - let accounts = default_accounts(); - Multisig::new(0, vec![accounts.alice, accounts.bob]); - } - - #[ink::test] - #[should_panic] - fn too_large_requirement_construction_fails() { - let accounts = default_accounts(); - Multisig::new(3, vec![accounts.alice, accounts.bob]); - } - - #[ink::test] - fn add_owner_works() { - let accounts = default_accounts(); - let mut contract = build_contract(); - set_from_wallet(); - let owners = contract.owners.len(); - contract.add_owner(accounts.frank); - assert_eq!(contract.owners.len(), owners + 1); - assert!(contract.is_owner.contains(accounts.frank)); - assert_eq!(test::recorded_events().len(), 1); - } - - #[ink::test] - #[should_panic] - fn add_existing_owner_fails() { - let accounts = default_accounts(); - let mut contract = build_contract(); - set_from_wallet(); - contract.add_owner(accounts.bob); - } - - #[ink::test] - #[should_panic] - fn add_owner_permission_denied() { - let accounts = default_accounts(); - let mut contract = build_contract(); - set_from_owner(); - contract.add_owner(accounts.frank); - } - - #[ink::test] - fn remove_owner_works() { - let accounts = default_accounts(); - let mut contract = build_contract(); - set_from_wallet(); - let owners = contract.owners.len(); - contract.remove_owner(accounts.alice); - assert_eq!(contract.owners.len(), owners - 1); - assert!(!contract.is_owner.contains(accounts.alice)); - assert_eq!(test::recorded_events().len(), 1); - } - - #[ink::test] - #[should_panic] - fn remove_owner_nonexisting_fails() { - let accounts = default_accounts(); - let mut contract = build_contract(); - set_from_wallet(); - contract.remove_owner(accounts.django); - } - - #[ink::test] - #[should_panic] - fn remove_owner_permission_denied() { - let accounts = default_accounts(); - let mut contract = build_contract(); - set_from_owner(); - contract.remove_owner(accounts.alice); - } - - #[ink::test] - fn replace_owner_works() { - let accounts = default_accounts(); - let mut contract = build_contract(); - set_from_wallet(); - let owners = contract.owners.len(); - contract.replace_owner(accounts.alice, accounts.django); - assert_eq!(contract.owners.len(), owners); - assert!(!contract.is_owner.contains(accounts.alice)); - assert!(contract.is_owner.contains(accounts.django)); - assert_eq!(test::recorded_events().len(), 2); - } - - #[ink::test] - #[should_panic] - fn replace_owner_existing_fails() { - let accounts = default_accounts(); - let mut contract = build_contract(); - set_from_wallet(); - contract.replace_owner(accounts.alice, accounts.bob); - } - - #[ink::test] - #[should_panic] - fn replace_owner_nonexisting_fails() { - let accounts = default_accounts(); - let mut contract = build_contract(); - set_from_wallet(); - contract.replace_owner(accounts.django, accounts.frank); - } - - #[ink::test] - #[should_panic] - fn replace_owner_permission_denied() { - let accounts = default_accounts(); - let mut contract = build_contract(); - set_from_owner(); - contract.replace_owner(accounts.alice, accounts.django); - } - - #[ink::test] - fn change_requirement_works() { - let mut contract = build_contract(); - assert_eq!(contract.requirement, 2); - set_from_wallet(); - contract.change_requirement(3); - assert_eq!(contract.requirement, 3); - assert_eq!(test::recorded_events().len(), 1); - } - - #[ink::test] - #[should_panic] - fn change_requirement_too_high() { - let mut contract = build_contract(); - set_from_wallet(); - contract.change_requirement(4); - } - - #[ink::test] - #[should_panic] - fn change_requirement_zero_fails() { - let mut contract = build_contract(); - set_from_wallet(); - contract.change_requirement(0); - } - - #[ink::test] - fn submit_transaction_works() { - submit_transaction(); - } - - #[ink::test] - #[should_panic] - fn submit_transaction_no_owner_fails() { - let mut contract = build_contract(); - set_from_no_owner(); - contract.submit_transaction(Transaction::change_requirement(1)); - } - - #[ink::test] - #[should_panic] - fn submit_transaction_wallet_fails() { - let mut contract = build_contract(); - set_from_wallet(); - contract.submit_transaction(Transaction::change_requirement(1)); - } - - #[ink::test] - fn cancel_transaction_works() { - let mut contract = submit_transaction(); - set_from_wallet(); - contract.cancel_transaction(0); - assert_eq!(contract.transaction_list.transactions.len(), 0); - assert_eq!(test::recorded_events().len(), 3); - } - - #[ink::test] - fn cancel_transaction_nonexisting() { - let mut contract = submit_transaction(); - set_from_wallet(); - contract.cancel_transaction(1); - assert_eq!(contract.transaction_list.transactions.len(), 1); - assert_eq!(test::recorded_events().len(), 2); - } - - #[ink::test] - #[should_panic] - fn cancel_transaction_no_permission() { - let mut contract = submit_transaction(); - contract.cancel_transaction(0); - } - - #[ink::test] - fn confirm_transaction_works() { - let mut contract = submit_transaction(); - let accounts = default_accounts(); - set_caller(accounts.bob); - contract.confirm_transaction(0); - assert_eq!(test::recorded_events().len(), 3); - contract.confirmations.get((0, accounts.bob)).unwrap(); - assert_eq!(contract.confirmation_count.get(0).unwrap(), 2); - } - - #[ink::test] - fn revoke_confirmations() { - // given - let mut contract = submit_transaction(); - let accounts = default_accounts(); - // Confirm by Bob - set_caller(accounts.bob); - contract.confirm_transaction(0); - // Confirm by Eve - set_caller(accounts.eve); - contract.confirm_transaction(0); - assert_eq!(contract.confirmation_count.get(0).unwrap(), 3); - // Revoke from Eve - contract.revoke_confirmation(0); - assert_eq!(contract.confirmation_count.get(0).unwrap(), 2); - // Revoke from Bob - set_caller(accounts.bob); - contract.revoke_confirmation(0); - assert_eq!(contract.confirmation_count.get(0).unwrap(), 1); - } - - #[ink::test] - fn confirm_transaction_already_confirmed() { - let mut contract = submit_transaction(); - let accounts = default_accounts(); - set_caller(accounts.alice); - contract.confirm_transaction(0); - assert_eq!(test::recorded_events().len(), 2); - contract.confirmations.get((0, accounts.alice)).unwrap(); - assert_eq!(contract.confirmation_count.get(0).unwrap(), 1); - } - - #[ink::test] - #[should_panic] - fn confirm_transaction_no_owner_fail() { - let mut contract = submit_transaction(); - set_from_no_owner(); - contract.confirm_transaction(0); - } - - #[ink::test] - fn revoke_transaction_works() { - let mut contract = submit_transaction(); - let accounts = default_accounts(); - set_caller(accounts.alice); - contract.revoke_confirmation(0); - assert_eq!(test::recorded_events().len(), 3); - assert!(!contract.confirmations.contains((0, accounts.alice))); - assert_eq!(contract.confirmation_count.get(0).unwrap(), 0); - } - - #[ink::test] - fn revoke_transaction_no_confirmer() { - let mut contract = submit_transaction(); - let accounts = default_accounts(); - set_caller(accounts.bob); - contract.revoke_confirmation(0); - assert_eq!(test::recorded_events().len(), 2); - assert!(contract.confirmations.contains((0, accounts.alice))); - assert_eq!(contract.confirmation_count.get(0).unwrap(), 1); - } - - #[ink::test] - #[should_panic] - fn revoke_transaction_no_owner_fail() { - let mut contract = submit_transaction(); - let accounts = default_accounts(); - set_caller(accounts.django); - contract.revoke_confirmation(0); - } - - #[ink::test] - fn execute_transaction_works() { - // Execution of calls is currently unsupported in off-chain test. - // Calling `execute_transaction` panics in any case. - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/multisig/tests.rs b/integration-tests/public/multisig/tests.rs new file mode 100644 index 0000000000..5907caee04 --- /dev/null +++ b/integration-tests/public/multisig/tests.rs @@ -0,0 +1,374 @@ +use super::multisig::*; +use ink::env::{ + call::utils::ArgumentList, + test, +}; +use ink::{Address, U256}; + +const WALLET: [u8; 20] = [7; 20]; + +impl Transaction { + fn change_requirement(requirement: u32) -> Self { + use ink::scale::Encode; + let call_args = ArgumentList::empty().push_arg(&requirement); + + // Multisig::change_requirement() + Self { + callee: Address::from(WALLET), + selector: ink::selector_bytes!(Abi::Ink, "change_requirement"), + input: call_args.encode(), + transferred_value: U256::zero(), + ref_time_limit: 1000000, + allow_reentry: false, + } + } +} + +fn set_caller(sender: Address) { + ink::env::test::set_caller(sender); +} + +fn set_from_wallet() { + let callee = Address::from(WALLET); + set_caller(callee); +} + +fn set_from_owner() { + let accounts = default_accounts(); + set_caller(accounts.alice); +} + +fn set_from_no_owner() { + let accounts = default_accounts(); + set_caller(accounts.django); +} + +fn default_accounts() -> test::DefaultAccounts { + ink::env::test::default_accounts() +} + +fn build_contract() -> Multisig { + // Set the contract's address as `WALLET`. + let callee: Address = Address::from(WALLET); + ink::env::test::set_callee(callee); + + let accounts = default_accounts(); + let owners = vec![accounts.alice, accounts.bob, accounts.eve]; + Multisig::new(2, owners) +} + +fn submit_transaction() -> Multisig { + let mut contract = build_contract(); + let accounts = default_accounts(); + set_from_owner(); + contract.submit_transaction(Transaction::change_requirement(1)); + assert_eq!(contract.transaction_list.transactions.len(), 1); + assert_eq!(test::recorded_events().len(), 2); + let transaction = contract.transactions.get(0).unwrap(); + assert_eq!(transaction, Transaction::change_requirement(1)); + contract.confirmations.get((0, accounts.alice)).unwrap(); + assert_eq!(contract.confirmation_count.get(0).unwrap(), 1); + contract +} + +#[ink::test] +fn construction_works() { + let accounts = default_accounts(); + let owners = [accounts.alice, accounts.bob, accounts.eve]; + let contract = build_contract(); + + assert_eq!(contract.owners.len(), 3); + assert_eq!(contract.requirement, 2); + use ink::prelude::collections::HashSet; + assert_eq!( + HashSet::<&Address>::from_iter(contract.owners.iter()), + HashSet::from_iter(owners.iter()), + ); + assert!(contract.is_owner.contains(accounts.alice)); + assert!(contract.is_owner.contains(accounts.bob)); + assert!(contract.is_owner.contains(accounts.eve)); + assert!(!contract.is_owner.contains(accounts.charlie)); + assert!(!contract.is_owner.contains(accounts.django)); + assert!(!contract.is_owner.contains(accounts.frank)); + assert_eq!(contract.transaction_list.transactions.len(), 0); +} + +#[ink::test] +#[should_panic] +fn empty_owner_construction_fails() { + Multisig::new(0, vec![]); +} + +#[ink::test] +#[should_panic] +fn zero_requirement_construction_fails() { + let accounts = default_accounts(); + Multisig::new(0, vec![accounts.alice, accounts.bob]); +} + +#[ink::test] +#[should_panic] +fn too_large_requirement_construction_fails() { + let accounts = default_accounts(); + Multisig::new(3, vec![accounts.alice, accounts.bob]); +} + +#[ink::test] +fn add_owner_works() { + let accounts = default_accounts(); + let mut contract = build_contract(); + set_from_wallet(); + let owners = contract.owners.len(); + contract.add_owner(accounts.frank); + assert_eq!(contract.owners.len(), owners + 1); + assert!(contract.is_owner.contains(accounts.frank)); + assert_eq!(test::recorded_events().len(), 1); +} + +#[ink::test] +#[should_panic] +fn add_existing_owner_fails() { + let accounts = default_accounts(); + let mut contract = build_contract(); + set_from_wallet(); + contract.add_owner(accounts.bob); +} + +#[ink::test] +#[should_panic] +fn add_owner_permission_denied() { + let accounts = default_accounts(); + let mut contract = build_contract(); + set_from_owner(); + contract.add_owner(accounts.frank); +} + +#[ink::test] +fn remove_owner_works() { + let accounts = default_accounts(); + let mut contract = build_contract(); + set_from_wallet(); + let owners = contract.owners.len(); + contract.remove_owner(accounts.alice); + assert_eq!(contract.owners.len(), owners - 1); + assert!(!contract.is_owner.contains(accounts.alice)); + assert_eq!(test::recorded_events().len(), 1); +} + +#[ink::test] +#[should_panic] +fn remove_owner_nonexisting_fails() { + let accounts = default_accounts(); + let mut contract = build_contract(); + set_from_wallet(); + contract.remove_owner(accounts.django); +} + +#[ink::test] +#[should_panic] +fn remove_owner_permission_denied() { + let accounts = default_accounts(); + let mut contract = build_contract(); + set_from_owner(); + contract.remove_owner(accounts.alice); +} + +#[ink::test] +fn replace_owner_works() { + let accounts = default_accounts(); + let mut contract = build_contract(); + set_from_wallet(); + let owners = contract.owners.len(); + contract.replace_owner(accounts.alice, accounts.django); + assert_eq!(contract.owners.len(), owners); + assert!(!contract.is_owner.contains(accounts.alice)); + assert!(contract.is_owner.contains(accounts.django)); + assert_eq!(test::recorded_events().len(), 2); +} + +#[ink::test] +#[should_panic] +fn replace_owner_existing_fails() { + let accounts = default_accounts(); + let mut contract = build_contract(); + set_from_wallet(); + contract.replace_owner(accounts.alice, accounts.bob); +} + +#[ink::test] +#[should_panic] +fn replace_owner_nonexisting_fails() { + let accounts = default_accounts(); + let mut contract = build_contract(); + set_from_wallet(); + contract.replace_owner(accounts.django, accounts.frank); +} + +#[ink::test] +#[should_panic] +fn replace_owner_permission_denied() { + let accounts = default_accounts(); + let mut contract = build_contract(); + set_from_owner(); + contract.replace_owner(accounts.alice, accounts.django); +} + +#[ink::test] +fn change_requirement_works() { + let mut contract = build_contract(); + assert_eq!(contract.requirement, 2); + set_from_wallet(); + contract.change_requirement(3); + assert_eq!(contract.requirement, 3); + assert_eq!(test::recorded_events().len(), 1); +} + +#[ink::test] +#[should_panic] +fn change_requirement_too_high() { + let mut contract = build_contract(); + set_from_wallet(); + contract.change_requirement(4); +} + +#[ink::test] +#[should_panic] +fn change_requirement_zero_fails() { + let mut contract = build_contract(); + set_from_wallet(); + contract.change_requirement(0); +} + +#[ink::test] +fn submit_transaction_works() { + submit_transaction(); +} + +#[ink::test] +#[should_panic] +fn submit_transaction_no_owner_fails() { + let mut contract = build_contract(); + set_from_no_owner(); + contract.submit_transaction(Transaction::change_requirement(1)); +} + +#[ink::test] +#[should_panic] +fn submit_transaction_wallet_fails() { + let mut contract = build_contract(); + set_from_wallet(); + contract.submit_transaction(Transaction::change_requirement(1)); +} + +#[ink::test] +fn cancel_transaction_works() { + let mut contract = submit_transaction(); + set_from_wallet(); + contract.cancel_transaction(0); + assert_eq!(contract.transaction_list.transactions.len(), 0); + assert_eq!(test::recorded_events().len(), 3); +} + +#[ink::test] +fn cancel_transaction_nonexisting() { + let mut contract = submit_transaction(); + set_from_wallet(); + contract.cancel_transaction(1); + assert_eq!(contract.transaction_list.transactions.len(), 1); + assert_eq!(test::recorded_events().len(), 2); +} + +#[ink::test] +#[should_panic] +fn cancel_transaction_no_permission() { + let mut contract = submit_transaction(); + contract.cancel_transaction(0); +} + +#[ink::test] +fn confirm_transaction_works() { + let mut contract = submit_transaction(); + let accounts = default_accounts(); + set_caller(accounts.bob); + contract.confirm_transaction(0); + assert_eq!(test::recorded_events().len(), 3); + contract.confirmations.get((0, accounts.bob)).unwrap(); + assert_eq!(contract.confirmation_count.get(0).unwrap(), 2); +} + +#[ink::test] +fn revoke_confirmations() { + // given + let mut contract = submit_transaction(); + let accounts = default_accounts(); + // Confirm by Bob + set_caller(accounts.bob); + contract.confirm_transaction(0); + // Confirm by Eve + set_caller(accounts.eve); + contract.confirm_transaction(0); + assert_eq!(contract.confirmation_count.get(0).unwrap(), 3); + // Revoke from Eve + contract.revoke_confirmation(0); + assert_eq!(contract.confirmation_count.get(0).unwrap(), 2); + // Revoke from Bob + set_caller(accounts.bob); + contract.revoke_confirmation(0); + assert_eq!(contract.confirmation_count.get(0).unwrap(), 1); +} + +#[ink::test] +fn confirm_transaction_already_confirmed() { + let mut contract = submit_transaction(); + let accounts = default_accounts(); + set_caller(accounts.alice); + contract.confirm_transaction(0); + assert_eq!(test::recorded_events().len(), 2); + contract.confirmations.get((0, accounts.alice)).unwrap(); + assert_eq!(contract.confirmation_count.get(0).unwrap(), 1); +} + +#[ink::test] +#[should_panic] +fn confirm_transaction_no_owner_fail() { + let mut contract = submit_transaction(); + set_from_no_owner(); + contract.confirm_transaction(0); +} + +#[ink::test] +fn revoke_transaction_works() { + let mut contract = submit_transaction(); + let accounts = default_accounts(); + set_caller(accounts.alice); + contract.revoke_confirmation(0); + assert_eq!(test::recorded_events().len(), 3); + assert!(!contract.confirmations.contains((0, accounts.alice))); + assert_eq!(contract.confirmation_count.get(0).unwrap(), 0); +} + +#[ink::test] +fn revoke_transaction_no_confirmer() { + let mut contract = submit_transaction(); + let accounts = default_accounts(); + set_caller(accounts.bob); + contract.revoke_confirmation(0); + assert_eq!(test::recorded_events().len(), 2); + assert!(contract.confirmations.contains((0, accounts.alice))); + assert_eq!(contract.confirmation_count.get(0).unwrap(), 1); +} + +#[ink::test] +#[should_panic] +fn revoke_transaction_no_owner_fail() { + let mut contract = submit_transaction(); + let accounts = default_accounts(); + set_caller(accounts.django); + contract.revoke_confirmation(0); +} + +#[ink::test] +fn execute_transaction_works() { + // Execution of calls is currently unsupported in off-chain test. + // Calling `execute_transaction` panics in any case. +} \ No newline at end of file diff --git a/integration-tests/public/payment-channel/Cargo.toml b/integration-tests/public/payment-channel/Cargo.toml index 98d68ed38c..5b0a5ab881 100755 --- a/integration-tests/public/payment-channel/Cargo.toml +++ b/integration-tests/public/payment-channel/Cargo.toml @@ -1,16 +1,17 @@ [package] -name = "payment_channel" +name = "payment-channel" version = "6.0.0-beta.1" authors = ["Use Ink "] edition = "2024" publish = false [dependencies] -ink = { path = "../../../crates/ink", default-features = false, features = ["unstable-hostfn"] } -sp-core = { version = "38.0.0", default-features = false } +ink = { path = "../../../crates/ink", default-features = false } [dev-dependencies] -hex-literal = "1" +ink_e2e = { path = "../../../crates/e2e" } +hex-literal = "0.4" +sp-core = { version = "34.0.0", default-features = false, features = ["full_crypto"] } [lib] path = "lib.rs" @@ -20,8 +21,8 @@ default = ["std"] std = [ "ink/std", ] - ink-as-dependency = [] +e2e-tests = [] [package.metadata.ink-lang] -abi = "ink" +abi = "ink" \ No newline at end of file diff --git a/integration-tests/public/payment-channel/lib.rs b/integration-tests/public/payment-channel/lib.rs index 5fe1ae3516..43cb7000a6 100755 --- a/integration-tests/public/payment-channel/lib.rs +++ b/integration-tests/public/payment-channel/lib.rs @@ -40,7 +40,7 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] #[ink::contract] -mod payment_channel { +pub mod payment_channel { use ink::U256; /// Struct for storing the payment channel details. @@ -125,7 +125,7 @@ mod payment_channel { } /// We split this out in order to make testing `close` simpler. - fn close_inner(&mut self, amount: U256, signature: [u8; 65]) -> Result<()> { + pub fn close_inner(&mut self, amount: U256, signature: [u8; 65]) -> Result<()> { if self.env().caller() != self.recipient { return Err(Error::CallerIsNotRecipient) } @@ -283,307 +283,7 @@ mod payment_channel { == ink::primitives::AccountIdMapper::to_address(&signature_account_id) } } - - #[cfg(test)] - mod tests { - use super::*; - - use hex_literal; - use sp_core::{ - Encode, - Pair, - }; - - fn default_accounts() -> ink::env::test::DefaultAccounts { - ink::env::test::default_accounts() - } - - fn set_next_caller(caller: Address) { - ink::env::test::set_caller(caller); - } - - fn set_contract_balance(addr: Address, balance: U256) { - ink::env::test::set_contract_balance(addr, balance); - } - - fn get_contract_balance(addr: Address) -> U256 { - ink::env::test::get_contract_balance::(addr) - .expect("Cannot get contract balance") - } - - fn advance_block() { - ink::env::test::advance_block::(); - } - - fn get_current_time() -> Timestamp { - let since_the_epoch = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .expect("Time went backwards"); - since_the_epoch.as_secs() - + since_the_epoch.subsec_nanos() as u64 / 1_000_000_000 - } - - fn get_dan() -> Address { - // Use Dan's seed - // `subkey inspect //Dan --scheme Ecdsa --output-type json | jq .secretSeed` - let seed = hex_literal::hex!( - "c31fa562972de437802e0df146b16146349590b444db41f7e3eb9deedeee6f64" - ); - let pair = sp_core::ecdsa::Pair::from_seed(&seed); - let pub_key = pair.public(); - let compressed_pub_key: [u8; 33] = pub_key.encode()[..] - .try_into() - .expect("slice with incorrect length"); - let mut account_id = [0; 32]; - ::hash( - &compressed_pub_key, - &mut account_id, - ); - ink::primitives::AccountIdMapper::to_address(&account_id) - } - - fn contract_id() -> Address { - let accounts = default_accounts(); - let contract_id = accounts.charlie; - ink::env::test::set_callee(contract_id); - contract_id - } - - fn sign(contract_id: Address, amount: U256) -> [u8; 65] { - let encodable = (contract_id, amount); - let mut hash = - ::Type::default(); // 256-bit buffer - ink::env::hash_encoded::(&encodable, &mut hash); - - // Use Dan's seed - // `subkey inspect //Dan --scheme Ecdsa --output-type json | jq .secretSeed` - let seed = hex_literal::hex!( - "c31fa562972de437802e0df146b16146349590b444db41f7e3eb9deedeee6f64" - ); - let pair = sp_core::ecdsa::Pair::from_seed(&seed); - - let signature = pair.sign_prehashed(&hash); - signature.0 - } - - #[ink::test] - fn test_deposit() { - // given - let accounts = default_accounts(); - let initial_balance = 10_000.into(); - let close_duration = 360_000; - let mock_deposit_value = 1_000.into(); - set_contract_balance(accounts.alice, initial_balance); - set_contract_balance(accounts.bob, initial_balance); - - // when - // Push the new execution context with Alice as the caller and - // the `mock_deposit_value` as the value deposited. - // Note: Currently there is no way to transfer funds to the contract. - set_next_caller(accounts.alice); - let payment_channel = PaymentChannel::new(accounts.bob, close_duration); - let contract_id = contract_id(); - set_contract_balance(contract_id, mock_deposit_value); - - // then - assert_eq!(payment_channel.get_balance(), mock_deposit_value); - } - - #[ink::test] - fn test_close() { - // given - let accounts = default_accounts(); - let dan = get_dan(); - let close_duration = 360_000; - let mock_deposit_value = 1_000.into(); - let amount = 500.into(); - let initial_balance = 10_000.into(); - set_contract_balance(accounts.alice, initial_balance); - set_contract_balance(dan, initial_balance); - - // when - set_next_caller(accounts.alice); - let mut payment_channel = PaymentChannel::new(dan, close_duration); - let contract_id = contract_id(); - set_contract_balance(contract_id, mock_deposit_value); - set_next_caller(dan); - let signature = sign(contract_id, amount); - - // then - let should_close = move || payment_channel.close(amount, signature).unwrap(); - ink::env::test::assert_contract_termination::( - should_close, - accounts.alice, - amount, - ); - assert_eq!(get_contract_balance(dan), initial_balance + amount); - } - - #[ink::test] - fn close_fails_invalid_signature() { - // given - let accounts = default_accounts(); - let dan = get_dan(); - let mock_deposit_value = 1_000.into(); - let close_duration = 360_000; - let amount = 400.into(); - let unexpected_amount = amount + U256::from(1); - let initial_balance = 10_000.into(); - set_contract_balance(accounts.alice, initial_balance); - set_contract_balance(dan, initial_balance); - - // when - set_next_caller(accounts.alice); - let mut payment_channel = PaymentChannel::new(dan, close_duration); - let contract_id = contract_id(); - set_contract_balance(contract_id, mock_deposit_value); - set_next_caller(dan); - let signature = sign(contract_id, amount); - - // then - let res = payment_channel.close_inner(unexpected_amount, signature); - assert!(res.is_err(), "Expected an error, got {res:?} instead."); - assert_eq!(res.unwrap_err(), Error::InvalidSignature,); - } - - #[ink::test] - fn test_withdraw() { - // given - let accounts = default_accounts(); - let dan = get_dan(); - let initial_balance = 10_000.into(); - let mock_deposit_value = 1_000.into(); - let close_duration = 360_000; - let amount = 500.into(); - set_contract_balance(accounts.alice, initial_balance); - set_contract_balance(dan, initial_balance); - - // when - set_next_caller(accounts.alice); - let mut payment_channel = PaymentChannel::new(dan, close_duration); - let contract_id = contract_id(); - set_contract_balance(contract_id, mock_deposit_value); - - set_next_caller(dan); - let signature = sign(contract_id, amount); - payment_channel - .withdraw(amount, signature) - .expect("withdraw failed"); - - // then - assert_eq!(payment_channel.get_balance(), amount); - assert_eq!(get_contract_balance(dan), initial_balance + amount); - } - - #[ink::test] - fn withdraw_fails_invalid_signature() { - // given - let accounts = default_accounts(); - let dan = get_dan(); - let initial_balance = 10_000.into(); - let close_duration = 360_000; - let amount = 400.into(); - let unexpected_amount = amount + U256::from(1); - let mock_deposit_value = 1_000.into(); - set_contract_balance(accounts.alice, initial_balance); - set_contract_balance(dan, initial_balance); - - // when - set_next_caller(accounts.alice); - let mut payment_channel = PaymentChannel::new(dan, close_duration); - let contract_id = contract_id(); - set_contract_balance(contract_id, mock_deposit_value); - set_next_caller(dan); - let signature = sign(contract_id, amount); - - // then - let res = payment_channel.withdraw(unexpected_amount, signature); - assert!(res.is_err(), "Expected an error, got {res:?} instead."); - assert_eq!(res.unwrap_err(), Error::InvalidSignature,); - } - - #[ink::test] - fn test_start_sender_close() { - // given - let accounts = default_accounts(); - let initial_balance = 10_000.into(); - let mock_deposit_value = 1_000.into(); - let close_duration = 1; - set_contract_balance(accounts.alice, initial_balance); - set_contract_balance(accounts.bob, initial_balance); - - // when - set_next_caller(accounts.alice); - let mut payment_channel = PaymentChannel::new(accounts.bob, close_duration); - let contract_id = contract_id(); - set_contract_balance(contract_id, mock_deposit_value); - - payment_channel - .start_sender_close() - .expect("start_sender_close failed"); - advance_block(); - - // then - let now = get_current_time(); - assert!(now > payment_channel.get_expiration().unwrap()); - } - - #[ink::test] - fn test_claim_timeout() { - // given - let accounts = default_accounts(); - let initial_balance = 10_000.into(); - let close_duration = 1; - let mock_deposit_value = 1_000.into(); - set_contract_balance(accounts.alice, initial_balance); - set_contract_balance(accounts.bob, initial_balance); - - // when - set_next_caller(accounts.alice); - let contract_id = contract_id(); - let mut payment_channel = PaymentChannel::new(accounts.bob, close_duration); - set_contract_balance(contract_id, mock_deposit_value); - - payment_channel - .start_sender_close() - .expect("start_sender_close failed"); - advance_block(); - - // then - let should_close = move || payment_channel.claim_timeout().unwrap(); - ink::env::test::assert_contract_termination::( - should_close, - accounts.alice, - mock_deposit_value, - ); - assert_eq!( - get_contract_balance(accounts.alice), - initial_balance + mock_deposit_value - ); - } - - #[ink::test] - fn test_getters() { - // given - let accounts = default_accounts(); - let initial_balance = 10_000.into(); - let mock_deposit_value = 1_000.into(); - let close_duration = 360_000; - set_contract_balance(accounts.alice, initial_balance); - set_contract_balance(accounts.bob, initial_balance); - - // when - set_next_caller(accounts.alice); - let contract_id = contract_id(); - let payment_channel = PaymentChannel::new(accounts.bob, close_duration); - set_contract_balance(contract_id, mock_deposit_value); - - // then - assert_eq!(payment_channel.get_sender(), accounts.alice); - assert_eq!(payment_channel.get_recipient(), accounts.bob); - assert_eq!(payment_channel.get_balance(), mock_deposit_value); - assert_eq!(payment_channel.get_close_duration(), close_duration); - assert_eq!(payment_channel.get_withdrawn(), U256::zero()); - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/payment-channel/tests.rs b/integration-tests/public/payment-channel/tests.rs new file mode 100644 index 0000000000..4866636a7f --- /dev/null +++ b/integration-tests/public/payment-channel/tests.rs @@ -0,0 +1,301 @@ +use super::payment_channel::*; +use ink::U256; +use ink::primitives::AccountId as Address; + +use hex_literal; +use sp_core::{ + Encode, + Pair, +}; + +fn default_accounts() -> ink::env::test::DefaultAccounts { + ink::env::test::default_accounts() +} + +fn set_next_caller(caller: Address) { + ink::env::test::set_caller(caller); +} + +fn set_contract_balance(addr: Address, balance: U256) { + ink::env::test::set_contract_balance(addr, balance); +} + +fn get_contract_balance(addr: Address) -> U256 { + ink::env::test::get_contract_balance::(addr) + .expect("Cannot get contract balance") +} + +fn advance_block() { + ink::env::test::advance_block::(); +} + +fn get_current_time() -> u64 { + let since_the_epoch = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("Time went backwards"); + since_the_epoch.as_secs() + + since_the_epoch.subsec_nanos() as u64 / 1_000_000_000 +} + +fn get_dan() -> Address { + // Use Dan's seed + // `subkey inspect //Dan --scheme Ecdsa --output-type json | jq .secretSeed` + let seed = hex_literal::hex!( + "c31fa562972de437802e0df146b16146349590b444db41f7e3eb9deedeee6f64" + ); + let pair = sp_core::ecdsa::Pair::from_seed(&seed); + let pub_key = pair.public(); + let compressed_pub_key: [u8; 33] = pub_key.encode()[..] + .try_into() + .expect("slice with incorrect length"); + let mut account_id = [0; 32]; + ::hash( + &compressed_pub_key, + &mut account_id, + ); + ink::primitives::AccountIdMapper::to_address(&account_id) +} + +fn contract_id() -> Address { + let accounts = default_accounts(); + let contract_id = accounts.charlie; + ink::env::test::set_callee(contract_id); + contract_id +} + +fn sign(contract_id: Address, amount: U256) -> [u8; 65] { + let encodable = (contract_id, amount); + let mut hash = + ::Type::default(); // 256-bit buffer + ink::env::hash_encoded::(&encodable, &mut hash); + + // Use Dan's seed + // `subkey inspect //Dan --scheme Ecdsa --output-type json | jq .secretSeed` + let seed = hex_literal::hex!( + "c31fa562972de437802e0df146b16146349590b444db41f7e3eb9deedeee6f64" + ); + let pair = sp_core::ecdsa::Pair::from_seed(&seed); + + let signature = pair.sign_prehashed(&hash); + signature.0 +} + +#[ink::test] +fn test_deposit() { + // given + let accounts = default_accounts(); + let initial_balance = 10_000.into(); + let close_duration = 360_000; + let mock_deposit_value = 1_000.into(); + set_contract_balance(accounts.alice, initial_balance); + set_contract_balance(accounts.bob, initial_balance); + + // when + // Push the new execution context with Alice as the caller and + // the `mock_deposit_value` as the value deposited. + // Note: Currently there is no way to transfer funds to the contract. + set_next_caller(accounts.alice); + let payment_channel = PaymentChannel::new(accounts.bob, close_duration); + let contract_id = contract_id(); + set_contract_balance(contract_id, mock_deposit_value); + + // then + assert_eq!(payment_channel.get_balance(), mock_deposit_value); +} + +#[ink::test] +fn test_close() { + // given + let accounts = default_accounts(); + let dan = get_dan(); + let close_duration = 360_000; + let mock_deposit_value = 1_000.into(); + let amount = 500.into(); + let initial_balance = 10_000.into(); + set_contract_balance(accounts.alice, initial_balance); + set_contract_balance(dan, initial_balance); + + // when + set_next_caller(accounts.alice); + let mut payment_channel = PaymentChannel::new(dan, close_duration); + let contract_id = contract_id(); + set_contract_balance(contract_id, mock_deposit_value); + set_next_caller(dan); + let signature = sign(contract_id, amount); + + // then + let should_close = move || payment_channel.close(amount, signature).unwrap(); + ink::env::test::assert_contract_termination::( + should_close, + accounts.alice, + amount, + ); + assert_eq!(get_contract_balance(dan), initial_balance + amount); +} + +#[ink::test] +fn close_fails_invalid_signature() { + // given + let accounts = default_accounts(); + let dan = get_dan(); + let mock_deposit_value = 1_000.into(); + let close_duration = 360_000; + let amount = 400.into(); + let unexpected_amount = amount + U256::from(1); + let initial_balance = 10_000.into(); + set_contract_balance(accounts.alice, initial_balance); + set_contract_balance(dan, initial_balance); + + // when + set_next_caller(accounts.alice); + let mut payment_channel = PaymentChannel::new(dan, close_duration); + let contract_id = contract_id(); + set_contract_balance(contract_id, mock_deposit_value); + set_next_caller(dan); + let signature = sign(contract_id, amount); + + // then + let res = payment_channel.close_inner(unexpected_amount, signature); + assert!(res.is_err(), "Expected an error, got {res:?} instead."); + assert_eq!(res.unwrap_err(), Error::InvalidSignature,); +} + +#[ink::test] +fn test_withdraw() { + // given + let accounts = default_accounts(); + let dan = get_dan(); + let initial_balance = 10_000.into(); + let mock_deposit_value = 1_000.into(); + let close_duration = 360_000; + let amount = 500.into(); + set_contract_balance(accounts.alice, initial_balance); + set_contract_balance(dan, initial_balance); + + // when + set_next_caller(accounts.alice); + let mut payment_channel = PaymentChannel::new(dan, close_duration); + let contract_id = contract_id(); + set_contract_balance(contract_id, mock_deposit_value); + + set_next_caller(dan); + let signature = sign(contract_id, amount); + payment_channel + .withdraw(amount, signature) + .expect("withdraw failed"); + + // then + assert_eq!(payment_channel.get_balance(), amount); + assert_eq!(get_contract_balance(dan), initial_balance + amount); +} + +#[ink::test] +fn withdraw_fails_invalid_signature() { + // given + let accounts = default_accounts(); + let dan = get_dan(); + let initial_balance = 10_000.into(); + let close_duration = 360_000; + let amount = 400.into(); + let unexpected_amount = amount + U256::from(1); + let mock_deposit_value = 1_000.into(); + set_contract_balance(accounts.alice, initial_balance); + set_contract_balance(dan, initial_balance); + + // when + set_next_caller(accounts.alice); + let mut payment_channel = PaymentChannel::new(dan, close_duration); + let contract_id = contract_id(); + set_contract_balance(contract_id, mock_deposit_value); + set_next_caller(dan); + let signature = sign(contract_id, amount); + + // then + let res = payment_channel.withdraw(unexpected_amount, signature); + assert!(res.is_err(), "Expected an error, got {res:?} instead."); + assert_eq!(res.unwrap_err(), Error::InvalidSignature,); +} + +#[ink::test] +fn test_start_sender_close() { + // given + let accounts = default_accounts(); + let initial_balance = 10_000.into(); + let mock_deposit_value = 1_000.into(); + let close_duration = 1; + set_contract_balance(accounts.alice, initial_balance); + set_contract_balance(accounts.bob, initial_balance); + + // when + set_next_caller(accounts.alice); + let mut payment_channel = PaymentChannel::new(accounts.bob, close_duration); + let contract_id = contract_id(); + set_contract_balance(contract_id, mock_deposit_value); + + payment_channel + .start_sender_close() + .expect("start_sender_close failed"); + advance_block(); + + // then + let now = get_current_time(); + assert!(now > payment_channel.get_expiration().unwrap()); +} + +#[ink::test] +fn test_claim_timeout() { + // given + let accounts = default_accounts(); + let initial_balance = 10_000.into(); + let close_duration = 1; + let mock_deposit_value = 1_000.into(); + set_contract_balance(accounts.alice, initial_balance); + set_contract_balance(accounts.bob, initial_balance); + + // when + set_next_caller(accounts.alice); + let contract_id = contract_id(); + let mut payment_channel = PaymentChannel::new(accounts.bob, close_duration); + set_contract_balance(contract_id, mock_deposit_value); + + payment_channel + .start_sender_close() + .expect("start_sender_close failed"); + advance_block(); + + // then + let should_close = move || payment_channel.claim_timeout().unwrap(); + ink::env::test::assert_contract_termination::( + should_close, + accounts.alice, + mock_deposit_value, + ); + assert_eq!( + get_contract_balance(accounts.alice), + initial_balance + mock_deposit_value + ); +} + +#[ink::test] +fn test_getters() { + // given + let accounts = default_accounts(); + let initial_balance = 10_000.into(); + let mock_deposit_value = 1_000.into(); + let close_duration = 360_000; + set_contract_balance(accounts.alice, initial_balance); + set_contract_balance(accounts.bob, initial_balance); + + // when + set_next_caller(accounts.alice); + let contract_id = contract_id(); + let payment_channel = PaymentChannel::new(accounts.bob, close_duration); + set_contract_balance(contract_id, mock_deposit_value); + + // then + assert_eq!(payment_channel.get_sender(), accounts.alice); + assert_eq!(payment_channel.get_recipient(), accounts.bob); + assert_eq!(payment_channel.get_balance(), mock_deposit_value); + assert_eq!(payment_channel.get_close_duration(), close_duration); + assert_eq!(payment_channel.get_withdrawn(), U256::zero()); +} \ No newline at end of file diff --git a/integration-tests/public/precompile-demo/lib.rs b/integration-tests/public/precompile-demo/lib.rs index e3a0e32497..08f4272ddf 100644 --- a/integration-tests/public/precompile-demo/lib.rs +++ b/integration-tests/public/precompile-demo/lib.rs @@ -29,7 +29,7 @@ pub trait System { } #[ink::contract] -mod precompile_demo { +pub mod precompile_demo { use super::System; use ink::prelude::vec::Vec; @@ -56,39 +56,7 @@ mod precompile_demo { out_bytes.0 } } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink_e2e::ContractsBackend; - - type E2EResult = std::result::Result>; - - #[ink_e2e::test] - async fn call_echo_works(mut client: ink_e2e::Client) -> E2EResult<()> { - // given - let mut constructor = PrecompileDemoRef::new(); - let contract = client - .instantiate("precompile_demo", &ink_e2e::bob(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let call_builder = contract.call_builder::(); - - // when - let data = vec![0x1, 0x2, 0x3, 0x4]; - let expected = data.clone(); - let call_echo = call_builder.call_echo(data); - let res = client - .call(&ink_e2e::bob(), &call_echo) - .submit() - .await - .expect("call_echo failed"); - - // then - assert_eq!(res.return_value(), expected); - - Ok(()) - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/precompile-demo/tests.rs b/integration-tests/public/precompile-demo/tests.rs new file mode 100644 index 0000000000..3ec27a877e --- /dev/null +++ b/integration-tests/public/precompile-demo/tests.rs @@ -0,0 +1,31 @@ +use super::precompile_demo::{PrecompileDemo, PrecompileDemoRef}; +use ink_e2e::ContractsBackend; + +type E2EResult = std::result::Result>; + +#[ink_e2e::test] +async fn call_echo_works(mut client: Client) -> E2EResult<()> { + // Given + let mut constructor = PrecompileDemoRef::new(); + let contract = client + .instantiate("precompile_demo", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // When + let data = vec![0x1, 0x2, 0x3, 0x4]; + let expected = data.clone(); + let call_echo = call_builder.call_echo(data); + let res = client + .call(&ink_e2e::bob(), &call_echo) + .submit() + .await + .expect("call_echo failed"); + + // Then + assert_eq!(res.return_value(), expected); + + Ok(()) +} \ No newline at end of file diff --git a/integration-tests/public/trait-dyn-cross-contract-calls/lib.rs b/integration-tests/public/trait-dyn-cross-contract-calls/lib.rs index 441828494b..d7df6217c7 100644 --- a/integration-tests/public/trait-dyn-cross-contract-calls/lib.rs +++ b/integration-tests/public/trait-dyn-cross-contract-calls/lib.rs @@ -41,93 +41,5 @@ pub mod caller { } } -#[cfg(all(test, feature = "e2e-tests"))] -mod e2e_tests { - use super::caller::{ - Caller, - CallerRef, - }; - use dyn_traits::Increment; - use ink_e2e::ContractsBackend; - use trait_incrementer::incrementer::{ - Incrementer, - IncrementerRef, - }; - - type E2EResult = Result>; - - /// A test deploys and instantiates the `trait_incrementer::Incrementer` and - /// `trait_incrementer_caller::Caller` contracts, where the `Caller` uses the account - /// id of the `Incrementer` for instantiation. - /// - /// The test verifies that we can increment the value of the `Incrementer` contract - /// through the `Caller` contract. - #[ink_e2e::test] - async fn e2e_cross_contract_calls(mut client: Client) -> E2EResult<()> { - let _ = client - .upload("trait-incrementer", &ink_e2e::alice()) - .submit() - .await - .expect("uploading `trait-incrementer` failed") - .code_hash; - - let _ = client - .upload("trait-incrementer-caller", &ink_e2e::alice()) - .submit() - .await - .expect("uploading `trait-incrementer-caller` failed") - .code_hash; - - let mut constructor = IncrementerRef::new(); - - let incrementer = client - .instantiate("trait-incrementer", &ink_e2e::alice(), &mut constructor) - .submit() - .await - .expect("instantiate failed"); - let incrementer_call = incrementer.call_builder::(); - - let mut constructor = CallerRef::new(incrementer.addr); - - let caller = client - .instantiate( - "trait-incrementer-caller", - &ink_e2e::alice(), - &mut constructor, - ) - .submit() - .await - .expect("instantiate failed"); - let mut caller_call = caller.call_builder::(); - - // Check through the caller that the value of the incrementer is zero - let get = caller_call.get(); - let value = client - .call(&ink_e2e::alice(), &get) - .dry_run() - .await? - .return_value(); - assert_eq!(value, 0); - - // Increment the value of the incrementer via the caller - let inc = caller_call.inc(); - let _ = client - .call(&ink_e2e::alice(), &inc) - .submit() - .await - .expect("calling `inc` failed"); - - // Ask the `trait-increment` about a value. It should be updated by the caller. - // Also use `contract_ref_from_path!(Increment)` instead of `IncrementerRef` - // to check that it also works with e2e testing. - let get = incrementer_call.get(); - let value = client - .call(&ink_e2e::alice(), &get) - .dry_run() - .await? - .return_value(); - assert_eq!(value, 1); - - Ok(()) - } -} +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/trait-dyn-cross-contract-calls/tests.rs b/integration-tests/public/trait-dyn-cross-contract-calls/tests.rs new file mode 100644 index 0000000000..c97860cd5a --- /dev/null +++ b/integration-tests/public/trait-dyn-cross-contract-calls/tests.rs @@ -0,0 +1,87 @@ +use super::caller::{ + Caller, + CallerRef, +}; +use dyn_traits::Increment; +use ink_e2e::ContractsBackend; +use trait_incrementer::incrementer::{ + Incrementer, + IncrementerRef, +}; + +type E2EResult = Result>; + +/// A test deploys and instantiates the `trait_incrementer::Incrementer` and +/// `trait_incrementer_caller::Caller` contracts, where the `Caller` uses the account +/// id of the `Incrementer` for instantiation. +/// +/// The test verifies that we can increment the value of the `Incrementer` contract +/// through the `Caller` contract. +#[ink_e2e::test] +async fn e2e_cross_contract_calls(mut client: Client) -> E2EResult<()> { + let _ = client + .upload("trait-incrementer", &ink_e2e::alice()) + .submit() + .await + .expect("uploading `trait-incrementer` failed") + .code_hash; + + let _ = client + .upload("trait-incrementer-caller", &ink_e2e::alice()) + .submit() + .await + .expect("uploading `trait-incrementer-caller` failed") + .code_hash; + + let mut constructor = IncrementerRef::new(); + + let incrementer = client + .instantiate("trait-incrementer", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let incrementer_call = incrementer.call_builder::(); + + let mut constructor = CallerRef::new(incrementer.addr); + + let caller = client + .instantiate( + "trait-incrementer-caller", + &ink_e2e::alice(), + &mut constructor, + ) + .submit() + .await + .expect("instantiate failed"); + let mut caller_call = caller.call_builder::(); + + // Check through the caller that the value of the incrementer is zero + let get = caller_call.get(); + let value = client + .call(&ink_e2e::alice(), &get) + .dry_run() + .await? + .return_value(); + assert_eq!(value, 0); + + // Increment the value of the incrementer via the caller + let inc = caller_call.inc(); + let _ = client + .call(&ink_e2e::alice(), &inc) + .submit() + .await + .expect("calling `inc` failed"); + + // Ask the `trait-increment` about a value. It should be updated by the caller. + // Also use `contract_ref_from_path!(Increment)` instead of `IncrementerRef` + // to check that it also works with e2e testing. + let get = incrementer_call.get(); + let value = client + .call(&ink_e2e::alice(), &get) + .dry_run() + .await? + .return_value(); + assert_eq!(value, 1); + + Ok(()) +} \ No newline at end of file diff --git a/integration-tests/public/trait-erc20/lib.rs b/integration-tests/public/trait-erc20/lib.rs index 55e823ba4b..dd6542d6ec 100644 --- a/integration-tests/public/trait-erc20/lib.rs +++ b/integration-tests/public/trait-erc20/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] #[ink::contract] -mod erc20 { +pub mod erc20 { use ink::{ U256, storage::Mapping, @@ -71,11 +71,11 @@ mod erc20 { #[ink(event)] pub struct Transfer { #[ink(topic)] - from: Option
, + pub from: Option
, #[ink(topic)] - to: Option
, + pub to: Option
, #[ink(topic)] - value: U256, + pub value: U256, } /// Event emitted when an approval occurs that `spender` is allowed to withdraw @@ -83,11 +83,11 @@ mod erc20 { #[ink(event)] pub struct Approval { #[ink(topic)] - owner: Address, + pub owner: Address, #[ink(topic)] - spender: Address, + pub spender: Address, #[ink(topic)] - value: U256, + pub value: U256, } impl Erc20 { @@ -261,302 +261,7 @@ mod erc20 { Ok(()) } } - - /// Unit tests. - #[cfg(test)] - mod tests { - /// Imports all the definitions from the outer scope so we can use them here. - use super::*; - use ink::{ - env::hash::{ - Blake2x256, - CryptoHash, - HashOutput, - }, - primitives::Clear, - }; - - fn assert_transfer_event( - event: &ink::env::test::EmittedEvent, - expected_from: Option
, - expected_to: Option
, - expected_value: U256, - ) { - let decoded_event = - ::decode(&mut &event.data[..]) - .expect("encountered invalid contract event data buffer"); - let Transfer { from, to, value } = decoded_event; - assert_eq!(from, expected_from, "encountered invalid Transfer.from"); - assert_eq!(to, expected_to, "encountered invalid Transfer.to"); - assert_eq!(value, expected_value, "encountered invalid Transfer.value"); - - fn encoded_into_hash(entity: T) -> Hash - where - T: ink::scale::Encode, - { - let mut result = Hash::CLEAR_HASH; - let len_result = result.as_ref().len(); - let encoded = entity.encode(); - let len_encoded = encoded.len(); - if len_encoded <= len_result { - result.as_mut()[..len_encoded].copy_from_slice(&encoded); - return result - } - let mut hash_output = - <::Type as Default>::default(); - ::hash(&encoded, &mut hash_output); - let copy_len = core::cmp::min(hash_output.len(), len_result); - result.as_mut()[0..copy_len].copy_from_slice(&hash_output[0..copy_len]); - result - } - - let mut expected_topics = Vec::new(); - expected_topics.push( - ink::blake2x256!("Transfer(Option
,Option
,U256)").into(), - ); - if let Some(from) = expected_from { - expected_topics.push(encoded_into_hash(from)); - } else { - expected_topics.push(Hash::CLEAR_HASH); - } - if let Some(to) = expected_to { - expected_topics.push(encoded_into_hash(to)); - } else { - expected_topics.push(Hash::CLEAR_HASH); - } - expected_topics.push(encoded_into_hash(value)); - - for (n, (actual_topic, expected_topic)) in - event.topics.iter().zip(expected_topics).enumerate() - { - let topic = ::decode(&mut &actual_topic[..]) - .expect("encountered invalid topic encoding"); - assert_eq!(topic, expected_topic, "encountered invalid topic at {n}"); - } - } - - /// The default constructor does its job. - #[ink::test] - fn new_works() { - // Constructor works. - set_caller(Address::from([0x01; 20])); - let initial_supply = 100.into(); - let erc20 = Erc20::new(initial_supply); - - // The `BaseErc20` trait has indeed been implemented. - assert_eq!(::total_supply(&erc20), initial_supply); - - // Transfer event triggered during initial construction. - let emitted_events = ink::env::test::recorded_events(); - assert_eq!(1, emitted_events.len()); - - assert_transfer_event( - &emitted_events[0], - None, - Some(Address::from([0x01; 20])), - 100.into(), - ); - } - - /// The total supply was applied. - #[ink::test] - fn total_supply_works() { - // Constructor works. - set_caller(Address::from([0x01; 20])); - let initial_supply = 100.into(); - let erc20 = Erc20::new(initial_supply); - // Transfer event triggered during initial construction. - let emitted_events = ink::env::test::recorded_events(); - assert_transfer_event( - &emitted_events[0], - None, - Some(Address::from([0x01; 20])), - 100.into(), - ); - // Get the token total supply. - assert_eq!(erc20.total_supply(), 100.into()); - } - - /// Get the actual balance of an account. - #[ink::test] - fn balance_of_works() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - - // Constructor works - let initial_supply = 100.into(); - let erc20 = Erc20::new(initial_supply); - // Transfer event triggered during initial construction - let emitted_events = ink::env::test::recorded_events(); - assert_transfer_event( - &emitted_events[0], - None, - Some(accounts.alice), - 100.into(), - ); - // Alice owns all the tokens on contract instantiation - assert_eq!(erc20.balance_of(accounts.alice), 100.into()); - // Bob does not owns tokens - assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); - } - - #[ink::test] - fn transfer_works() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - - // Constructor works. - let initial_supply = 100.into(); - let mut erc20 = Erc20::new(initial_supply); - // Transfer event triggered during initial construction. - assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); - // Alice transfers 10 tokens to Bob. - assert_eq!(erc20.transfer(accounts.bob, U256::from(10)), Ok(())); - // Bob owns 10 tokens. - assert_eq!(erc20.balance_of(accounts.bob), U256::from(10)); - - let emitted_events = ink::env::test::recorded_events(); - assert_eq!(emitted_events.len(), 2); - // Check first transfer event related to ERC-20 instantiation. - assert_transfer_event( - &emitted_events[0], - None, - Some(accounts.alice), - 100.into(), - ); - // Check the second transfer event relating to the actual transfer. - assert_transfer_event( - &emitted_events[1], - Some(accounts.alice), - Some(accounts.bob), - 10.into(), - ); - } - - #[ink::test] - fn invalid_transfer_should_fail() { - // Constructor works. - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - - let initial_supply = 100.into(); - let mut erc20 = Erc20::new(initial_supply); - - assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); - // Set Bob as caller - set_caller(accounts.bob); - - // Bob fails to transfer 10 tokens to Eve. - assert_eq!( - erc20.transfer(accounts.eve, 10.into()), - Err(Error::InsufficientBalance) - ); - // Alice owns all the tokens. - assert_eq!(erc20.balance_of(accounts.alice), 100.into()); - assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); - assert_eq!(erc20.balance_of(accounts.eve), U256::zero()); - - // Transfer event triggered during initial construction. - let emitted_events = ink::env::test::recorded_events(); - assert_eq!(emitted_events.len(), 1); - assert_transfer_event( - &emitted_events[0], - None, - Some(accounts.alice), - 100.into(), - ); - } - - #[ink::test] - fn transfer_from_works() { - // Constructor works. - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - - let initial_supply = 100.into(); - let mut erc20 = Erc20::new(initial_supply); - - // Transfer event triggered during initial construction. - let accounts = ink::env::test::default_accounts(); - - // Bob fails to transfer tokens owned by Alice. - assert_eq!( - erc20.transfer_from(accounts.alice, accounts.eve, 10.into()), - Err(Error::InsufficientAllowance) - ); - // Alice approves Bob for token transfers on her behalf. - assert_eq!(erc20.approve(accounts.bob, U256::from(10)), Ok(())); - - // The approve event takes place. - assert_eq!(ink::env::test::recorded_events().len(), 2); - - // Set Bob as caller. - set_caller(accounts.bob); - - // Bob transfers tokens from Alice to Eve. - assert_eq!( - erc20.transfer_from(accounts.alice, accounts.eve, 10.into()), - Ok(()) - ); - // Eve owns tokens. - assert_eq!(erc20.balance_of(accounts.eve), U256::from(10)); - - // Check all transfer events that happened during the previous calls: - let emitted_events = ink::env::test::recorded_events(); - assert_eq!(emitted_events.len(), 3); - assert_transfer_event( - &emitted_events[0], - None, - Some(accounts.alice), - 100.into(), - ); - // The second event `emitted_events[1]` is an Approve event that we skip - // checking. - assert_transfer_event( - &emitted_events[2], - Some(accounts.alice), - Some(accounts.eve), - 10.into(), - ); - } - - #[ink::test] - fn allowance_must_not_change_on_failed_transfer() { - let accounts = ink::env::test::default_accounts(); - set_caller(accounts.alice); - let initial_supply = 100.into(); - let mut erc20 = Erc20::new(initial_supply); - - // Alice approves Bob for token transfers on her behalf. - let alice_balance = erc20.balance_of(accounts.alice); - let initial_allowance = alice_balance + U256::from(2); - assert_eq!(erc20.approve(accounts.bob, initial_allowance), Ok(())); - - // Set Bob as caller. - set_caller(accounts.bob); - - // Bob tries to transfer tokens from Alice to Eve. - let emitted_events_before = ink::env::test::recorded_events(); - assert_eq!( - erc20.transfer_from( - accounts.alice, - accounts.eve, - alice_balance + U256::from(1) - ), - Err(Error::InsufficientBalance) - ); - // Allowance must have stayed the same - assert_eq!( - erc20.allowance(accounts.alice, accounts.bob), - initial_allowance - ); - // No more events must have been emitted - let emitted_events_after = ink::env::test::recorded_events(); - assert_eq!(emitted_events_before.len(), emitted_events_after.len()); - } - - fn set_caller(sender: Address) { - ink::env::test::set_caller(sender); - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/trait-erc20/tests.rs b/integration-tests/public/trait-erc20/tests.rs new file mode 100644 index 0000000000..2c3183e2e1 --- /dev/null +++ b/integration-tests/public/trait-erc20/tests.rs @@ -0,0 +1,294 @@ +use super::erc20::*; +use ink::{ + env::hash::{ + Blake2x256, + CryptoHash, + HashOutput, + }, + primitives::{ + Clear, + Hash, + }, + U256, +}; + +fn assert_transfer_event( + event: &ink::env::test::EmittedEvent, + expected_from: Option
, + expected_to: Option
, + expected_value: U256, +) { + let decoded_event = ::decode(&mut &event.data[..]) + .expect("encountered invalid contract event data buffer"); + let Transfer { from, to, value } = decoded_event; + assert_eq!(from, expected_from, "encountered invalid Transfer.from"); + assert_eq!(to, expected_to, "encountered invalid Transfer.to"); + assert_eq!(value, expected_value, "encountered invalid Transfer.value"); + + fn encoded_into_hash(entity: T) -> Hash + where + T: ink::scale::Encode, + { + let mut result = Hash::CLEAR_HASH; + let len_result = result.as_ref().len(); + let encoded = entity.encode(); + let len_encoded = encoded.len(); + if len_encoded <= len_result { + result.as_mut()[..len_encoded].copy_from_slice(&encoded); + return result + } + let mut hash_output = <::Type as Default>::default(); + ::hash(&encoded, &mut hash_output); + let copy_len = core::cmp::min(hash_output.len(), len_result); + result.as_mut()[0..copy_len].copy_from_slice(&hash_output[0..copy_len]); + result + } + + let mut expected_topics = Vec::new(); + expected_topics.push( + ink::blake2x256!("Transfer(Option
,Option
,U256)").into(), + ); + if let Some(from) = expected_from { + expected_topics.push(encoded_into_hash(from)); + } else { + expected_topics.push(Hash::CLEAR_HASH); + } + if let Some(to) = expected_to { + expected_topics.push(encoded_into_hash(to)); + } else { + expected_topics.push(Hash::CLEAR_HASH); + } + expected_topics.push(encoded_into_hash(value)); + + for (n, (actual_topic, expected_topic)) in + event.topics.iter().zip(expected_topics).enumerate() + { + let topic = ::decode(&mut &actual_topic[..]) + .expect("encountered invalid topic encoding"); + assert_eq!(topic, expected_topic, "encountered invalid topic at {n}"); + } +} + +/// The default constructor does its job. +#[ink::test] +fn new_works() { + // Constructor works. + set_caller(Address::from([0x01; 20])); + let initial_supply = 100.into(); + let erc20 = Erc20::new(initial_supply); + + // The `BaseErc20` trait has indeed been implemented. + assert_eq!(::total_supply(&erc20), initial_supply); + + // Transfer event triggered during initial construction. + let emitted_events = ink::env::test::recorded_events(); + assert_eq!(1, emitted_events.len()); + + assert_transfer_event( + &emitted_events[0], + None, + Some(Address::from([0x01; 20])), + 100.into(), + ); +} + +/// The total supply was applied. +#[ink::test] +fn total_supply_works() { + // Constructor works. + set_caller(Address::from([0x01; 20])); + let initial_supply = 100.into(); + let erc20 = Erc20::new(initial_supply); + // Transfer event triggered during initial construction. + let emitted_events = ink::env::test::recorded_events(); + assert_transfer_event( + &emitted_events[0], + None, + Some(Address::from([0x01; 20])), + 100.into(), + ); + // Get the token total supply. + assert_eq!(erc20.total_supply(), 100.into()); +} + +/// Get the actual balance of an account. +#[ink::test] +fn balance_of_works() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + + // Constructor works + let initial_supply = 100.into(); + let erc20 = Erc20::new(initial_supply); + // Transfer event triggered during initial construction + let emitted_events = ink::env::test::recorded_events(); + assert_transfer_event( + &emitted_events[0], + None, + Some(accounts.alice), + 100.into(), + ); + // Alice owns all the tokens on contract instantiation + assert_eq!(erc20.balance_of(accounts.alice), 100.into()); + // Bob does not owns tokens + assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); +} + +#[ink::test] +fn transfer_works() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + + // Constructor works. + let initial_supply = 100.into(); + let mut erc20 = Erc20::new(initial_supply); + // Transfer event triggered during initial construction. + assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); + // Alice transfers 10 tokens to Bob. + assert_eq!(erc20.transfer(accounts.bob, U256::from(10)), Ok(())); + // Bob owns 10 tokens. + assert_eq!(erc20.balance_of(accounts.bob), U256::from(10)); + + let emitted_events = ink::env::test::recorded_events(); + assert_eq!(emitted_events.len(), 2); + // Check first transfer event related to ERC-20 instantiation. + assert_transfer_event( + &emitted_events[0], + None, + Some(accounts.alice), + 100.into(), + ); + // Check the second transfer event relating to the actual transfer. + assert_transfer_event( + &emitted_events[1], + Some(accounts.alice), + Some(accounts.bob), + 10.into(), + ); +} + +#[ink::test] +fn invalid_transfer_should_fail() { + // Constructor works. + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + + let initial_supply = 100.into(); + let mut erc20 = Erc20::new(initial_supply); + + assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); + // Set Bob as caller + set_caller(accounts.bob); + + // Bob fails to transfer 10 tokens to Eve. + assert_eq!( + erc20.transfer(accounts.eve, 10.into()), + Err(Error::InsufficientBalance) + ); + // Alice owns all the tokens. + assert_eq!(erc20.balance_of(accounts.alice), 100.into()); + assert_eq!(erc20.balance_of(accounts.bob), U256::zero()); + assert_eq!(erc20.balance_of(accounts.eve), U256::zero()); + + // Transfer event triggered during initial construction. + let emitted_events = ink::env::test::recorded_events(); + assert_eq!(emitted_events.len(), 1); + assert_transfer_event( + &emitted_events[0], + None, + Some(accounts.alice), + 100.into(), + ); +} + +#[ink::test] +fn transfer_from_works() { + // Constructor works. + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + + let initial_supply = 100.into(); + let mut erc20 = Erc20::new(initial_supply); + + // Transfer event triggered during initial construction. + let accounts = ink::env::test::default_accounts(); + + // Bob fails to transfer tokens owned by Alice. + assert_eq!( + erc20.transfer_from(accounts.alice, accounts.eve, 10.into()), + Err(Error::InsufficientAllowance) + ); + // Alice approves Bob for token transfers on her behalf. + assert_eq!(erc20.approve(accounts.bob, U256::from(10)), Ok(())); + + // The approve event takes place. + assert_eq!(ink::env::test::recorded_events().len(), 2); + + // Set Bob as caller. + set_caller(accounts.bob); + + // Bob transfers tokens from Alice to Eve. + assert_eq!( + erc20.transfer_from(accounts.alice, accounts.eve, 10.into()), + Ok(()) + ); + // Eve owns tokens. + assert_eq!(erc20.balance_of(accounts.eve), U256::from(10)); + + // Check all transfer events that happened during the previous calls: + let emitted_events = ink::env::test::recorded_events(); + assert_eq!(emitted_events.len(), 3); + assert_transfer_event( + &emitted_events[0], + None, + Some(accounts.alice), + 100.into(), + ); + // The second event `emitted_events[1]` is an Approve event that we skip + // checking. + assert_transfer_event( + &emitted_events[2], + Some(accounts.alice), + Some(accounts.eve), + 10.into(), + ); +} + +#[ink::test] +fn allowance_must_not_change_on_failed_transfer() { + let accounts = ink::env::test::default_accounts(); + set_caller(accounts.alice); + let initial_supply = 100.into(); + let mut erc20 = Erc20::new(initial_supply); + + // Alice approves Bob for token transfers on her behalf. + let alice_balance = erc20.balance_of(accounts.alice); + let initial_allowance = alice_balance + U256::from(2); + assert_eq!(erc20.approve(accounts.bob, initial_allowance), Ok(())); + + // Set Bob as caller. + set_caller(accounts.bob); + + // Bob tries to transfer tokens from Alice to Eve. + let emitted_events_before = ink::env::test::recorded_events(); + assert_eq!( + erc20.transfer_from( + accounts.alice, + accounts.eve, + alice_balance + U256::from(1) + ), + Err(Error::InsufficientBalance) + ); + // Allowance must have stayed the same + assert_eq!( + erc20.allowance(accounts.alice, accounts.bob), + initial_allowance + ); + // No more events must have been emitted + let emitted_events_after = ink::env::test::recorded_events(); + assert_eq!(emitted_events_before.len(), emitted_events_after.len()); +} + +fn set_caller(sender: Address) { + ink::env::test::set_caller(sender); +} \ No newline at end of file diff --git a/integration-tests/public/trait-flipper/lib.rs b/integration-tests/public/trait-flipper/lib.rs index b465ea382e..87616caefc 100644 --- a/integration-tests/public/trait-flipper/lib.rs +++ b/integration-tests/public/trait-flipper/lib.rs @@ -40,25 +40,7 @@ pub mod flipper { self.value } } - - #[cfg(test)] - mod tests { - use super::*; - - #[::ink::test] - fn default_works() { - let flipper = Flipper::new(); - assert!(flipper.get()); - } - - #[::ink::test] - fn it_works() { - let mut flipper = Flipper::new(); - // Can call using universal call syntax using the trait. - assert!(::get(&flipper)); - ::flip(&mut flipper); - // Normal call syntax possible to as long as the trait is in scope. - assert!(!flipper.get()); - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/trait-flipper/tests.rs b/integration-tests/public/trait-flipper/tests.rs new file mode 100644 index 0000000000..50be53f2eb --- /dev/null +++ b/integration-tests/public/trait-flipper/tests.rs @@ -0,0 +1,28 @@ +use super::Flip; +use super::flipper::Flipper; + +#[::ink::test] +fn default_works() { + // Given + let flipper = Flipper::new(); + + // Then + assert!(flipper.get()); +} + +#[::ink::test] +fn it_works() { + // Given + let mut flipper = Flipper::new(); + + // Then + // Can call using universal call syntax using the trait. + assert!(::get(&flipper)); + + // When + ::flip(&mut flipper); + + // Then + // Normal call syntax possible to as long as the trait is in scope. + assert!(!flipper.get()); +} \ No newline at end of file diff --git a/integration-tests/public/trait-incrementer/lib.rs b/integration-tests/public/trait-incrementer/lib.rs index 65edd6f9d1..fd3eff95ac 100644 --- a/integration-tests/public/trait-incrementer/lib.rs +++ b/integration-tests/public/trait-incrementer/lib.rs @@ -46,25 +46,7 @@ pub mod incrementer { self.value = 0; } } - - #[cfg(test)] - mod tests { - use super::*; - - #[test] - fn default_works() { - let incrementer = Incrementer::new(0); - assert_eq!(incrementer.get(), 0); - } - - #[test] - fn it_works() { - let mut incrementer = Incrementer::new(0); - // Can call using universal call syntax using the trait. - assert_eq!(::get(&incrementer), 0); - ::inc(&mut incrementer); - // Normal call syntax possible to as long as the trait is in scope. - assert_eq!(incrementer.get(), 1); - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/trait-incrementer/tests.rs b/integration-tests/public/trait-incrementer/tests.rs new file mode 100644 index 0000000000..5bbd07a12f --- /dev/null +++ b/integration-tests/public/trait-incrementer/tests.rs @@ -0,0 +1,28 @@ +use super::incrementer::Incrementer; +use traits::Increment; + +#[test] +fn default_works() { + // Given + let incrementer = Incrementer::new(0); + + // Then + assert_eq!(incrementer.get(), 0); +} + +#[test] +fn it_works() { + // Given + let mut incrementer = Incrementer::new(0); + + // Then + // Can call using universal call syntax using the trait. + assert_eq!(::get(&incrementer), 0); + + // When + ::inc(&mut incrementer); + + // Then + // Normal call syntax possible to as long as the trait is in scope. + assert_eq!(incrementer.get(), 1); +} \ No newline at end of file diff --git a/integration-tests/public/wildcard-selector/lib.rs b/integration-tests/public/wildcard-selector/lib.rs index 5bcdce5c49..5bd37fb238 100644 --- a/integration-tests/public/wildcard-selector/lib.rs +++ b/integration-tests/public/wildcard-selector/lib.rs @@ -9,7 +9,7 @@ pub mod wildcard_selector { #[cfg(feature = "emit-event")] #[ink::event] pub struct Event { - msg: String, + pub msg: String, } #[ink(storage)] @@ -55,152 +55,7 @@ pub mod wildcard_selector { }); } } - - #[cfg(all(test, feature = "e2e-tests"))] - mod e2e_tests { - use super::*; - use ink_e2e::ContractsBackend; - - use ink::{ - env::call::utils::{ - Argument, - ArgumentList, - EmptyArgumentList, - }, - primitives::abi::Ink, - }; - - type E2EResult = std::result::Result>; - type Environment = ::Env; - - fn build_message( - addr: Address, - selector: [u8; 4], - message: String, - ) -> ink_e2e::CallBuilderFinal< - Environment, - ArgumentList, EmptyArgumentList, Ink>, - (), - Ink, - > { - ink::env::call::build_call::() - .call(addr) - .exec_input( - ink::env::call::ExecutionInput::new(ink::env::call::Selector::new( - selector, - )) - .push_arg(message), - ) - .returns::<()>() - } - - #[ink_e2e::test(features = ["emit-event"])] - async fn arbitrary_selectors_handled_by_wildcard( - mut client: Client, - ) -> E2EResult<()> { - // given - let mut constructor = WildcardSelectorRef::new(); - let contract_acc_id = client - .instantiate("wildcard_selector", &ink_e2e::alice(), &mut constructor) - .submit() - .await - .expect("instantiate failed") - .addr; - - // when - const ARBITRARY_SELECTOR: [u8; 4] = [0xF9, 0xF9, 0xF9, 0xF9]; - let wildcard_message = "WILDCARD_MESSAGE 1".to_string(); - let wildcard = build_message( - contract_acc_id, - ARBITRARY_SELECTOR, - wildcard_message.clone(), - ); - - let _result = client - .call(&ink_e2e::bob(), &wildcard) - .submit() - .await - .expect("wildcard failed"); - - const ARBITRARY_SELECTOR_2: [u8; 4] = [0x01, 0x23, 0x45, 0x67]; - let wildcard_message2 = "WILDCARD_MESSAGE 2".to_string(); - let wildcard2 = build_message( - contract_acc_id, - ARBITRARY_SELECTOR_2, - wildcard_message2.clone(), - ); - - let _result2 = client - .call(&ink_e2e::bob(), &wildcard2) - .submit() - .await - .expect("wildcard failed"); - - // then - let contract_events = _result.contract_emitted_events()?; - assert_eq!(1, contract_events.len()); - let contract_event = contract_events.first().expect("first event must exist"); - let event: Event = - ink::scale::Decode::decode(&mut &contract_event.event.data[..]) - .expect("encountered invalid contract event data buffer"); - assert_eq!( - event.msg, - format!( - "Wildcard selector: {ARBITRARY_SELECTOR:?}, message: {wildcard_message}" - ) - ); - - /* - // todo - assert!(result.debug_message().contains(&format!( - "Wildcard selector: {:?}, message: {}", - ARBITRARY_SELECTOR, wildcard_message - ))); - - assert!(result2.debug_message().contains(&format!( - "Wildcard selector: {:?}, message: {}", - ARBITRARY_SELECTOR_2, wildcard_message2 - ))); - */ - - Ok(()) - } - - #[ink_e2e::test] - async fn wildcard_complement_works(mut client: Client) -> E2EResult<()> { - // given - let mut constructor = WildcardSelectorRef::new(); - let contract_acc_id = client - .instantiate("wildcard_selector", &ink_e2e::alice(), &mut constructor) - .submit() - .await - .expect("instantiate failed") - .addr; - - // when - let wildcard_complement_message = "WILDCARD COMPLEMENT MESSAGE".to_string(); - let wildcard = build_message( - contract_acc_id, - ink::IIP2_WILDCARD_COMPLEMENT_SELECTOR, - wildcard_complement_message.clone(), - ); - - let _result = client - .call(&ink_e2e::bob(), &wildcard) - .submit() - .await - .expect("wildcard failed"); - - // then - /* - // todo - assert!(result.debug_message().contains(&format!( - "Wildcard complement message: {}", - wildcard_complement_message - ))); - */ - - Ok(()) - } - } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/integration-tests/public/wildcard-selector/tests.rs b/integration-tests/public/wildcard-selector/tests.rs new file mode 100644 index 0000000000..f1756c54a7 --- /dev/null +++ b/integration-tests/public/wildcard-selector/tests.rs @@ -0,0 +1,127 @@ +use super::wildcard_selector::*; + +#[cfg(all(test, feature = "e2e-tests"))] +mod e2e_tests { + use super::*; + use ink_e2e::ContractsBackend; + + use ink::{ + env::call::utils::{ + Argument, + ArgumentList, + EmptyArgumentList, + }, + primitives::abi::Ink, + }; + + type E2EResult = std::result::Result>; + type Environment = ::Env; + + fn build_message( + addr: AccountId, // Changed to AccountId (which is H160 in v6) to match Address alias usage usually found + selector: [u8; 4], + message: String, + ) -> ink_e2e::CallBuilderFinal< + Environment, + ArgumentList, EmptyArgumentList, Ink>, + (), + Ink, + > { + ink::env::call::build_call::() + .call(addr) + .exec_input( + ink::env::call::ExecutionInput::new(ink::env::call::Selector::new( + selector, + )) + .push_arg(message), + ) + .returns::<()>() + } + + #[ink_e2e::test(features = ["emit-event"])] + async fn arbitrary_selectors_handled_by_wildcard( + mut client: Client, + ) -> E2EResult<()> { + // Given + let mut constructor = WildcardSelectorRef::new(); + let contract_acc_id = client + .instantiate("wildcard_selector", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed") + .addr; + + // When + const ARBITRARY_SELECTOR: [u8; 4] = [0xF9, 0xF9, 0xF9, 0xF9]; + let wildcard_message = "WILDCARD_MESSAGE 1".to_string(); + let wildcard = build_message( + contract_acc_id, + ARBITRARY_SELECTOR, + wildcard_message.clone(), + ); + + let _result = client + .call(&ink_e2e::bob(), &wildcard) + .submit() + .await + .expect("wildcard failed"); + + const ARBITRARY_SELECTOR_2: [u8; 4] = [0x01, 0x23, 0x45, 0x67]; + let wildcard_message2 = "WILDCARD_MESSAGE 2".to_string(); + let wildcard2 = build_message( + contract_acc_id, + ARBITRARY_SELECTOR_2, + wildcard_message2.clone(), + ); + + let _result2 = client + .call(&ink_e2e::bob(), &wildcard2) + .submit() + .await + .expect("wildcard failed"); + + // Then + let contract_events = _result.contract_emitted_events()?; + assert_eq!(1, contract_events.len()); + let contract_event = contract_events.first().expect("first event must exist"); + let event: Event = + ink::scale::Decode::decode(&mut &contract_event.event.data[..]) + .expect("encountered invalid contract event data buffer"); + assert_eq!( + event.msg, + format!( + "Wildcard selector: {ARBITRARY_SELECTOR:?}, message: {wildcard_message}" + ) + ); + + Ok(()) + } + + #[ink_e2e::test] + async fn wildcard_complement_works(mut client: Client) -> E2EResult<()> { + // Given + let mut constructor = WildcardSelectorRef::new(); + let contract_acc_id = client + .instantiate("wildcard_selector", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed") + .addr; + + // When + let wildcard_complement_message = "WILDCARD COMPLEMENT MESSAGE".to_string(); + let wildcard = build_message( + contract_acc_id, + ink::IIP2_WILDCARD_COMPLEMENT_SELECTOR, + wildcard_complement_message.clone(), + ); + + let _result = client + .call(&ink_e2e::bob(), &wildcard) + .submit() + .await + .expect("wildcard failed"); + + Ok(()) + } +} \ No newline at end of file From 32c31026e80fc46ca1c85cbf1fa29dcce58340da Mon Sep 17 00:00:00 2001 From: Alhibb <63309522+Alhibb@users.noreply.github.com> Date: Fri, 5 Dec 2025 21:33:09 +0100 Subject: [PATCH 5/5] folder restructured --- integration-tests/public/README.md | 81 ++++++++++++++++++ .../conditional}/Cargo.toml | 0 .../conditional}/lib.rs | 0 .../conditional}/tests.rs | 0 .../custom-env}/Cargo.toml | 0 .../custom-env}/README.md | 0 .../custom-env}/lib.rs | 0 .../custom-env}/tests.rs | 0 .../debugging}/Cargo.toml | 0 .../debugging}/lib.rs | 0 .../debugging}/tests.rs | 0 .../fuzzing}/Cargo.toml | 0 .../{fuzz-testing => advanced/fuzzing}/lib.rs | 0 .../fuzzing}/tests.rs | 0 .../upgradeable}/README.md | 0 .../upgradeable}/delegator/Cargo.toml | 0 .../delegator/delegatee/Cargo.toml | 0 .../upgradeable}/delegator/delegatee/lib.rs | 0 .../delegator/delegatee2/Cargo.toml | 0 .../upgradeable}/delegator/delegatee2/lib.rs | 0 .../upgradeable}/delegator/lib.rs | 0 .../set-code-hash-migration/Cargo.toml | 0 .../set-code-hash-migration/e2e_tests.rs | 0 .../set-code-hash-migration/lib.rs | 0 .../migration/Cargo.toml | 0 .../set-code-hash-migration/migration/lib.rs | 0 .../updated-incrementer/Cargo.toml | 0 .../updated-incrementer/lib.rs | 0 .../upgradeable}/set-code-hash/Cargo.toml | 0 .../upgradeable}/set-code-hash/lib.rs | 0 .../updated-incrementer/Cargo.toml | 0 .../set-code-hash/updated-incrementer/lib.rs | 0 .../public/{ => basics}/dns/Cargo.toml | 0 .../public/{ => basics}/dns/lib.rs | 0 .../public/{ => basics}/dns/tests.rs | 0 .../public/{ => basics}/events/Cargo.toml | 0 .../events/event-def-unused/Cargo.toml | 0 .../events/event-def-unused/src/lib.rs | 0 .../{ => basics}/events/event-def/Cargo.toml | 0 .../{ => basics}/events/event-def/src/lib.rs | 0 .../{ => basics}/events/event-def2/Cargo.toml | 0 .../{ => basics}/events/event-def2/src/lib.rs | 0 .../public/{ => basics}/events/lib.rs | 0 .../public/{ => basics}/flipper/Cargo.toml | 0 .../public/{ => basics}/flipper/lib.rs | 0 .../public/{ => basics}/flipper/tests.rs | 0 .../{ => basics}/incrementer/Cargo.toml | 0 .../public/{ => basics}/incrementer/lib.rs | 0 .../public/{ => basics}/incrementer/tests.rs | 0 .../terminator}/Cargo.toml | 0 .../terminator}/lib.rs | 0 .../terminator}/tests.rs | 0 .../advanced}/Cargo.toml | 0 .../advanced}/e2e_tests.rs | 0 .../advanced}/lib.rs | 0 .../advanced}/other-contract/Cargo.toml | 0 .../advanced}/other-contract/lib.rs | 0 .../basic}/Cargo.toml | 0 .../basic}/e2e_tests.rs | 0 .../basic}/lib.rs | 0 .../basic}/other-contract/Cargo.toml | 0 .../basic}/other-contract/lib.rs | 0 .../invocation}/Cargo.toml | 0 .../invocation}/README.md | 0 .../invocation}/contract1/Cargo.toml | 0 .../invocation}/contract1/lib.rs | 0 .../invocation}/contract2/Cargo.toml | 0 .../invocation}/contract2/lib.rs | 0 .../invocation}/lib.rs | 0 .../invocation}/virtual_contract/Cargo.toml | 0 .../invocation}/virtual_contract/lib.rs | 0 .../virtual_contract_ver1/Cargo.toml | 0 .../invocation}/virtual_contract_ver1/lib.rs | 0 .../virtual_contract_ver2/Cargo.toml | 0 .../invocation}/virtual_contract_ver2/lib.rs | 0 .../multi-caller}/.images/code-hashes.png | Bin .../multi-caller}/Cargo.toml | 0 .../multi-caller}/README.md | 0 .../multi-caller}/accumulator/Cargo.toml | 0 .../multi-caller}/accumulator/lib.rs | 0 .../multi-caller}/adder/Cargo.toml | 0 .../multi-caller}/adder/lib.rs | 0 .../multi-caller}/build-all.sh | 0 .../multi-caller}/lib.rs | 0 .../multi-caller}/subber/Cargo.toml | 0 .../multi-caller}/subber/lib.rs | 0 .../multi-caller}/tests.rs | 0 .../transfer}/Cargo.toml | 0 .../transfer}/lib.rs | 0 .../transfer}/tests.rs | 0 .../public/{ => misc}/bytes/Cargo.toml | 0 .../public/{ => misc}/bytes/lib.rs | 0 .../public/{ => misc}/bytes/tests.rs | 0 .../public/{ => misc}/multisig/Cargo.toml | 0 .../public/{ => misc}/multisig/lib.rs | 0 .../public/{ => misc}/multisig/tests.rs | 0 .../{ => misc}/payment-channel/Cargo.toml | 0 .../public/{ => misc}/payment-channel/lib.rs | 0 .../{ => misc}/payment-channel/tests.rs | 0 .../wildcard}/Cargo.toml | 0 .../wildcard}/lib.rs | 0 .../wildcard}/tests.rs | 0 .../assets-precompile/Cargo.toml | 0 .../{ => runtime}/assets-precompile/README.md | 0 .../{ => runtime}/assets-precompile/lib.rs | 0 .../{ => runtime}/assets-precompile/tests.rs | 0 .../call-contract}/Cargo.toml | 0 .../call-contract}/custom-runtime/Cargo.toml | 0 .../pallet-revive-caller/Cargo.toml | 0 .../pallet-revive-caller/src/executor.rs | 0 .../pallet-revive-caller/src/lib.rs | 0 .../call-contract}/custom-runtime/src/lib.rs | 0 .../call-contract}/e2e_tests.rs | 0 .../call-contract}/lib.rs | 0 .../call-contract}/traits/Cargo.toml | 0 .../call-contract}/traits/lib.rs | 0 .../e2e-call}/Cargo.toml | 0 .../e2e-call}/lib.rs | 0 .../e2e-call}/tests.rs | 0 .../precompile}/Cargo.toml | 0 .../precompile}/lib.rs | 0 .../precompile}/tests.rs | 0 .../{contract-xcm => runtime/xcm}/Cargo.toml | 0 .../{contract-xcm => runtime/xcm}/lib.rs | 0 .../{contract-xcm => runtime/xcm}/tests.rs | 0 .../allocator}/Cargo.toml | 0 .../allocator}/lib.rs | 0 .../allocator}/tests.rs | 0 .../basic}/Cargo.toml | 0 .../basic}/e2e_tests.rs | 0 .../basic}/lib.rs | 0 .../complex}/Cargo.toml | 0 .../complex}/README.md | 0 .../complex}/lib.rs | 0 .../complex}/tests.rs | 0 .../fallible}/Cargo.toml | 0 .../fallible}/lib.rs | 0 .../fallible}/tests.rs | 0 .../public/{ => storage}/lazyvec/Cargo.toml | 0 .../public/{ => storage}/lazyvec/lib.rs | 0 .../public/{ => storage}/lazyvec/tests.rs | 0 .../public/{ => tokens}/erc1155/Cargo.toml | 0 .../public/{ => tokens}/erc1155/lib.rs | 0 .../public/{ => tokens}/erc1155/tests.rs | 0 .../public/{ => tokens}/erc20/Cargo.toml | 0 .../public/{ => tokens}/erc20/lib.rs | 0 .../public/{ => tokens}/erc20/tests.rs | 0 .../public/{ => tokens}/erc721/Cargo.toml | 0 .../public/{ => tokens}/erc721/lib.rs | 0 .../public/{ => tokens}/erc721/tests.rs | 0 .../dyn-cross-contract}/Cargo.toml | 0 .../contracts/incrementer/Cargo.toml | 0 .../contracts/incrementer/lib.rs | 0 .../dyn-cross-contract}/lib.rs | 0 .../dyn-cross-contract}/tests.rs | 0 .../dyn-cross-contract}/traits/Cargo.toml | 0 .../dyn-cross-contract}/traits/lib.rs | 0 .../{trait-erc20 => traits/erc20}/Cargo.toml | 0 .../{trait-erc20 => traits/erc20}/lib.rs | 0 .../{trait-erc20 => traits/erc20}/tests.rs | 0 .../flipper}/Cargo.toml | 0 .../{trait-flipper => traits/flipper}/lib.rs | 0 .../flipper}/tests.rs | 0 .../incrementer}/Cargo.toml | 0 .../incrementer}/lib.rs | 0 .../incrementer}/tests.rs | 0 .../incrementer}/traits/Cargo.toml | 0 .../incrementer}/traits/lib.rs | 0 168 files changed, 81 insertions(+) create mode 100644 integration-tests/public/README.md rename integration-tests/public/{conditional-compilation => advanced/conditional}/Cargo.toml (100%) mode change 100755 => 100644 rename integration-tests/public/{conditional-compilation => advanced/conditional}/lib.rs (100%) mode change 100755 => 100644 rename integration-tests/public/{conditional-compilation => advanced/conditional}/tests.rs (100%) rename integration-tests/public/{custom-environment => advanced/custom-env}/Cargo.toml (100%) rename integration-tests/public/{custom-environment => advanced/custom-env}/README.md (100%) rename integration-tests/public/{custom-environment => advanced/custom-env}/lib.rs (100%) rename integration-tests/public/{custom-environment => advanced/custom-env}/tests.rs (100%) rename integration-tests/public/{debugging-strategies => advanced/debugging}/Cargo.toml (100%) mode change 100755 => 100644 rename integration-tests/public/{debugging-strategies => advanced/debugging}/lib.rs (100%) mode change 100755 => 100644 rename integration-tests/public/{debugging-strategies => advanced/debugging}/tests.rs (100%) rename integration-tests/public/{fuzz-testing => advanced/fuzzing}/Cargo.toml (100%) rename integration-tests/public/{fuzz-testing => advanced/fuzzing}/lib.rs (100%) rename integration-tests/public/{fuzz-testing => advanced/fuzzing}/tests.rs (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/README.md (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/delegator/Cargo.toml (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/delegator/delegatee/Cargo.toml (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/delegator/delegatee/lib.rs (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/delegator/delegatee2/Cargo.toml (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/delegator/delegatee2/lib.rs (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/delegator/lib.rs (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/set-code-hash-migration/Cargo.toml (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/set-code-hash-migration/e2e_tests.rs (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/set-code-hash-migration/lib.rs (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/set-code-hash-migration/migration/Cargo.toml (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/set-code-hash-migration/migration/lib.rs (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/set-code-hash-migration/updated-incrementer/Cargo.toml (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/set-code-hash-migration/updated-incrementer/lib.rs (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/set-code-hash/Cargo.toml (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/set-code-hash/lib.rs (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/set-code-hash/updated-incrementer/Cargo.toml (100%) rename integration-tests/public/{upgradeable-contracts => advanced/upgradeable}/set-code-hash/updated-incrementer/lib.rs (100%) rename integration-tests/public/{ => basics}/dns/Cargo.toml (100%) rename integration-tests/public/{ => basics}/dns/lib.rs (100%) rename integration-tests/public/{ => basics}/dns/tests.rs (100%) rename integration-tests/public/{ => basics}/events/Cargo.toml (100%) rename integration-tests/public/{ => basics}/events/event-def-unused/Cargo.toml (100%) rename integration-tests/public/{ => basics}/events/event-def-unused/src/lib.rs (100%) rename integration-tests/public/{ => basics}/events/event-def/Cargo.toml (100%) rename integration-tests/public/{ => basics}/events/event-def/src/lib.rs (100%) rename integration-tests/public/{ => basics}/events/event-def2/Cargo.toml (100%) rename integration-tests/public/{ => basics}/events/event-def2/src/lib.rs (100%) rename integration-tests/public/{ => basics}/events/lib.rs (100%) rename integration-tests/public/{ => basics}/flipper/Cargo.toml (100%) rename integration-tests/public/{ => basics}/flipper/lib.rs (100%) rename integration-tests/public/{ => basics}/flipper/tests.rs (100%) rename integration-tests/public/{ => basics}/incrementer/Cargo.toml (100%) rename integration-tests/public/{ => basics}/incrementer/lib.rs (100%) rename integration-tests/public/{ => basics}/incrementer/tests.rs (100%) rename integration-tests/public/{contract-terminate => basics/terminator}/Cargo.toml (100%) rename integration-tests/public/{contract-terminate => basics/terminator}/lib.rs (100%) rename integration-tests/public/{contract-terminate => basics/terminator}/tests.rs (100%) rename integration-tests/public/{cross-contract-calls-advanced => cross-contract/advanced}/Cargo.toml (100%) mode change 100755 => 100644 rename integration-tests/public/{cross-contract-calls-advanced => cross-contract/advanced}/e2e_tests.rs (100%) rename integration-tests/public/{cross-contract-calls-advanced => cross-contract/advanced}/lib.rs (100%) mode change 100755 => 100644 rename integration-tests/public/{cross-contract-calls-advanced => cross-contract/advanced}/other-contract/Cargo.toml (100%) mode change 100755 => 100644 rename integration-tests/public/{cross-contract-calls-advanced => cross-contract/advanced}/other-contract/lib.rs (100%) mode change 100755 => 100644 rename integration-tests/public/{cross-contract-calls => cross-contract/basic}/Cargo.toml (100%) mode change 100755 => 100644 rename integration-tests/public/{cross-contract-calls => cross-contract/basic}/e2e_tests.rs (100%) rename integration-tests/public/{cross-contract-calls => cross-contract/basic}/lib.rs (100%) mode change 100755 => 100644 rename integration-tests/public/{cross-contract-calls => cross-contract/basic}/other-contract/Cargo.toml (100%) mode change 100755 => 100644 rename integration-tests/public/{cross-contract-calls => cross-contract/basic}/other-contract/lib.rs (100%) mode change 100755 => 100644 rename integration-tests/public/{contract-invocation => cross-contract/invocation}/Cargo.toml (100%) rename integration-tests/public/{contract-invocation => cross-contract/invocation}/README.md (100%) rename integration-tests/public/{contract-invocation => cross-contract/invocation}/contract1/Cargo.toml (100%) rename integration-tests/public/{contract-invocation => cross-contract/invocation}/contract1/lib.rs (100%) rename integration-tests/public/{contract-invocation => cross-contract/invocation}/contract2/Cargo.toml (100%) rename integration-tests/public/{contract-invocation => cross-contract/invocation}/contract2/lib.rs (100%) rename integration-tests/public/{contract-invocation => cross-contract/invocation}/lib.rs (100%) rename integration-tests/public/{contract-invocation => cross-contract/invocation}/virtual_contract/Cargo.toml (100%) rename integration-tests/public/{contract-invocation => cross-contract/invocation}/virtual_contract/lib.rs (100%) rename integration-tests/public/{contract-invocation => cross-contract/invocation}/virtual_contract_ver1/Cargo.toml (100%) rename integration-tests/public/{contract-invocation => cross-contract/invocation}/virtual_contract_ver1/lib.rs (100%) rename integration-tests/public/{contract-invocation => cross-contract/invocation}/virtual_contract_ver2/Cargo.toml (100%) rename integration-tests/public/{contract-invocation => cross-contract/invocation}/virtual_contract_ver2/lib.rs (100%) rename integration-tests/public/{multi-contract-caller => cross-contract/multi-caller}/.images/code-hashes.png (100%) rename integration-tests/public/{multi-contract-caller => cross-contract/multi-caller}/Cargo.toml (100%) rename integration-tests/public/{multi-contract-caller => cross-contract/multi-caller}/README.md (100%) rename integration-tests/public/{multi-contract-caller => cross-contract/multi-caller}/accumulator/Cargo.toml (100%) rename integration-tests/public/{multi-contract-caller => cross-contract/multi-caller}/accumulator/lib.rs (100%) rename integration-tests/public/{multi-contract-caller => cross-contract/multi-caller}/adder/Cargo.toml (100%) rename integration-tests/public/{multi-contract-caller => cross-contract/multi-caller}/adder/lib.rs (100%) rename integration-tests/public/{multi-contract-caller => cross-contract/multi-caller}/build-all.sh (100%) mode change 100755 => 100644 rename integration-tests/public/{multi-contract-caller => cross-contract/multi-caller}/lib.rs (100%) rename integration-tests/public/{multi-contract-caller => cross-contract/multi-caller}/subber/Cargo.toml (100%) rename integration-tests/public/{multi-contract-caller => cross-contract/multi-caller}/subber/lib.rs (100%) rename integration-tests/public/{multi-contract-caller => cross-contract/multi-caller}/tests.rs (100%) rename integration-tests/public/{contract-transfer => cross-contract/transfer}/Cargo.toml (100%) rename integration-tests/public/{contract-transfer => cross-contract/transfer}/lib.rs (100%) rename integration-tests/public/{contract-transfer => cross-contract/transfer}/tests.rs (100%) rename integration-tests/public/{ => misc}/bytes/Cargo.toml (100%) rename integration-tests/public/{ => misc}/bytes/lib.rs (100%) rename integration-tests/public/{ => misc}/bytes/tests.rs (100%) rename integration-tests/public/{ => misc}/multisig/Cargo.toml (100%) mode change 100755 => 100644 rename integration-tests/public/{ => misc}/multisig/lib.rs (100%) mode change 100755 => 100644 rename integration-tests/public/{ => misc}/multisig/tests.rs (100%) rename integration-tests/public/{ => misc}/payment-channel/Cargo.toml (100%) mode change 100755 => 100644 rename integration-tests/public/{ => misc}/payment-channel/lib.rs (100%) mode change 100755 => 100644 rename integration-tests/public/{ => misc}/payment-channel/tests.rs (100%) rename integration-tests/public/{wildcard-selector => misc/wildcard}/Cargo.toml (100%) rename integration-tests/public/{wildcard-selector => misc/wildcard}/lib.rs (100%) rename integration-tests/public/{wildcard-selector => misc/wildcard}/tests.rs (100%) rename integration-tests/public/{ => runtime}/assets-precompile/Cargo.toml (100%) rename integration-tests/public/{ => runtime}/assets-precompile/README.md (100%) rename integration-tests/public/{ => runtime}/assets-precompile/lib.rs (100%) rename integration-tests/public/{ => runtime}/assets-precompile/tests.rs (100%) rename integration-tests/public/{runtime-call-contract => runtime/call-contract}/Cargo.toml (100%) rename integration-tests/public/{runtime-call-contract => runtime/call-contract}/custom-runtime/Cargo.toml (100%) rename integration-tests/public/{runtime-call-contract => runtime/call-contract}/custom-runtime/pallet-revive-caller/Cargo.toml (100%) rename integration-tests/public/{runtime-call-contract => runtime/call-contract}/custom-runtime/pallet-revive-caller/src/executor.rs (100%) rename integration-tests/public/{runtime-call-contract => runtime/call-contract}/custom-runtime/pallet-revive-caller/src/lib.rs (100%) rename integration-tests/public/{runtime-call-contract => runtime/call-contract}/custom-runtime/src/lib.rs (100%) rename integration-tests/public/{runtime-call-contract => runtime/call-contract}/e2e_tests.rs (100%) rename integration-tests/public/{runtime-call-contract => runtime/call-contract}/lib.rs (100%) rename integration-tests/public/{runtime-call-contract => runtime/call-contract}/traits/Cargo.toml (100%) rename integration-tests/public/{runtime-call-contract => runtime/call-contract}/traits/lib.rs (100%) rename integration-tests/public/{e2e-call-runtime => runtime/e2e-call}/Cargo.toml (100%) rename integration-tests/public/{e2e-call-runtime => runtime/e2e-call}/lib.rs (100%) rename integration-tests/public/{e2e-call-runtime => runtime/e2e-call}/tests.rs (100%) rename integration-tests/public/{precompile-demo => runtime/precompile}/Cargo.toml (100%) rename integration-tests/public/{precompile-demo => runtime/precompile}/lib.rs (100%) rename integration-tests/public/{precompile-demo => runtime/precompile}/tests.rs (100%) rename integration-tests/public/{contract-xcm => runtime/xcm}/Cargo.toml (100%) rename integration-tests/public/{contract-xcm => runtime/xcm}/lib.rs (100%) rename integration-tests/public/{contract-xcm => runtime/xcm}/tests.rs (100%) rename integration-tests/public/{custom-allocator => storage/allocator}/Cargo.toml (100%) mode change 100755 => 100644 rename integration-tests/public/{custom-allocator => storage/allocator}/lib.rs (100%) mode change 100755 => 100644 rename integration-tests/public/{custom-allocator => storage/allocator}/tests.rs (100%) rename integration-tests/public/{contract-storage => storage/basic}/Cargo.toml (100%) mode change 100755 => 100644 rename integration-tests/public/{contract-storage => storage/basic}/e2e_tests.rs (100%) rename integration-tests/public/{contract-storage => storage/basic}/lib.rs (100%) mode change 100755 => 100644 rename integration-tests/public/{complex-storage-structures => storage/complex}/Cargo.toml (100%) rename integration-tests/public/{complex-storage-structures => storage/complex}/README.md (100%) rename integration-tests/public/{complex-storage-structures => storage/complex}/lib.rs (100%) rename integration-tests/public/{complex-storage-structures => storage/complex}/tests.rs (100%) rename integration-tests/public/{fallible-setter => storage/fallible}/Cargo.toml (100%) rename integration-tests/public/{fallible-setter => storage/fallible}/lib.rs (100%) rename integration-tests/public/{fallible-setter => storage/fallible}/tests.rs (100%) rename integration-tests/public/{ => storage}/lazyvec/Cargo.toml (100%) mode change 100755 => 100644 rename integration-tests/public/{ => storage}/lazyvec/lib.rs (100%) mode change 100755 => 100644 rename integration-tests/public/{ => storage}/lazyvec/tests.rs (100%) rename integration-tests/public/{ => tokens}/erc1155/Cargo.toml (100%) rename integration-tests/public/{ => tokens}/erc1155/lib.rs (100%) rename integration-tests/public/{ => tokens}/erc1155/tests.rs (100%) rename integration-tests/public/{ => tokens}/erc20/Cargo.toml (100%) rename integration-tests/public/{ => tokens}/erc20/lib.rs (100%) rename integration-tests/public/{ => tokens}/erc20/tests.rs (100%) rename integration-tests/public/{ => tokens}/erc721/Cargo.toml (100%) rename integration-tests/public/{ => tokens}/erc721/lib.rs (100%) rename integration-tests/public/{ => tokens}/erc721/tests.rs (100%) rename integration-tests/public/{trait-dyn-cross-contract-calls => traits/dyn-cross-contract}/Cargo.toml (100%) rename integration-tests/public/{trait-dyn-cross-contract-calls => traits/dyn-cross-contract}/contracts/incrementer/Cargo.toml (100%) rename integration-tests/public/{trait-dyn-cross-contract-calls => traits/dyn-cross-contract}/contracts/incrementer/lib.rs (100%) rename integration-tests/public/{trait-dyn-cross-contract-calls => traits/dyn-cross-contract}/lib.rs (100%) rename integration-tests/public/{trait-dyn-cross-contract-calls => traits/dyn-cross-contract}/tests.rs (100%) rename integration-tests/public/{trait-dyn-cross-contract-calls => traits/dyn-cross-contract}/traits/Cargo.toml (100%) rename integration-tests/public/{trait-dyn-cross-contract-calls => traits/dyn-cross-contract}/traits/lib.rs (100%) rename integration-tests/public/{trait-erc20 => traits/erc20}/Cargo.toml (100%) rename integration-tests/public/{trait-erc20 => traits/erc20}/lib.rs (100%) rename integration-tests/public/{trait-erc20 => traits/erc20}/tests.rs (100%) rename integration-tests/public/{trait-flipper => traits/flipper}/Cargo.toml (100%) rename integration-tests/public/{trait-flipper => traits/flipper}/lib.rs (100%) rename integration-tests/public/{trait-flipper => traits/flipper}/tests.rs (100%) rename integration-tests/public/{trait-incrementer => traits/incrementer}/Cargo.toml (100%) rename integration-tests/public/{trait-incrementer => traits/incrementer}/lib.rs (100%) rename integration-tests/public/{trait-incrementer => traits/incrementer}/tests.rs (100%) rename integration-tests/public/{trait-incrementer => traits/incrementer}/traits/Cargo.toml (100%) rename integration-tests/public/{trait-incrementer => traits/incrementer}/traits/lib.rs (100%) diff --git a/integration-tests/public/README.md b/integration-tests/public/README.md new file mode 100644 index 0000000000..f76b4fc4a4 --- /dev/null +++ b/integration-tests/public/README.md @@ -0,0 +1,81 @@ +# ink! Smart Contract Examples & Integration Tests + +Welcome to the collection of example smart contracts and integration tests for [ink!](https://github.com/use-ink/ink), the relentless smart contract language for Polkadot and Substrate. + +This directory contains a curated set of examples demonstrates everything from basic "Hello World" contracts to advanced upgradeability patterns and cross-chain messaging (XCM). + +## Directory Structure + +The examples are organized into categories to help you navigate from foundational concepts to complex implementations. + +### Basics +*Fundamental building blocks for learning ink!.* + +- **[`flipper`](basics/flipper)**: The classic "Hello World" of smart contracts. A simple boolean value you can flip. +- **[`incrementer`](basics/incrementer)**: Demonstrates simple state mutation with integers. +- **[`dns`](basics/dns)**: Shows how to use the storage `Mapping` type. +- **[`events`](basics/events)**: Examples of defining and emitting events. +- **[`terminator`](basics/terminator)**: How to remove a contract from storage using `terminate`. + +### 🪙 Tokens +*Implementations of standard token interfaces.* + +- **[`erc20`](tokens/erc20)**: Complete implementation of the ERC-20 fungible token standard. +- **[`erc721`](tokens/erc721)**: Non-Fungible Token (NFT) standard implementation. +- **[`erc1155`](tokens/erc1155)**: Multi-Token standard implementation. + +### Traits +*Defining shared behavior using Rust traits.* + +- **[`erc20`](traits/erc20)**: ERC-20 implemented using ink! trait definitions. +- **[`flipper`](traits/flipper)**: The flipper contract, but defined via a trait. +- **[`incrementer`](traits/incrementer)**: Trait-based incrementer. +- **[`dyn-cross-contract`](traits/dyn-cross-contract)**: How to make cross-contract calls using dynamic trait dispatch. + +### Cross-Contract Interactions +*Calling other contracts from your contract.* + +- **[`basic`](cross-contract/basic)**: Simple example of one contract calling another. +- **[`advanced`](cross-contract/advanced)**: Using call builders to set gas limits and storage deposits. +- **[`multi-caller`](cross-contract/multi-caller)**: A contract that can switch between calling different target contracts. +- **[`transfer`](cross-contract/transfer)**: Sending value (tokens) to another contract. +- **[`invocation`](cross-contract/invocation)**: Instantiating new contracts from within a contract. + +### Storage +*Advanced storage layouts and data structures.* + +- **[`basic`](storage/basic)**: Overview of available storage types. +- **[`complex`](storage/complex)**: Nested structs and Enums in storage. +- **[`lazyvec`](storage/lazyvec)**: Using `Lazy` vector types for gas optimization. +- **[`allocator`](storage/allocator)**: Using a custom heap allocator (e.g., `bumpalo`). + +### Runtime & Chain +*Interacting with the Substrate chain and runtime.* + +- **[`call-contract`](runtime/call-contract)**: The runtime calling into a contract. +- **[`e2e-call`](runtime/e2e-call)**: A contract calling a runtime dispatchable (pallet function). +- **[`xcm`](runtime/xcm)**: Cross-Consensus Messaging examples. +- **[`precompile`](runtime/precompile)**: interacting with runtime precompiles. + +### Advanced +*Complex patterns and niche features.* + +- **[`upgradeable`](advanced/upgradeable)**: Contracts that can upgrade their own code (`set_code_hash`). +- **[`custom-env`](advanced/custom-env)**: Defining custom chain extensions and environment types. +- **[`fuzzing`](advanced/fuzzing)**: Setup for property-based fuzz testing. +- **[`debugging`](advanced/debugging)**: Techniques for interpreting debug buffers. + +## Running the Tests + +Most examples in this directory are standard Rust crates. You can run their tests using Cargo. + +```bash +cd basics/flipper +cargo test +``` + +For End-to-End (E2E) tests that require a running Substrate node (like `substrate-contracts-node`), look for `e2e_tests.rs` files and ensure your environment is set up correctly with the `PINK_Runtime` or a local node. + +## Contributing + +If you are adding a new example, please place it in the most appropriate category directory. Ensure it includes a `README.md` and basic tests. diff --git a/integration-tests/public/conditional-compilation/Cargo.toml b/integration-tests/public/advanced/conditional/Cargo.toml old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/conditional-compilation/Cargo.toml rename to integration-tests/public/advanced/conditional/Cargo.toml diff --git a/integration-tests/public/conditional-compilation/lib.rs b/integration-tests/public/advanced/conditional/lib.rs old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/conditional-compilation/lib.rs rename to integration-tests/public/advanced/conditional/lib.rs diff --git a/integration-tests/public/conditional-compilation/tests.rs b/integration-tests/public/advanced/conditional/tests.rs similarity index 100% rename from integration-tests/public/conditional-compilation/tests.rs rename to integration-tests/public/advanced/conditional/tests.rs diff --git a/integration-tests/public/custom-environment/Cargo.toml b/integration-tests/public/advanced/custom-env/Cargo.toml similarity index 100% rename from integration-tests/public/custom-environment/Cargo.toml rename to integration-tests/public/advanced/custom-env/Cargo.toml diff --git a/integration-tests/public/custom-environment/README.md b/integration-tests/public/advanced/custom-env/README.md similarity index 100% rename from integration-tests/public/custom-environment/README.md rename to integration-tests/public/advanced/custom-env/README.md diff --git a/integration-tests/public/custom-environment/lib.rs b/integration-tests/public/advanced/custom-env/lib.rs similarity index 100% rename from integration-tests/public/custom-environment/lib.rs rename to integration-tests/public/advanced/custom-env/lib.rs diff --git a/integration-tests/public/custom-environment/tests.rs b/integration-tests/public/advanced/custom-env/tests.rs similarity index 100% rename from integration-tests/public/custom-environment/tests.rs rename to integration-tests/public/advanced/custom-env/tests.rs diff --git a/integration-tests/public/debugging-strategies/Cargo.toml b/integration-tests/public/advanced/debugging/Cargo.toml old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/debugging-strategies/Cargo.toml rename to integration-tests/public/advanced/debugging/Cargo.toml diff --git a/integration-tests/public/debugging-strategies/lib.rs b/integration-tests/public/advanced/debugging/lib.rs old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/debugging-strategies/lib.rs rename to integration-tests/public/advanced/debugging/lib.rs diff --git a/integration-tests/public/debugging-strategies/tests.rs b/integration-tests/public/advanced/debugging/tests.rs similarity index 100% rename from integration-tests/public/debugging-strategies/tests.rs rename to integration-tests/public/advanced/debugging/tests.rs diff --git a/integration-tests/public/fuzz-testing/Cargo.toml b/integration-tests/public/advanced/fuzzing/Cargo.toml similarity index 100% rename from integration-tests/public/fuzz-testing/Cargo.toml rename to integration-tests/public/advanced/fuzzing/Cargo.toml diff --git a/integration-tests/public/fuzz-testing/lib.rs b/integration-tests/public/advanced/fuzzing/lib.rs similarity index 100% rename from integration-tests/public/fuzz-testing/lib.rs rename to integration-tests/public/advanced/fuzzing/lib.rs diff --git a/integration-tests/public/fuzz-testing/tests.rs b/integration-tests/public/advanced/fuzzing/tests.rs similarity index 100% rename from integration-tests/public/fuzz-testing/tests.rs rename to integration-tests/public/advanced/fuzzing/tests.rs diff --git a/integration-tests/public/upgradeable-contracts/README.md b/integration-tests/public/advanced/upgradeable/README.md similarity index 100% rename from integration-tests/public/upgradeable-contracts/README.md rename to integration-tests/public/advanced/upgradeable/README.md diff --git a/integration-tests/public/upgradeable-contracts/delegator/Cargo.toml b/integration-tests/public/advanced/upgradeable/delegator/Cargo.toml similarity index 100% rename from integration-tests/public/upgradeable-contracts/delegator/Cargo.toml rename to integration-tests/public/advanced/upgradeable/delegator/Cargo.toml diff --git a/integration-tests/public/upgradeable-contracts/delegator/delegatee/Cargo.toml b/integration-tests/public/advanced/upgradeable/delegator/delegatee/Cargo.toml similarity index 100% rename from integration-tests/public/upgradeable-contracts/delegator/delegatee/Cargo.toml rename to integration-tests/public/advanced/upgradeable/delegator/delegatee/Cargo.toml diff --git a/integration-tests/public/upgradeable-contracts/delegator/delegatee/lib.rs b/integration-tests/public/advanced/upgradeable/delegator/delegatee/lib.rs similarity index 100% rename from integration-tests/public/upgradeable-contracts/delegator/delegatee/lib.rs rename to integration-tests/public/advanced/upgradeable/delegator/delegatee/lib.rs diff --git a/integration-tests/public/upgradeable-contracts/delegator/delegatee2/Cargo.toml b/integration-tests/public/advanced/upgradeable/delegator/delegatee2/Cargo.toml similarity index 100% rename from integration-tests/public/upgradeable-contracts/delegator/delegatee2/Cargo.toml rename to integration-tests/public/advanced/upgradeable/delegator/delegatee2/Cargo.toml diff --git a/integration-tests/public/upgradeable-contracts/delegator/delegatee2/lib.rs b/integration-tests/public/advanced/upgradeable/delegator/delegatee2/lib.rs similarity index 100% rename from integration-tests/public/upgradeable-contracts/delegator/delegatee2/lib.rs rename to integration-tests/public/advanced/upgradeable/delegator/delegatee2/lib.rs diff --git a/integration-tests/public/upgradeable-contracts/delegator/lib.rs b/integration-tests/public/advanced/upgradeable/delegator/lib.rs similarity index 100% rename from integration-tests/public/upgradeable-contracts/delegator/lib.rs rename to integration-tests/public/advanced/upgradeable/delegator/lib.rs diff --git a/integration-tests/public/upgradeable-contracts/set-code-hash-migration/Cargo.toml b/integration-tests/public/advanced/upgradeable/set-code-hash-migration/Cargo.toml similarity index 100% rename from integration-tests/public/upgradeable-contracts/set-code-hash-migration/Cargo.toml rename to integration-tests/public/advanced/upgradeable/set-code-hash-migration/Cargo.toml diff --git a/integration-tests/public/upgradeable-contracts/set-code-hash-migration/e2e_tests.rs b/integration-tests/public/advanced/upgradeable/set-code-hash-migration/e2e_tests.rs similarity index 100% rename from integration-tests/public/upgradeable-contracts/set-code-hash-migration/e2e_tests.rs rename to integration-tests/public/advanced/upgradeable/set-code-hash-migration/e2e_tests.rs diff --git a/integration-tests/public/upgradeable-contracts/set-code-hash-migration/lib.rs b/integration-tests/public/advanced/upgradeable/set-code-hash-migration/lib.rs similarity index 100% rename from integration-tests/public/upgradeable-contracts/set-code-hash-migration/lib.rs rename to integration-tests/public/advanced/upgradeable/set-code-hash-migration/lib.rs diff --git a/integration-tests/public/upgradeable-contracts/set-code-hash-migration/migration/Cargo.toml b/integration-tests/public/advanced/upgradeable/set-code-hash-migration/migration/Cargo.toml similarity index 100% rename from integration-tests/public/upgradeable-contracts/set-code-hash-migration/migration/Cargo.toml rename to integration-tests/public/advanced/upgradeable/set-code-hash-migration/migration/Cargo.toml diff --git a/integration-tests/public/upgradeable-contracts/set-code-hash-migration/migration/lib.rs b/integration-tests/public/advanced/upgradeable/set-code-hash-migration/migration/lib.rs similarity index 100% rename from integration-tests/public/upgradeable-contracts/set-code-hash-migration/migration/lib.rs rename to integration-tests/public/advanced/upgradeable/set-code-hash-migration/migration/lib.rs diff --git a/integration-tests/public/upgradeable-contracts/set-code-hash-migration/updated-incrementer/Cargo.toml b/integration-tests/public/advanced/upgradeable/set-code-hash-migration/updated-incrementer/Cargo.toml similarity index 100% rename from integration-tests/public/upgradeable-contracts/set-code-hash-migration/updated-incrementer/Cargo.toml rename to integration-tests/public/advanced/upgradeable/set-code-hash-migration/updated-incrementer/Cargo.toml diff --git a/integration-tests/public/upgradeable-contracts/set-code-hash-migration/updated-incrementer/lib.rs b/integration-tests/public/advanced/upgradeable/set-code-hash-migration/updated-incrementer/lib.rs similarity index 100% rename from integration-tests/public/upgradeable-contracts/set-code-hash-migration/updated-incrementer/lib.rs rename to integration-tests/public/advanced/upgradeable/set-code-hash-migration/updated-incrementer/lib.rs diff --git a/integration-tests/public/upgradeable-contracts/set-code-hash/Cargo.toml b/integration-tests/public/advanced/upgradeable/set-code-hash/Cargo.toml similarity index 100% rename from integration-tests/public/upgradeable-contracts/set-code-hash/Cargo.toml rename to integration-tests/public/advanced/upgradeable/set-code-hash/Cargo.toml diff --git a/integration-tests/public/upgradeable-contracts/set-code-hash/lib.rs b/integration-tests/public/advanced/upgradeable/set-code-hash/lib.rs similarity index 100% rename from integration-tests/public/upgradeable-contracts/set-code-hash/lib.rs rename to integration-tests/public/advanced/upgradeable/set-code-hash/lib.rs diff --git a/integration-tests/public/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml b/integration-tests/public/advanced/upgradeable/set-code-hash/updated-incrementer/Cargo.toml similarity index 100% rename from integration-tests/public/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml rename to integration-tests/public/advanced/upgradeable/set-code-hash/updated-incrementer/Cargo.toml diff --git a/integration-tests/public/upgradeable-contracts/set-code-hash/updated-incrementer/lib.rs b/integration-tests/public/advanced/upgradeable/set-code-hash/updated-incrementer/lib.rs similarity index 100% rename from integration-tests/public/upgradeable-contracts/set-code-hash/updated-incrementer/lib.rs rename to integration-tests/public/advanced/upgradeable/set-code-hash/updated-incrementer/lib.rs diff --git a/integration-tests/public/dns/Cargo.toml b/integration-tests/public/basics/dns/Cargo.toml similarity index 100% rename from integration-tests/public/dns/Cargo.toml rename to integration-tests/public/basics/dns/Cargo.toml diff --git a/integration-tests/public/dns/lib.rs b/integration-tests/public/basics/dns/lib.rs similarity index 100% rename from integration-tests/public/dns/lib.rs rename to integration-tests/public/basics/dns/lib.rs diff --git a/integration-tests/public/dns/tests.rs b/integration-tests/public/basics/dns/tests.rs similarity index 100% rename from integration-tests/public/dns/tests.rs rename to integration-tests/public/basics/dns/tests.rs diff --git a/integration-tests/public/events/Cargo.toml b/integration-tests/public/basics/events/Cargo.toml similarity index 100% rename from integration-tests/public/events/Cargo.toml rename to integration-tests/public/basics/events/Cargo.toml diff --git a/integration-tests/public/events/event-def-unused/Cargo.toml b/integration-tests/public/basics/events/event-def-unused/Cargo.toml similarity index 100% rename from integration-tests/public/events/event-def-unused/Cargo.toml rename to integration-tests/public/basics/events/event-def-unused/Cargo.toml diff --git a/integration-tests/public/events/event-def-unused/src/lib.rs b/integration-tests/public/basics/events/event-def-unused/src/lib.rs similarity index 100% rename from integration-tests/public/events/event-def-unused/src/lib.rs rename to integration-tests/public/basics/events/event-def-unused/src/lib.rs diff --git a/integration-tests/public/events/event-def/Cargo.toml b/integration-tests/public/basics/events/event-def/Cargo.toml similarity index 100% rename from integration-tests/public/events/event-def/Cargo.toml rename to integration-tests/public/basics/events/event-def/Cargo.toml diff --git a/integration-tests/public/events/event-def/src/lib.rs b/integration-tests/public/basics/events/event-def/src/lib.rs similarity index 100% rename from integration-tests/public/events/event-def/src/lib.rs rename to integration-tests/public/basics/events/event-def/src/lib.rs diff --git a/integration-tests/public/events/event-def2/Cargo.toml b/integration-tests/public/basics/events/event-def2/Cargo.toml similarity index 100% rename from integration-tests/public/events/event-def2/Cargo.toml rename to integration-tests/public/basics/events/event-def2/Cargo.toml diff --git a/integration-tests/public/events/event-def2/src/lib.rs b/integration-tests/public/basics/events/event-def2/src/lib.rs similarity index 100% rename from integration-tests/public/events/event-def2/src/lib.rs rename to integration-tests/public/basics/events/event-def2/src/lib.rs diff --git a/integration-tests/public/events/lib.rs b/integration-tests/public/basics/events/lib.rs similarity index 100% rename from integration-tests/public/events/lib.rs rename to integration-tests/public/basics/events/lib.rs diff --git a/integration-tests/public/flipper/Cargo.toml b/integration-tests/public/basics/flipper/Cargo.toml similarity index 100% rename from integration-tests/public/flipper/Cargo.toml rename to integration-tests/public/basics/flipper/Cargo.toml diff --git a/integration-tests/public/flipper/lib.rs b/integration-tests/public/basics/flipper/lib.rs similarity index 100% rename from integration-tests/public/flipper/lib.rs rename to integration-tests/public/basics/flipper/lib.rs diff --git a/integration-tests/public/flipper/tests.rs b/integration-tests/public/basics/flipper/tests.rs similarity index 100% rename from integration-tests/public/flipper/tests.rs rename to integration-tests/public/basics/flipper/tests.rs diff --git a/integration-tests/public/incrementer/Cargo.toml b/integration-tests/public/basics/incrementer/Cargo.toml similarity index 100% rename from integration-tests/public/incrementer/Cargo.toml rename to integration-tests/public/basics/incrementer/Cargo.toml diff --git a/integration-tests/public/incrementer/lib.rs b/integration-tests/public/basics/incrementer/lib.rs similarity index 100% rename from integration-tests/public/incrementer/lib.rs rename to integration-tests/public/basics/incrementer/lib.rs diff --git a/integration-tests/public/incrementer/tests.rs b/integration-tests/public/basics/incrementer/tests.rs similarity index 100% rename from integration-tests/public/incrementer/tests.rs rename to integration-tests/public/basics/incrementer/tests.rs diff --git a/integration-tests/public/contract-terminate/Cargo.toml b/integration-tests/public/basics/terminator/Cargo.toml similarity index 100% rename from integration-tests/public/contract-terminate/Cargo.toml rename to integration-tests/public/basics/terminator/Cargo.toml diff --git a/integration-tests/public/contract-terminate/lib.rs b/integration-tests/public/basics/terminator/lib.rs similarity index 100% rename from integration-tests/public/contract-terminate/lib.rs rename to integration-tests/public/basics/terminator/lib.rs diff --git a/integration-tests/public/contract-terminate/tests.rs b/integration-tests/public/basics/terminator/tests.rs similarity index 100% rename from integration-tests/public/contract-terminate/tests.rs rename to integration-tests/public/basics/terminator/tests.rs diff --git a/integration-tests/public/cross-contract-calls-advanced/Cargo.toml b/integration-tests/public/cross-contract/advanced/Cargo.toml old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/cross-contract-calls-advanced/Cargo.toml rename to integration-tests/public/cross-contract/advanced/Cargo.toml diff --git a/integration-tests/public/cross-contract-calls-advanced/e2e_tests.rs b/integration-tests/public/cross-contract/advanced/e2e_tests.rs similarity index 100% rename from integration-tests/public/cross-contract-calls-advanced/e2e_tests.rs rename to integration-tests/public/cross-contract/advanced/e2e_tests.rs diff --git a/integration-tests/public/cross-contract-calls-advanced/lib.rs b/integration-tests/public/cross-contract/advanced/lib.rs old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/cross-contract-calls-advanced/lib.rs rename to integration-tests/public/cross-contract/advanced/lib.rs diff --git a/integration-tests/public/cross-contract-calls-advanced/other-contract/Cargo.toml b/integration-tests/public/cross-contract/advanced/other-contract/Cargo.toml old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/cross-contract-calls-advanced/other-contract/Cargo.toml rename to integration-tests/public/cross-contract/advanced/other-contract/Cargo.toml diff --git a/integration-tests/public/cross-contract-calls-advanced/other-contract/lib.rs b/integration-tests/public/cross-contract/advanced/other-contract/lib.rs old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/cross-contract-calls-advanced/other-contract/lib.rs rename to integration-tests/public/cross-contract/advanced/other-contract/lib.rs diff --git a/integration-tests/public/cross-contract-calls/Cargo.toml b/integration-tests/public/cross-contract/basic/Cargo.toml old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/cross-contract-calls/Cargo.toml rename to integration-tests/public/cross-contract/basic/Cargo.toml diff --git a/integration-tests/public/cross-contract-calls/e2e_tests.rs b/integration-tests/public/cross-contract/basic/e2e_tests.rs similarity index 100% rename from integration-tests/public/cross-contract-calls/e2e_tests.rs rename to integration-tests/public/cross-contract/basic/e2e_tests.rs diff --git a/integration-tests/public/cross-contract-calls/lib.rs b/integration-tests/public/cross-contract/basic/lib.rs old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/cross-contract-calls/lib.rs rename to integration-tests/public/cross-contract/basic/lib.rs diff --git a/integration-tests/public/cross-contract-calls/other-contract/Cargo.toml b/integration-tests/public/cross-contract/basic/other-contract/Cargo.toml old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/cross-contract-calls/other-contract/Cargo.toml rename to integration-tests/public/cross-contract/basic/other-contract/Cargo.toml diff --git a/integration-tests/public/cross-contract-calls/other-contract/lib.rs b/integration-tests/public/cross-contract/basic/other-contract/lib.rs old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/cross-contract-calls/other-contract/lib.rs rename to integration-tests/public/cross-contract/basic/other-contract/lib.rs diff --git a/integration-tests/public/contract-invocation/Cargo.toml b/integration-tests/public/cross-contract/invocation/Cargo.toml similarity index 100% rename from integration-tests/public/contract-invocation/Cargo.toml rename to integration-tests/public/cross-contract/invocation/Cargo.toml diff --git a/integration-tests/public/contract-invocation/README.md b/integration-tests/public/cross-contract/invocation/README.md similarity index 100% rename from integration-tests/public/contract-invocation/README.md rename to integration-tests/public/cross-contract/invocation/README.md diff --git a/integration-tests/public/contract-invocation/contract1/Cargo.toml b/integration-tests/public/cross-contract/invocation/contract1/Cargo.toml similarity index 100% rename from integration-tests/public/contract-invocation/contract1/Cargo.toml rename to integration-tests/public/cross-contract/invocation/contract1/Cargo.toml diff --git a/integration-tests/public/contract-invocation/contract1/lib.rs b/integration-tests/public/cross-contract/invocation/contract1/lib.rs similarity index 100% rename from integration-tests/public/contract-invocation/contract1/lib.rs rename to integration-tests/public/cross-contract/invocation/contract1/lib.rs diff --git a/integration-tests/public/contract-invocation/contract2/Cargo.toml b/integration-tests/public/cross-contract/invocation/contract2/Cargo.toml similarity index 100% rename from integration-tests/public/contract-invocation/contract2/Cargo.toml rename to integration-tests/public/cross-contract/invocation/contract2/Cargo.toml diff --git a/integration-tests/public/contract-invocation/contract2/lib.rs b/integration-tests/public/cross-contract/invocation/contract2/lib.rs similarity index 100% rename from integration-tests/public/contract-invocation/contract2/lib.rs rename to integration-tests/public/cross-contract/invocation/contract2/lib.rs diff --git a/integration-tests/public/contract-invocation/lib.rs b/integration-tests/public/cross-contract/invocation/lib.rs similarity index 100% rename from integration-tests/public/contract-invocation/lib.rs rename to integration-tests/public/cross-contract/invocation/lib.rs diff --git a/integration-tests/public/contract-invocation/virtual_contract/Cargo.toml b/integration-tests/public/cross-contract/invocation/virtual_contract/Cargo.toml similarity index 100% rename from integration-tests/public/contract-invocation/virtual_contract/Cargo.toml rename to integration-tests/public/cross-contract/invocation/virtual_contract/Cargo.toml diff --git a/integration-tests/public/contract-invocation/virtual_contract/lib.rs b/integration-tests/public/cross-contract/invocation/virtual_contract/lib.rs similarity index 100% rename from integration-tests/public/contract-invocation/virtual_contract/lib.rs rename to integration-tests/public/cross-contract/invocation/virtual_contract/lib.rs diff --git a/integration-tests/public/contract-invocation/virtual_contract_ver1/Cargo.toml b/integration-tests/public/cross-contract/invocation/virtual_contract_ver1/Cargo.toml similarity index 100% rename from integration-tests/public/contract-invocation/virtual_contract_ver1/Cargo.toml rename to integration-tests/public/cross-contract/invocation/virtual_contract_ver1/Cargo.toml diff --git a/integration-tests/public/contract-invocation/virtual_contract_ver1/lib.rs b/integration-tests/public/cross-contract/invocation/virtual_contract_ver1/lib.rs similarity index 100% rename from integration-tests/public/contract-invocation/virtual_contract_ver1/lib.rs rename to integration-tests/public/cross-contract/invocation/virtual_contract_ver1/lib.rs diff --git a/integration-tests/public/contract-invocation/virtual_contract_ver2/Cargo.toml b/integration-tests/public/cross-contract/invocation/virtual_contract_ver2/Cargo.toml similarity index 100% rename from integration-tests/public/contract-invocation/virtual_contract_ver2/Cargo.toml rename to integration-tests/public/cross-contract/invocation/virtual_contract_ver2/Cargo.toml diff --git a/integration-tests/public/contract-invocation/virtual_contract_ver2/lib.rs b/integration-tests/public/cross-contract/invocation/virtual_contract_ver2/lib.rs similarity index 100% rename from integration-tests/public/contract-invocation/virtual_contract_ver2/lib.rs rename to integration-tests/public/cross-contract/invocation/virtual_contract_ver2/lib.rs diff --git a/integration-tests/public/multi-contract-caller/.images/code-hashes.png b/integration-tests/public/cross-contract/multi-caller/.images/code-hashes.png similarity index 100% rename from integration-tests/public/multi-contract-caller/.images/code-hashes.png rename to integration-tests/public/cross-contract/multi-caller/.images/code-hashes.png diff --git a/integration-tests/public/multi-contract-caller/Cargo.toml b/integration-tests/public/cross-contract/multi-caller/Cargo.toml similarity index 100% rename from integration-tests/public/multi-contract-caller/Cargo.toml rename to integration-tests/public/cross-contract/multi-caller/Cargo.toml diff --git a/integration-tests/public/multi-contract-caller/README.md b/integration-tests/public/cross-contract/multi-caller/README.md similarity index 100% rename from integration-tests/public/multi-contract-caller/README.md rename to integration-tests/public/cross-contract/multi-caller/README.md diff --git a/integration-tests/public/multi-contract-caller/accumulator/Cargo.toml b/integration-tests/public/cross-contract/multi-caller/accumulator/Cargo.toml similarity index 100% rename from integration-tests/public/multi-contract-caller/accumulator/Cargo.toml rename to integration-tests/public/cross-contract/multi-caller/accumulator/Cargo.toml diff --git a/integration-tests/public/multi-contract-caller/accumulator/lib.rs b/integration-tests/public/cross-contract/multi-caller/accumulator/lib.rs similarity index 100% rename from integration-tests/public/multi-contract-caller/accumulator/lib.rs rename to integration-tests/public/cross-contract/multi-caller/accumulator/lib.rs diff --git a/integration-tests/public/multi-contract-caller/adder/Cargo.toml b/integration-tests/public/cross-contract/multi-caller/adder/Cargo.toml similarity index 100% rename from integration-tests/public/multi-contract-caller/adder/Cargo.toml rename to integration-tests/public/cross-contract/multi-caller/adder/Cargo.toml diff --git a/integration-tests/public/multi-contract-caller/adder/lib.rs b/integration-tests/public/cross-contract/multi-caller/adder/lib.rs similarity index 100% rename from integration-tests/public/multi-contract-caller/adder/lib.rs rename to integration-tests/public/cross-contract/multi-caller/adder/lib.rs diff --git a/integration-tests/public/multi-contract-caller/build-all.sh b/integration-tests/public/cross-contract/multi-caller/build-all.sh old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/multi-contract-caller/build-all.sh rename to integration-tests/public/cross-contract/multi-caller/build-all.sh diff --git a/integration-tests/public/multi-contract-caller/lib.rs b/integration-tests/public/cross-contract/multi-caller/lib.rs similarity index 100% rename from integration-tests/public/multi-contract-caller/lib.rs rename to integration-tests/public/cross-contract/multi-caller/lib.rs diff --git a/integration-tests/public/multi-contract-caller/subber/Cargo.toml b/integration-tests/public/cross-contract/multi-caller/subber/Cargo.toml similarity index 100% rename from integration-tests/public/multi-contract-caller/subber/Cargo.toml rename to integration-tests/public/cross-contract/multi-caller/subber/Cargo.toml diff --git a/integration-tests/public/multi-contract-caller/subber/lib.rs b/integration-tests/public/cross-contract/multi-caller/subber/lib.rs similarity index 100% rename from integration-tests/public/multi-contract-caller/subber/lib.rs rename to integration-tests/public/cross-contract/multi-caller/subber/lib.rs diff --git a/integration-tests/public/multi-contract-caller/tests.rs b/integration-tests/public/cross-contract/multi-caller/tests.rs similarity index 100% rename from integration-tests/public/multi-contract-caller/tests.rs rename to integration-tests/public/cross-contract/multi-caller/tests.rs diff --git a/integration-tests/public/contract-transfer/Cargo.toml b/integration-tests/public/cross-contract/transfer/Cargo.toml similarity index 100% rename from integration-tests/public/contract-transfer/Cargo.toml rename to integration-tests/public/cross-contract/transfer/Cargo.toml diff --git a/integration-tests/public/contract-transfer/lib.rs b/integration-tests/public/cross-contract/transfer/lib.rs similarity index 100% rename from integration-tests/public/contract-transfer/lib.rs rename to integration-tests/public/cross-contract/transfer/lib.rs diff --git a/integration-tests/public/contract-transfer/tests.rs b/integration-tests/public/cross-contract/transfer/tests.rs similarity index 100% rename from integration-tests/public/contract-transfer/tests.rs rename to integration-tests/public/cross-contract/transfer/tests.rs diff --git a/integration-tests/public/bytes/Cargo.toml b/integration-tests/public/misc/bytes/Cargo.toml similarity index 100% rename from integration-tests/public/bytes/Cargo.toml rename to integration-tests/public/misc/bytes/Cargo.toml diff --git a/integration-tests/public/bytes/lib.rs b/integration-tests/public/misc/bytes/lib.rs similarity index 100% rename from integration-tests/public/bytes/lib.rs rename to integration-tests/public/misc/bytes/lib.rs diff --git a/integration-tests/public/bytes/tests.rs b/integration-tests/public/misc/bytes/tests.rs similarity index 100% rename from integration-tests/public/bytes/tests.rs rename to integration-tests/public/misc/bytes/tests.rs diff --git a/integration-tests/public/multisig/Cargo.toml b/integration-tests/public/misc/multisig/Cargo.toml old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/multisig/Cargo.toml rename to integration-tests/public/misc/multisig/Cargo.toml diff --git a/integration-tests/public/multisig/lib.rs b/integration-tests/public/misc/multisig/lib.rs old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/multisig/lib.rs rename to integration-tests/public/misc/multisig/lib.rs diff --git a/integration-tests/public/multisig/tests.rs b/integration-tests/public/misc/multisig/tests.rs similarity index 100% rename from integration-tests/public/multisig/tests.rs rename to integration-tests/public/misc/multisig/tests.rs diff --git a/integration-tests/public/payment-channel/Cargo.toml b/integration-tests/public/misc/payment-channel/Cargo.toml old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/payment-channel/Cargo.toml rename to integration-tests/public/misc/payment-channel/Cargo.toml diff --git a/integration-tests/public/payment-channel/lib.rs b/integration-tests/public/misc/payment-channel/lib.rs old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/payment-channel/lib.rs rename to integration-tests/public/misc/payment-channel/lib.rs diff --git a/integration-tests/public/payment-channel/tests.rs b/integration-tests/public/misc/payment-channel/tests.rs similarity index 100% rename from integration-tests/public/payment-channel/tests.rs rename to integration-tests/public/misc/payment-channel/tests.rs diff --git a/integration-tests/public/wildcard-selector/Cargo.toml b/integration-tests/public/misc/wildcard/Cargo.toml similarity index 100% rename from integration-tests/public/wildcard-selector/Cargo.toml rename to integration-tests/public/misc/wildcard/Cargo.toml diff --git a/integration-tests/public/wildcard-selector/lib.rs b/integration-tests/public/misc/wildcard/lib.rs similarity index 100% rename from integration-tests/public/wildcard-selector/lib.rs rename to integration-tests/public/misc/wildcard/lib.rs diff --git a/integration-tests/public/wildcard-selector/tests.rs b/integration-tests/public/misc/wildcard/tests.rs similarity index 100% rename from integration-tests/public/wildcard-selector/tests.rs rename to integration-tests/public/misc/wildcard/tests.rs diff --git a/integration-tests/public/assets-precompile/Cargo.toml b/integration-tests/public/runtime/assets-precompile/Cargo.toml similarity index 100% rename from integration-tests/public/assets-precompile/Cargo.toml rename to integration-tests/public/runtime/assets-precompile/Cargo.toml diff --git a/integration-tests/public/assets-precompile/README.md b/integration-tests/public/runtime/assets-precompile/README.md similarity index 100% rename from integration-tests/public/assets-precompile/README.md rename to integration-tests/public/runtime/assets-precompile/README.md diff --git a/integration-tests/public/assets-precompile/lib.rs b/integration-tests/public/runtime/assets-precompile/lib.rs similarity index 100% rename from integration-tests/public/assets-precompile/lib.rs rename to integration-tests/public/runtime/assets-precompile/lib.rs diff --git a/integration-tests/public/assets-precompile/tests.rs b/integration-tests/public/runtime/assets-precompile/tests.rs similarity index 100% rename from integration-tests/public/assets-precompile/tests.rs rename to integration-tests/public/runtime/assets-precompile/tests.rs diff --git a/integration-tests/public/runtime-call-contract/Cargo.toml b/integration-tests/public/runtime/call-contract/Cargo.toml similarity index 100% rename from integration-tests/public/runtime-call-contract/Cargo.toml rename to integration-tests/public/runtime/call-contract/Cargo.toml diff --git a/integration-tests/public/runtime-call-contract/custom-runtime/Cargo.toml b/integration-tests/public/runtime/call-contract/custom-runtime/Cargo.toml similarity index 100% rename from integration-tests/public/runtime-call-contract/custom-runtime/Cargo.toml rename to integration-tests/public/runtime/call-contract/custom-runtime/Cargo.toml diff --git a/integration-tests/public/runtime-call-contract/custom-runtime/pallet-revive-caller/Cargo.toml b/integration-tests/public/runtime/call-contract/custom-runtime/pallet-revive-caller/Cargo.toml similarity index 100% rename from integration-tests/public/runtime-call-contract/custom-runtime/pallet-revive-caller/Cargo.toml rename to integration-tests/public/runtime/call-contract/custom-runtime/pallet-revive-caller/Cargo.toml diff --git a/integration-tests/public/runtime-call-contract/custom-runtime/pallet-revive-caller/src/executor.rs b/integration-tests/public/runtime/call-contract/custom-runtime/pallet-revive-caller/src/executor.rs similarity index 100% rename from integration-tests/public/runtime-call-contract/custom-runtime/pallet-revive-caller/src/executor.rs rename to integration-tests/public/runtime/call-contract/custom-runtime/pallet-revive-caller/src/executor.rs diff --git a/integration-tests/public/runtime-call-contract/custom-runtime/pallet-revive-caller/src/lib.rs b/integration-tests/public/runtime/call-contract/custom-runtime/pallet-revive-caller/src/lib.rs similarity index 100% rename from integration-tests/public/runtime-call-contract/custom-runtime/pallet-revive-caller/src/lib.rs rename to integration-tests/public/runtime/call-contract/custom-runtime/pallet-revive-caller/src/lib.rs diff --git a/integration-tests/public/runtime-call-contract/custom-runtime/src/lib.rs b/integration-tests/public/runtime/call-contract/custom-runtime/src/lib.rs similarity index 100% rename from integration-tests/public/runtime-call-contract/custom-runtime/src/lib.rs rename to integration-tests/public/runtime/call-contract/custom-runtime/src/lib.rs diff --git a/integration-tests/public/runtime-call-contract/e2e_tests.rs b/integration-tests/public/runtime/call-contract/e2e_tests.rs similarity index 100% rename from integration-tests/public/runtime-call-contract/e2e_tests.rs rename to integration-tests/public/runtime/call-contract/e2e_tests.rs diff --git a/integration-tests/public/runtime-call-contract/lib.rs b/integration-tests/public/runtime/call-contract/lib.rs similarity index 100% rename from integration-tests/public/runtime-call-contract/lib.rs rename to integration-tests/public/runtime/call-contract/lib.rs diff --git a/integration-tests/public/runtime-call-contract/traits/Cargo.toml b/integration-tests/public/runtime/call-contract/traits/Cargo.toml similarity index 100% rename from integration-tests/public/runtime-call-contract/traits/Cargo.toml rename to integration-tests/public/runtime/call-contract/traits/Cargo.toml diff --git a/integration-tests/public/runtime-call-contract/traits/lib.rs b/integration-tests/public/runtime/call-contract/traits/lib.rs similarity index 100% rename from integration-tests/public/runtime-call-contract/traits/lib.rs rename to integration-tests/public/runtime/call-contract/traits/lib.rs diff --git a/integration-tests/public/e2e-call-runtime/Cargo.toml b/integration-tests/public/runtime/e2e-call/Cargo.toml similarity index 100% rename from integration-tests/public/e2e-call-runtime/Cargo.toml rename to integration-tests/public/runtime/e2e-call/Cargo.toml diff --git a/integration-tests/public/e2e-call-runtime/lib.rs b/integration-tests/public/runtime/e2e-call/lib.rs similarity index 100% rename from integration-tests/public/e2e-call-runtime/lib.rs rename to integration-tests/public/runtime/e2e-call/lib.rs diff --git a/integration-tests/public/e2e-call-runtime/tests.rs b/integration-tests/public/runtime/e2e-call/tests.rs similarity index 100% rename from integration-tests/public/e2e-call-runtime/tests.rs rename to integration-tests/public/runtime/e2e-call/tests.rs diff --git a/integration-tests/public/precompile-demo/Cargo.toml b/integration-tests/public/runtime/precompile/Cargo.toml similarity index 100% rename from integration-tests/public/precompile-demo/Cargo.toml rename to integration-tests/public/runtime/precompile/Cargo.toml diff --git a/integration-tests/public/precompile-demo/lib.rs b/integration-tests/public/runtime/precompile/lib.rs similarity index 100% rename from integration-tests/public/precompile-demo/lib.rs rename to integration-tests/public/runtime/precompile/lib.rs diff --git a/integration-tests/public/precompile-demo/tests.rs b/integration-tests/public/runtime/precompile/tests.rs similarity index 100% rename from integration-tests/public/precompile-demo/tests.rs rename to integration-tests/public/runtime/precompile/tests.rs diff --git a/integration-tests/public/contract-xcm/Cargo.toml b/integration-tests/public/runtime/xcm/Cargo.toml similarity index 100% rename from integration-tests/public/contract-xcm/Cargo.toml rename to integration-tests/public/runtime/xcm/Cargo.toml diff --git a/integration-tests/public/contract-xcm/lib.rs b/integration-tests/public/runtime/xcm/lib.rs similarity index 100% rename from integration-tests/public/contract-xcm/lib.rs rename to integration-tests/public/runtime/xcm/lib.rs diff --git a/integration-tests/public/contract-xcm/tests.rs b/integration-tests/public/runtime/xcm/tests.rs similarity index 100% rename from integration-tests/public/contract-xcm/tests.rs rename to integration-tests/public/runtime/xcm/tests.rs diff --git a/integration-tests/public/custom-allocator/Cargo.toml b/integration-tests/public/storage/allocator/Cargo.toml old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/custom-allocator/Cargo.toml rename to integration-tests/public/storage/allocator/Cargo.toml diff --git a/integration-tests/public/custom-allocator/lib.rs b/integration-tests/public/storage/allocator/lib.rs old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/custom-allocator/lib.rs rename to integration-tests/public/storage/allocator/lib.rs diff --git a/integration-tests/public/custom-allocator/tests.rs b/integration-tests/public/storage/allocator/tests.rs similarity index 100% rename from integration-tests/public/custom-allocator/tests.rs rename to integration-tests/public/storage/allocator/tests.rs diff --git a/integration-tests/public/contract-storage/Cargo.toml b/integration-tests/public/storage/basic/Cargo.toml old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/contract-storage/Cargo.toml rename to integration-tests/public/storage/basic/Cargo.toml diff --git a/integration-tests/public/contract-storage/e2e_tests.rs b/integration-tests/public/storage/basic/e2e_tests.rs similarity index 100% rename from integration-tests/public/contract-storage/e2e_tests.rs rename to integration-tests/public/storage/basic/e2e_tests.rs diff --git a/integration-tests/public/contract-storage/lib.rs b/integration-tests/public/storage/basic/lib.rs old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/contract-storage/lib.rs rename to integration-tests/public/storage/basic/lib.rs diff --git a/integration-tests/public/complex-storage-structures/Cargo.toml b/integration-tests/public/storage/complex/Cargo.toml similarity index 100% rename from integration-tests/public/complex-storage-structures/Cargo.toml rename to integration-tests/public/storage/complex/Cargo.toml diff --git a/integration-tests/public/complex-storage-structures/README.md b/integration-tests/public/storage/complex/README.md similarity index 100% rename from integration-tests/public/complex-storage-structures/README.md rename to integration-tests/public/storage/complex/README.md diff --git a/integration-tests/public/complex-storage-structures/lib.rs b/integration-tests/public/storage/complex/lib.rs similarity index 100% rename from integration-tests/public/complex-storage-structures/lib.rs rename to integration-tests/public/storage/complex/lib.rs diff --git a/integration-tests/public/complex-storage-structures/tests.rs b/integration-tests/public/storage/complex/tests.rs similarity index 100% rename from integration-tests/public/complex-storage-structures/tests.rs rename to integration-tests/public/storage/complex/tests.rs diff --git a/integration-tests/public/fallible-setter/Cargo.toml b/integration-tests/public/storage/fallible/Cargo.toml similarity index 100% rename from integration-tests/public/fallible-setter/Cargo.toml rename to integration-tests/public/storage/fallible/Cargo.toml diff --git a/integration-tests/public/fallible-setter/lib.rs b/integration-tests/public/storage/fallible/lib.rs similarity index 100% rename from integration-tests/public/fallible-setter/lib.rs rename to integration-tests/public/storage/fallible/lib.rs diff --git a/integration-tests/public/fallible-setter/tests.rs b/integration-tests/public/storage/fallible/tests.rs similarity index 100% rename from integration-tests/public/fallible-setter/tests.rs rename to integration-tests/public/storage/fallible/tests.rs diff --git a/integration-tests/public/lazyvec/Cargo.toml b/integration-tests/public/storage/lazyvec/Cargo.toml old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/lazyvec/Cargo.toml rename to integration-tests/public/storage/lazyvec/Cargo.toml diff --git a/integration-tests/public/lazyvec/lib.rs b/integration-tests/public/storage/lazyvec/lib.rs old mode 100755 new mode 100644 similarity index 100% rename from integration-tests/public/lazyvec/lib.rs rename to integration-tests/public/storage/lazyvec/lib.rs diff --git a/integration-tests/public/lazyvec/tests.rs b/integration-tests/public/storage/lazyvec/tests.rs similarity index 100% rename from integration-tests/public/lazyvec/tests.rs rename to integration-tests/public/storage/lazyvec/tests.rs diff --git a/integration-tests/public/erc1155/Cargo.toml b/integration-tests/public/tokens/erc1155/Cargo.toml similarity index 100% rename from integration-tests/public/erc1155/Cargo.toml rename to integration-tests/public/tokens/erc1155/Cargo.toml diff --git a/integration-tests/public/erc1155/lib.rs b/integration-tests/public/tokens/erc1155/lib.rs similarity index 100% rename from integration-tests/public/erc1155/lib.rs rename to integration-tests/public/tokens/erc1155/lib.rs diff --git a/integration-tests/public/erc1155/tests.rs b/integration-tests/public/tokens/erc1155/tests.rs similarity index 100% rename from integration-tests/public/erc1155/tests.rs rename to integration-tests/public/tokens/erc1155/tests.rs diff --git a/integration-tests/public/erc20/Cargo.toml b/integration-tests/public/tokens/erc20/Cargo.toml similarity index 100% rename from integration-tests/public/erc20/Cargo.toml rename to integration-tests/public/tokens/erc20/Cargo.toml diff --git a/integration-tests/public/erc20/lib.rs b/integration-tests/public/tokens/erc20/lib.rs similarity index 100% rename from integration-tests/public/erc20/lib.rs rename to integration-tests/public/tokens/erc20/lib.rs diff --git a/integration-tests/public/erc20/tests.rs b/integration-tests/public/tokens/erc20/tests.rs similarity index 100% rename from integration-tests/public/erc20/tests.rs rename to integration-tests/public/tokens/erc20/tests.rs diff --git a/integration-tests/public/erc721/Cargo.toml b/integration-tests/public/tokens/erc721/Cargo.toml similarity index 100% rename from integration-tests/public/erc721/Cargo.toml rename to integration-tests/public/tokens/erc721/Cargo.toml diff --git a/integration-tests/public/erc721/lib.rs b/integration-tests/public/tokens/erc721/lib.rs similarity index 100% rename from integration-tests/public/erc721/lib.rs rename to integration-tests/public/tokens/erc721/lib.rs diff --git a/integration-tests/public/erc721/tests.rs b/integration-tests/public/tokens/erc721/tests.rs similarity index 100% rename from integration-tests/public/erc721/tests.rs rename to integration-tests/public/tokens/erc721/tests.rs diff --git a/integration-tests/public/trait-dyn-cross-contract-calls/Cargo.toml b/integration-tests/public/traits/dyn-cross-contract/Cargo.toml similarity index 100% rename from integration-tests/public/trait-dyn-cross-contract-calls/Cargo.toml rename to integration-tests/public/traits/dyn-cross-contract/Cargo.toml diff --git a/integration-tests/public/trait-dyn-cross-contract-calls/contracts/incrementer/Cargo.toml b/integration-tests/public/traits/dyn-cross-contract/contracts/incrementer/Cargo.toml similarity index 100% rename from integration-tests/public/trait-dyn-cross-contract-calls/contracts/incrementer/Cargo.toml rename to integration-tests/public/traits/dyn-cross-contract/contracts/incrementer/Cargo.toml diff --git a/integration-tests/public/trait-dyn-cross-contract-calls/contracts/incrementer/lib.rs b/integration-tests/public/traits/dyn-cross-contract/contracts/incrementer/lib.rs similarity index 100% rename from integration-tests/public/trait-dyn-cross-contract-calls/contracts/incrementer/lib.rs rename to integration-tests/public/traits/dyn-cross-contract/contracts/incrementer/lib.rs diff --git a/integration-tests/public/trait-dyn-cross-contract-calls/lib.rs b/integration-tests/public/traits/dyn-cross-contract/lib.rs similarity index 100% rename from integration-tests/public/trait-dyn-cross-contract-calls/lib.rs rename to integration-tests/public/traits/dyn-cross-contract/lib.rs diff --git a/integration-tests/public/trait-dyn-cross-contract-calls/tests.rs b/integration-tests/public/traits/dyn-cross-contract/tests.rs similarity index 100% rename from integration-tests/public/trait-dyn-cross-contract-calls/tests.rs rename to integration-tests/public/traits/dyn-cross-contract/tests.rs diff --git a/integration-tests/public/trait-dyn-cross-contract-calls/traits/Cargo.toml b/integration-tests/public/traits/dyn-cross-contract/traits/Cargo.toml similarity index 100% rename from integration-tests/public/trait-dyn-cross-contract-calls/traits/Cargo.toml rename to integration-tests/public/traits/dyn-cross-contract/traits/Cargo.toml diff --git a/integration-tests/public/trait-dyn-cross-contract-calls/traits/lib.rs b/integration-tests/public/traits/dyn-cross-contract/traits/lib.rs similarity index 100% rename from integration-tests/public/trait-dyn-cross-contract-calls/traits/lib.rs rename to integration-tests/public/traits/dyn-cross-contract/traits/lib.rs diff --git a/integration-tests/public/trait-erc20/Cargo.toml b/integration-tests/public/traits/erc20/Cargo.toml similarity index 100% rename from integration-tests/public/trait-erc20/Cargo.toml rename to integration-tests/public/traits/erc20/Cargo.toml diff --git a/integration-tests/public/trait-erc20/lib.rs b/integration-tests/public/traits/erc20/lib.rs similarity index 100% rename from integration-tests/public/trait-erc20/lib.rs rename to integration-tests/public/traits/erc20/lib.rs diff --git a/integration-tests/public/trait-erc20/tests.rs b/integration-tests/public/traits/erc20/tests.rs similarity index 100% rename from integration-tests/public/trait-erc20/tests.rs rename to integration-tests/public/traits/erc20/tests.rs diff --git a/integration-tests/public/trait-flipper/Cargo.toml b/integration-tests/public/traits/flipper/Cargo.toml similarity index 100% rename from integration-tests/public/trait-flipper/Cargo.toml rename to integration-tests/public/traits/flipper/Cargo.toml diff --git a/integration-tests/public/trait-flipper/lib.rs b/integration-tests/public/traits/flipper/lib.rs similarity index 100% rename from integration-tests/public/trait-flipper/lib.rs rename to integration-tests/public/traits/flipper/lib.rs diff --git a/integration-tests/public/trait-flipper/tests.rs b/integration-tests/public/traits/flipper/tests.rs similarity index 100% rename from integration-tests/public/trait-flipper/tests.rs rename to integration-tests/public/traits/flipper/tests.rs diff --git a/integration-tests/public/trait-incrementer/Cargo.toml b/integration-tests/public/traits/incrementer/Cargo.toml similarity index 100% rename from integration-tests/public/trait-incrementer/Cargo.toml rename to integration-tests/public/traits/incrementer/Cargo.toml diff --git a/integration-tests/public/trait-incrementer/lib.rs b/integration-tests/public/traits/incrementer/lib.rs similarity index 100% rename from integration-tests/public/trait-incrementer/lib.rs rename to integration-tests/public/traits/incrementer/lib.rs diff --git a/integration-tests/public/trait-incrementer/tests.rs b/integration-tests/public/traits/incrementer/tests.rs similarity index 100% rename from integration-tests/public/trait-incrementer/tests.rs rename to integration-tests/public/traits/incrementer/tests.rs diff --git a/integration-tests/public/trait-incrementer/traits/Cargo.toml b/integration-tests/public/traits/incrementer/traits/Cargo.toml similarity index 100% rename from integration-tests/public/trait-incrementer/traits/Cargo.toml rename to integration-tests/public/traits/incrementer/traits/Cargo.toml diff --git a/integration-tests/public/trait-incrementer/traits/lib.rs b/integration-tests/public/traits/incrementer/traits/lib.rs similarity index 100% rename from integration-tests/public/trait-incrementer/traits/lib.rs rename to integration-tests/public/traits/incrementer/traits/lib.rs