diff --git a/aleph-client/src/contract/mod.rs b/aleph-client/src/contract/mod.rs index d0e8aa0388..d039b0f8d4 100644 --- a/aleph-client/src/contract/mod.rs +++ b/aleph-client/src/contract/mod.rs @@ -69,7 +69,6 @@ pub struct ContractInstance { impl ContractInstance { const MAX_READ_GAS: u64 = 500000000000u64; const MAX_GAS: u64 = 100000000000u64; - const PAYABLE_VALUE: u64 = 0u64; const STORAGE_FEE_LIMIT: Option = None; /// Creates a new contract instance under `address` with metadata read from `metadata_path`. @@ -121,7 +120,7 @@ impl ContractInstance { /// Executes a 0-argument contract call. pub fn contract_exec0(&self, conn: &SignedConnection, message: &str) -> Result<()> { - self.contract_exec::(conn, message, &[]) + self.contract_exec_value::(conn, message, &[], 0) } /// Executes a contract call. @@ -130,6 +129,27 @@ impl ContractInstance { conn: &SignedConnection, message: &str, args: &[S], + ) -> Result<()> { + self.contract_exec_value(conn, message, args, 0) + } + + /// Executes a 0-argument contract call sending the given amount of value with it. + pub fn contract_exec_value0( + &self, + conn: &SignedConnection, + message: &str, + value: u128, + ) -> Result<()> { + self.contract_exec_value::(conn, message, &[], value) + } + + /// Executes a contract call sending the given amount of value with it. + pub fn contract_exec_value + Debug>( + &self, + conn: &SignedConnection, + message: &str, + args: &[S], + value: u128, ) -> Result<()> { let data = self.encode(message, args)?; let xt = compose_extrinsic!( @@ -137,7 +157,7 @@ impl ContractInstance { "Contracts", "call", GenericAddress::Id(self.address.clone()), - Compact(Self::PAYABLE_VALUE), + Compact(value), Compact(Self::MAX_GAS), Self::STORAGE_FEE_LIMIT, data diff --git a/contracts/scripts/deploy.sh b/contracts/scripts/deploy.sh index 92d07083fb..2088495fb6 100755 --- a/contracts/scripts/deploy.sh +++ b/contracts/scripts/deploy.sh @@ -347,13 +347,13 @@ jq -n --arg early_bird_special "$EARLY_BIRD_SPECIAL" \ the_pressiah_cometh_marketplace: $the_pressiah_cometh_marketplace, access_control: $access_control, simple_dex: $simple_dex, + wrapped_azero: $wrapped_azero, button_code_hash: $button_code_hash, ticket_token_code_hash: $ticket_token_code_hash, game_token_code_hash: $game_token_code_hash, marketplace_code_hash: $marketplace_code_hash, access_control_code_hash: $access_control_code_hash, simple_dex_code_hash: $simple_dex_code_hash, - wrapped_azero: $wrapped_azero, wrapped_azero_code_hash: $wrapped_azero_code_hash }' > addresses.json diff --git a/contracts/scripts/test.sh b/contracts/scripts/test.sh index 96b7061d0f..aa69af279e 100755 --- a/contracts/scripts/test.sh +++ b/contracts/scripts/test.sh @@ -8,10 +8,12 @@ EARLY_BIRD_SPECIAL=$(jq --raw-output ".early_bird_special" < "$CONTRACTS_PATH"/a THE_PRESSIAH_COMETH=$(jq --raw-output ".the_pressiah_cometh" < "$CONTRACTS_PATH"/addresses.json) BACK_TO_THE_FUTURE=$(jq --raw-output ".back_to_the_future" < "$CONTRACTS_PATH"/addresses.json) SIMPLE_DEX=$(jq --raw-output ".simple_dex" < "$CONTRACTS_PATH"/addresses.json) +WRAPPED_AZERO=$(jq --raw-output ".wrapped_azero" < "$CONTRACTS_PATH"/addresses.json) pushd "$E2E_PATH" RUST_LOG="aleph_e2e_client=info" cargo run --release -- \ + --test-cases wrapped_azero \ --test-cases simple_dex \ --test-cases marketplace \ --test-cases button_game_reset \ @@ -22,10 +24,12 @@ RUST_LOG="aleph_e2e_client=info" cargo run --release -- \ --the-pressiah-cometh "$THE_PRESSIAH_COMETH" \ --back-to-the-future "$BACK_TO_THE_FUTURE" \ --simple-dex "$SIMPLE_DEX" \ + --wrapped-azero "$WRAPPED_AZERO" \ --button-game-metadata ../contracts/button/target/ink/metadata.json \ --ticket-token-metadata ../contracts/ticket_token/target/ink/metadata.json \ --reward-token-metadata ../contracts/game_token/target/ink/metadata.json \ --marketplace-metadata ../contracts/marketplace/target/ink/metadata.json \ - --simple-dex-metadata ../contracts/simple_dex/target/ink/metadata.json + --simple-dex-metadata ../contracts/simple_dex/target/ink/metadata.json \ + --wrapped-azero-metadata ../contracts/wrapped_azero/target/ink/metadata.json exit $? diff --git a/e2e-tests/src/cases.rs b/e2e-tests/src/cases.rs index bdbdb83bd3..36263c0074 100644 --- a/e2e-tests/src/cases.rs +++ b/e2e-tests/src/cases.rs @@ -20,6 +20,7 @@ use crate::{ staking_new_validator as test_staking_new_validator, the_pressiah_cometh as test_the_pressiah_cometh, token_transfer as test_token_transfer, treasury_access as test_treasury_access, validators_rotate as test_validators_rotate, + wrapped_azero as test_wrapped_azero, }, }; @@ -73,6 +74,7 @@ pub fn possible_test_cases() -> PossibleTestCases { ("the_pressiah_cometh", test_the_pressiah_cometh as TestCase), ("marketplace", test_marketplace as TestCase), ("simple_dex", test_simple_dex as TestCase), + ("wrapped_azero", test_wrapped_azero as TestCase), ("ban_automatic", test_ban_automatic as TestCase), ("ban_manual", test_ban_manual as TestCase), ( diff --git a/e2e-tests/src/config.rs b/e2e-tests/src/config.rs index 788581655a..527f2e5a05 100644 --- a/e2e-tests/src/config.rs +++ b/e2e-tests/src/config.rs @@ -81,10 +81,14 @@ pub struct TestCaseParams { #[clap(long)] pub the_pressiah_cometh: Option, - /// Address of the simple dex contract. + /// Address of the simple dex contract. Only used by button tests. #[clap(long)] pub simple_dex: Option, + /// Address of the wrapped azero contract. Only used by button tests. + #[clap(long)] + pub wrapped_azero: Option, + /// Path to the button game metadata file. Only used by button tests. #[clap(long)] pub button_game_metadata: Option, @@ -105,6 +109,10 @@ pub struct TestCaseParams { #[clap(long)] pub simple_dex_metadata: Option, + /// Path to wrapped_azero metadata file. Only used by button tests. + #[clap(long)] + pub wrapped_azero_metadata: Option, + /// Version for the VersionUpgrade test. #[clap(long)] pub upgrade_to_version: Option, diff --git a/e2e-tests/src/test/button_game/contracts.rs b/e2e-tests/src/test/button_game/contracts.rs index 247945ce4a..65868dc2b9 100644 --- a/e2e-tests/src/test/button_game/contracts.rs +++ b/e2e-tests/src/test/button_game/contracts.rs @@ -303,3 +303,55 @@ impl From<&MarketplaceInstance> for AccountId { marketplace.contract.address().clone() } } + +#[derive(Debug)] +pub struct WAzeroInstance { + contract: ContractInstance, +} + +impl WAzeroInstance { + pub fn new(config: &Config) -> Result { + let wazero_address = config + .test_case_params + .wrapped_azero + .clone() + .context("Wrapped AZERO address not set.")?; + let wazero_address = AccountId::from_string(&wazero_address)?; + let metadata_path = config + .test_case_params + .wrapped_azero_metadata + .clone() + .context("Wrapped AZERO metadata path not set.")?; + + Ok(Self { + contract: ContractInstance::new(wazero_address, &metadata_path)?, + }) + } + + pub fn wrap(&self, conn: &SignedConnection, value: Balance) -> Result<()> { + self.contract.contract_exec_value0(conn, "wrap", value) + } + + pub fn unwrap(&self, conn: &SignedConnection, amount: Balance) -> Result<()> { + self.contract + .contract_exec(conn, "unwrap", &[amount.to_string()]) + } + + pub fn balance_of(&self, conn: &C, account: AccountId) -> Result { + self.contract + .contract_read(conn, "PSP22::balance_of", &[account.to_string()])? + .try_into() + } +} + +impl<'a> From<&'a WAzeroInstance> for &'a ContractInstance { + fn from(wazero: &'a WAzeroInstance) -> Self { + &wazero.contract + } +} + +impl<'a> From<&'a WAzeroInstance> for AccountId { + fn from(wazero: &'a WAzeroInstance) -> Self { + wazero.contract.address().clone() + } +} diff --git a/e2e-tests/src/test/button_game/helpers.rs b/e2e-tests/src/test/button_game/helpers.rs index 638c25f33c..654fb45173 100644 --- a/e2e-tests/src/test/button_game/helpers.rs +++ b/e2e-tests/src/test/button_game/helpers.rs @@ -20,7 +20,7 @@ use rand::Rng; use sp_core::Pair; use super::contracts::{ - ButtonInstance, MarketplaceInstance, PSP22TokenInstance, SimpleDexInstance, + ButtonInstance, MarketplaceInstance, PSP22TokenInstance, SimpleDexInstance, WAzeroInstance, }; use crate::Config; @@ -139,10 +139,43 @@ pub(super) struct DexTestContext { pub events: BufferedReceiver>, } +pub(super) struct WAzeroTestContext { + pub conn: Connection, + /// A random account with some money for fees. + pub account: KeyPairWrapper, + pub wazero: Arc, + /// A [BufferedReceiver] preconfigured to listen for events of `wazero`. + pub events: BufferedReceiver>, +} + +pub(super) fn setup_wrapped_azero_test(config: &Config) -> Result { + let (conn, _authority, account) = basic_test_context(config); + let wazero = Arc::new(WAzeroInstance::new(config)?); + + let contract = wazero.clone(); + let subscription = subscribe_events(&conn)?; + let (events_tx, events_rx) = channel(); + + thread::spawn(move || { + let contract_metadata = vec![contract.as_ref().into()]; + + listen_contract_events(subscription, &contract_metadata, None, |event| { + let _ = events_tx.send(event); + }); + }); + + let events = BufferedReceiver::new(events_rx, Duration::from_secs(3)); + + Ok(WAzeroTestContext { + conn, + account, + wazero, + events, + }) +} + pub(super) fn setup_dex_test(config: &Config) -> Result { - let conn = config.get_first_signed_connection().as_connection(); - let authority = KeyPairWrapper(aleph_client::keypair_from_string(&config.sudo_seed)); - let account = random_account(); + let (conn, authority, account) = basic_test_context(config); let dex = Arc::new(SimpleDexInstance::new(config)?); let token1 = @@ -174,7 +207,6 @@ pub(super) fn setup_dex_test(config: &Config) -> Result { }); let events = BufferedReceiver::new(events_rx, Duration::from_secs(3)); - transfer(&conn, &authority, &account, alephs(100)); Ok(DexTestContext { conn, @@ -202,10 +234,7 @@ pub(super) fn setup_button_test( config: &Config, button_contract_address: &Option, ) -> Result { - let conn = config.get_first_signed_connection().as_connection(); - - let authority = KeyPairWrapper(aleph_client::keypair_from_string(&config.sudo_seed)); - let player = random_account(); + let (conn, authority, player) = basic_test_context(config); let button = Arc::new(ButtonInstance::new(config, button_contract_address)?); let ticket_token = Arc::new(ticket_token(&conn, &button, config)?); @@ -235,8 +264,6 @@ pub(super) fn setup_button_test( let events = BufferedReceiver::new(events_rx, Duration::from_secs(3)); - transfer(&conn, &authority, &player, alephs(100)); - Ok(ButtonTestContext { button, ticket_token, @@ -249,6 +276,17 @@ pub(super) fn setup_button_test( }) } +/// Prepares a `(conn, authority, account)` triple with some money in `account` for fees. +fn basic_test_context(config: &Config) -> (Connection, KeyPairWrapper, KeyPairWrapper) { + let conn = config.get_first_signed_connection().as_connection(); + let authority = KeyPairWrapper(aleph_client::keypair_from_string(&config.sudo_seed)); + let account = random_account(); + + transfer(&conn, &authority, &account, alephs(100)); + + (conn, authority, account) +} + /// A receiver where it's possible to wait for messages out of order. pub struct BufferedReceiver { buffer: Vec, diff --git a/e2e-tests/src/test/button_game/mod.rs b/e2e-tests/src/test/button_game/mod.rs index 4ffc399750..ac116ef382 100644 --- a/e2e-tests/src/test/button_game/mod.rs +++ b/e2e-tests/src/test/button_game/mod.rs @@ -9,8 +9,9 @@ use sp_core::Pair; use crate::{ test::button_game::helpers::{ - assert_recv, assert_recv_id, mega, refute_recv_id, setup_button_test, setup_dex_test, - wait_for_death, ButtonTestContext, DexTestContext, + alephs, assert_recv, assert_recv_id, mega, refute_recv_id, setup_button_test, + setup_dex_test, setup_wrapped_azero_test, wait_for_death, ButtonTestContext, + DexTestContext, WAzeroTestContext, }, Config, }; @@ -18,6 +19,47 @@ use crate::{ mod contracts; mod helpers; +/// Test wrapped azero +/// +/// The scenario: +/// +/// 1. Wraps some azero and checks that the PSP22 balance increased accordingly. +/// 2. Unwraps half of the amount, checks that some wrapped funds remained while the rest has been returned to azero, +/// minus fees. +pub fn wrapped_azero(config: &Config) -> Result<()> { + let WAzeroTestContext { + conn, + account, + wazero, + mut events, + .. + } = setup_wrapped_azero_test(config)?; + let account_conn = &sign(&conn, &account); + let account_id: AccountId = account.public().into(); + + wazero.wrap(account_conn, alephs(2))?; + + let event = assert_recv_id(&mut events, "Wrapped"); + let_assert!(Some(Value::Literal(acc_id)) = event.data.get("caller")); + assert!(*acc_id == account_id.to_string()); + assert!(event.data.get("amount") == Some(&Value::UInt(alephs(2)))); + assert!(wazero.balance_of(account_conn, account.public().into())? == alephs(2)); + + let balance_before = aleph_client::get_free_balance(&conn, &account_id); + wazero.unwrap(account_conn, alephs(1))?; + + let event = assert_recv_id(&mut events, "UnWrapped"); + let balance_after = aleph_client::get_free_balance(&conn, &account_id); + let max_fee = alephs(1) / 100; + assert!(balance_after - balance_before > alephs(1) - max_fee); + let_assert!(Some(Value::Literal(acc_id)) = event.data.get("caller")); + assert!(*acc_id == account_id.to_string()); + assert!(event.data.get("amount") == Some(&Value::UInt(alephs(1)))); + assert!(wazero.balance_of(account_conn, account.public().into())? == alephs(1)); + + Ok(()) +} + /// Test trading on simple_dex. /// /// The scenario does the following (given 3 tokens A, B, C): diff --git a/e2e-tests/src/test/mod.rs b/e2e-tests/src/test/mod.rs index b7a718e2d8..90b1261b06 100644 --- a/e2e-tests/src/test/mod.rs +++ b/e2e-tests/src/test/mod.rs @@ -1,7 +1,7 @@ pub use ban::{ban_automatic, ban_manual, clearing_session_count}; pub use button_game::{ back_to_the_future, button_game_reset, early_bird_special, marketplace, simple_dex, - the_pressiah_cometh, + the_pressiah_cometh, wrapped_azero, }; pub use electing_validators::authorities_are_staking; pub use era_payout::era_payouts_calculated_correctly;