From e5c5f1226a7234ee43cf7e6a78b80dc169647a95 Mon Sep 17 00:00:00 2001 From: MOZGIII Date: Fri, 30 Jun 2023 21:38:55 -0300 Subject: [PATCH 01/29] Make currency swap return the original imbalance on failure --- crates/primitives-currency-swap/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/primitives-currency-swap/src/lib.rs b/crates/primitives-currency-swap/src/lib.rs index f43653f3f..6bd3e0a7e 100644 --- a/crates/primitives-currency-swap/src/lib.rs +++ b/crates/primitives-currency-swap/src/lib.rs @@ -37,7 +37,7 @@ pub type ToNegativeImbalanceFor = <>::To as Currency>::NegativeImbalance; -/// A type alias for compact declaration of the error type for the [`CurrencySwap::swap`] call. +/// A type alias for compact delcaration of the error type for the [`CurrencySwap::swap`] call. pub type ErrorFor = Error< FromNegativeImbalanceFor, >::Error, From 5bc5451d157786640b6e2d9c13331d22932c0f66 Mon Sep 17 00:00:00 2001 From: MOZGIII Date: Fri, 30 Jun 2023 21:39:12 -0300 Subject: [PATCH 02/29] Adjust the precompile-currency-swap --- crates/precompile-currency-swap/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/precompile-currency-swap/src/lib.rs b/crates/precompile-currency-swap/src/lib.rs index a47b8aff6..d85031177 100644 --- a/crates/precompile-currency-swap/src/lib.rs +++ b/crates/precompile-currency-swap/src/lib.rs @@ -135,7 +135,7 @@ where })?; let imbalance = CurrencySwapT::swap(imbalance).map_err(|error| { - // Here we undo the withdrawal to avoid having a dangling imbalance. + // Here we undo the withdrawl to avoid having a dangling imbalance. CurrencySwapT::From::resolve_creating(&from, error.incoming_imbalance); PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, From ad6806c6ef8ce283b3e8d8c17ab5586b703f215b Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Mon, 26 Jun 2023 18:25:28 +0300 Subject: [PATCH 03/29] Add basic structure --- crates/pallet-currency-swap/src/lib.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 crates/pallet-currency-swap/src/lib.rs diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs new file mode 100644 index 000000000..9c5c39c5d --- /dev/null +++ b/crates/pallet-currency-swap/src/lib.rs @@ -0,0 +1,23 @@ +//! A substrate pallet containing the currency swap integration. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +pub mod traits; + +// We have to temporarily allow some clippy lints. Later on we'll send patches to substrate to +// fix them at their end. +#[allow(clippy::missing_docs_in_private_items)] +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + /// Configuration trait of this pallet. + #[pallet::config] + pub trait Config: frame_system::Config {} +} From 48c3eed9da9bfe830aeef32d26c313573a63380f Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Mon, 26 Jun 2023 23:28:05 +0300 Subject: [PATCH 04/29] Define CurrencySwap interface --- Cargo.lock | 10 ++++++++++ crates/pallet-currency-swap/Cargo.toml | 16 ++++++++++++++++ crates/pallet-currency-swap/src/traits.rs | 14 ++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 crates/pallet-currency-swap/Cargo.toml create mode 100644 crates/pallet-currency-swap/src/traits.rs diff --git a/Cargo.lock b/Cargo.lock index 2ec96fc9d..df6f2cae5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5908,6 +5908,16 @@ dependencies = [ "sp-core", ] +[[package]] +name = "pallet-currency-swap" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", +] + [[package]] name = "pallet-dynamic-fee" version = "4.0.0-dev" diff --git a/crates/pallet-currency-swap/Cargo.toml b/crates/pallet-currency-swap/Cargo.toml new file mode 100644 index 000000000..b7d498fba --- /dev/null +++ b/crates/pallet-currency-swap/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "pallet-currency-swap" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } +frame-support = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } +frame-system = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } + +[features] +default = ["std"] +std = ["codec/std", "frame-support/std", "frame-system/std", "scale-info/std"] +try-runtime = ["frame-support/try-runtime", "frame-system/try-runtime"] diff --git a/crates/pallet-currency-swap/src/traits.rs b/crates/pallet-currency-swap/src/traits.rs new file mode 100644 index 000000000..7710ff600 --- /dev/null +++ b/crates/pallet-currency-swap/src/traits.rs @@ -0,0 +1,14 @@ +//! Traits we use and expose. + +use frame_support::{sp_runtime::DispatchError, traits::Currency}; + +pub trait CurrencySwap { + type From: Currency; + type To: Currency; + + fn swap( + account_id_from: &AccountIdFrom, + account_id_to: &AccountIdTo, + amount: &>::Balance, + ) -> Result<>::Balance, DispatchError>; +} From 40b007567a537903f27d3cab35ea579e591646c7 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 29 Jun 2023 15:52:45 +0300 Subject: [PATCH 05/29] Define config with accounts and currencies types --- Cargo.lock | 2 ++ crates/pallet-currency-swap/Cargo.toml | 6 ++++-- crates/pallet-currency-swap/src/lib.rs | 27 +++++++++++++++++++++++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df6f2cae5..6688bc0f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5916,6 +5916,8 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", + "sp-runtime", + "sp-std", ] [[package]] diff --git a/crates/pallet-currency-swap/Cargo.toml b/crates/pallet-currency-swap/Cargo.toml index b7d498fba..5ccfc2168 100644 --- a/crates/pallet-currency-swap/Cargo.toml +++ b/crates/pallet-currency-swap/Cargo.toml @@ -9,8 +9,10 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = frame-support = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } frame-system = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +sp-runtime = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } +sp-std = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } [features] default = ["std"] -std = ["codec/std", "frame-support/std", "frame-system/std", "scale-info/std"] -try-runtime = ["frame-support/try-runtime", "frame-system/try-runtime"] +std = ["codec/std", "frame-support/std", "frame-system/std", "scale-info/std", "sp-runtime/std", "sp-std/std"] +try-runtime = ["frame-support/try-runtime", "frame-system/try-runtime", "sp-runtime/try-runtime"] diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index 9c5c39c5d..b621d9f5b 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -2,6 +2,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +use frame_support::traits::Currency; pub use pallet::*; pub mod traits; @@ -11,6 +12,10 @@ pub mod traits; #[allow(clippy::missing_docs_in_private_items)] #[frame_support::pallet] pub mod pallet { + use frame_support::pallet_prelude::*; + use sp_runtime::traits::MaybeDisplay; + use sp_std::fmt::Debug; + use super::*; #[pallet::pallet] @@ -19,5 +24,25 @@ pub mod pallet { /// Configuration trait of this pallet. #[pallet::config] - pub trait Config: frame_system::Config {} + pub trait Config: frame_system::Config { + type AccountIdFrom: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + Ord + + MaxEncodedLen; + + type CurrencyFrom: Currency; + + type AccountIdTo: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + Ord + + MaxEncodedLen; + + type CurrencyTo: Currency; + } } From ccf0978f8d57fd78cfeef3de76e553e93e662a8d Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 29 Jun 2023 16:33:59 +0300 Subject: [PATCH 06/29] Basic swap call --- crates/pallet-currency-swap/src/lib.rs | 36 ++++++++++++++-------- crates/pallet-currency-swap/src/traits.rs | 2 +- crates/pallet-currency-swap/src/weights.rs | 15 +++++++++ 3 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 crates/pallet-currency-swap/src/weights.rs diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index b621d9f5b..80c99d8d6 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -2,8 +2,10 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::traits::Currency; pub use pallet::*; +pub use weights::*; + +pub mod weights; pub mod traits; @@ -12,9 +14,11 @@ pub mod traits; #[allow(clippy::missing_docs_in_private_items)] #[frame_support::pallet] pub mod pallet { - use frame_support::pallet_prelude::*; + use frame_support::{pallet_prelude::*, traits::Currency}; + use frame_system::pallet_prelude::*; use sp_runtime::traits::MaybeDisplay; use sp_std::fmt::Debug; + use traits::CurrencySwap; use super::*; @@ -25,7 +29,7 @@ pub mod pallet { /// Configuration trait of this pallet. #[pallet::config] pub trait Config: frame_system::Config { - type AccountIdFrom: Parameter + type AccountIdTo: Parameter + Member + MaybeSerializeDeserialize + Debug @@ -33,16 +37,24 @@ pub mod pallet { + Ord + MaxEncodedLen; - type CurrencyFrom: Currency; + type CurrencySwap: CurrencySwap; - type AccountIdTo: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + Ord - + MaxEncodedLen; + type WeightInfo: WeightInfo; + } - type CurrencyTo: Currency; + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::swap())] + pub fn swap( + origin: OriginFor, + account_id_to: T::AccountIdTo, + balance: <>::From as Currency>::Balance, + ) -> DispatchResult { + let account_id_from = ensure_signed(origin)?; + + let _ = T::CurrencySwap::swap(&account_id_from, &account_id_to, balance)?; + Ok(()) + } } } diff --git a/crates/pallet-currency-swap/src/traits.rs b/crates/pallet-currency-swap/src/traits.rs index 7710ff600..815d43a69 100644 --- a/crates/pallet-currency-swap/src/traits.rs +++ b/crates/pallet-currency-swap/src/traits.rs @@ -9,6 +9,6 @@ pub trait CurrencySwap { fn swap( account_id_from: &AccountIdFrom, account_id_to: &AccountIdTo, - amount: &>::Balance, + amount: >::Balance, ) -> Result<>::Balance, DispatchError>; } diff --git a/crates/pallet-currency-swap/src/weights.rs b/crates/pallet-currency-swap/src/weights.rs new file mode 100644 index 000000000..37daf0790 --- /dev/null +++ b/crates/pallet-currency-swap/src/weights.rs @@ -0,0 +1,15 @@ +//! Weights definition for pallet-currency-swap. + +use frame_support::weights::Weight; + +/// Weight functions needed for pallet-currency-swap. +pub trait WeightInfo { + /// A function to calculate required weights for swap call. + fn swap() -> Weight; +} + +impl WeightInfo for () { + fn swap() -> Weight { + Weight::zero() + } +} From 5c712512661c21b4a939058c26c926a2e1107abf Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 29 Jun 2023 16:37:21 +0300 Subject: [PATCH 07/29] Define custom error in CurrencySwap trait --- crates/pallet-currency-swap/src/lib.rs | 3 ++- crates/pallet-currency-swap/src/traits.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index 80c99d8d6..0b881c297 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -53,7 +53,8 @@ pub mod pallet { ) -> DispatchResult { let account_id_from = ensure_signed(origin)?; - let _ = T::CurrencySwap::swap(&account_id_from, &account_id_to, balance)?; + let _ = T::CurrencySwap::swap(&account_id_from, &account_id_to, balance) + .map_err(Into::into)?; Ok(()) } } diff --git a/crates/pallet-currency-swap/src/traits.rs b/crates/pallet-currency-swap/src/traits.rs index 815d43a69..02bc8ef74 100644 --- a/crates/pallet-currency-swap/src/traits.rs +++ b/crates/pallet-currency-swap/src/traits.rs @@ -5,10 +5,11 @@ use frame_support::{sp_runtime::DispatchError, traits::Currency}; pub trait CurrencySwap { type From: Currency; type To: Currency; + type Error: Into; fn swap( account_id_from: &AccountIdFrom, account_id_to: &AccountIdTo, amount: >::Balance, - ) -> Result<>::Balance, DispatchError>; + ) -> Result<>::Balance, Self::Error>; } From 9ff039f90dcaa6ed1f431427c2c34e56136e7824 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 29 Jun 2023 17:41:07 +0300 Subject: [PATCH 08/29] Draft working test --- Cargo.lock | 5 + crates/pallet-currency-swap/Cargo.toml | 7 + crates/pallet-currency-swap/src/lib.rs | 5 + crates/pallet-currency-swap/src/mock.rs | 155 +++++++++++++++++++++++ crates/pallet-currency-swap/src/tests.rs | 42 ++++++ 5 files changed, 214 insertions(+) create mode 100644 crates/pallet-currency-swap/src/mock.rs create mode 100644 crates/pallet-currency-swap/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 6688bc0f2..59bdee275 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5914,8 +5914,13 @@ version = "0.1.0" dependencies = [ "frame-support", "frame-system", + "mockall", + "pallet-balances", + "pallet-evm-balances", + "pallet-evm-system", "parity-scale-codec", "scale-info", + "sp-core", "sp-runtime", "sp-std", ] diff --git a/crates/pallet-currency-swap/Cargo.toml b/crates/pallet-currency-swap/Cargo.toml index 5ccfc2168..b86d13097 100644 --- a/crates/pallet-currency-swap/Cargo.toml +++ b/crates/pallet-currency-swap/Cargo.toml @@ -12,6 +12,13 @@ scale-info = { version = "2.5.0", default-features = false, features = ["derive" sp-runtime = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } sp-std = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } +[dev-dependencies] +mockall = "0.11" +pallet-balances = { git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } +pallet-evm-balances = { git = "https://github.com/humanode-network/frontier", branch = "locked/polkadot-v0.9.38" } +pallet-evm-system = { git = "https://github.com/humanode-network/frontier", branch = "locked/polkadot-v0.9.38" } +sp-core = { git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } + [features] default = ["std"] std = ["codec/std", "frame-support/std", "frame-system/std", "scale-info/std", "sp-runtime/std", "sp-std/std"] diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index 0b881c297..09fa6bff2 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -9,6 +9,11 @@ pub mod weights; pub mod traits; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + // We have to temporarily allow some clippy lints. Later on we'll send patches to substrate to // fix them at their end. #[allow(clippy::missing_docs_in_private_items)] diff --git a/crates/pallet-currency-swap/src/mock.rs b/crates/pallet-currency-swap/src/mock.rs new file mode 100644 index 000000000..fc643d72d --- /dev/null +++ b/crates/pallet-currency-swap/src/mock.rs @@ -0,0 +1,155 @@ +//! The mock for the pallet. + +// Allow simple integer arithmetic in tests. +#![allow(clippy::integer_arithmetic)] + +use frame_support::{ + sp_io, + sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, DispatchError, + }, + traits::{ConstU32, ConstU64}, +}; +use mockall::mock; +use sp_core::{H160, H256}; + +use crate::{self as pallet_currency_swap, traits}; + +mock! { + #[derive(Debug)] + pub CurrencySwap {} + impl traits::CurrencySwap for CurrencySwap { + type From = Balances; + type To = EvmBalances; + type Error = DispatchError; + + fn swap( + account_id_from: &u64, + account_id_to: &H160, + amount: u64, + ) -> Result; + } +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + Balances: pallet_balances, + EvmSystem: pallet_evm_system, + EvmBalances: pallet_evm_balances, + CurrencySwap: pallet_currency_swap, + } +); + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); +} + +impl pallet_evm_system::Config for Test { + type RuntimeEvent = RuntimeEvent; + type AccountId = H160; + type Index = u64; + type AccountData = pallet_evm_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); +} + +impl pallet_evm_balances::Config for Test { + type RuntimeEvent = RuntimeEvent; + type AccountId = H160; + type Balance = u64; + type ExistentialDeposit = ConstU64<1>; + type AccountStore = EvmSystem; + type DustRemoval = (); +} + +impl pallet_currency_swap::Config for Test { + type AccountIdTo = H160; + type CurrencySwap = MockCurrencySwap; + type WeightInfo = (); +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let genesis_config = GenesisConfig::default(); + new_test_ext_with(genesis_config) +} + +// This function basically just builds a genesis storage key/value store according to +// our desired mockup. +pub fn new_test_ext_with(genesis_config: GenesisConfig) -> sp_io::TestExternalities { + let storage = genesis_config.build_storage().unwrap(); + storage.into() +} + +pub fn runtime_lock() -> std::sync::MutexGuard<'static, ()> { + static MOCK_RUNTIME_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(()); + + // Ignore the poisoning for the tests that panic. + // We only care about concurrency here, not about the poisoning. + match MOCK_RUNTIME_MUTEX.lock() { + Ok(guard) => guard, + Err(poisoned) => poisoned.into_inner(), + } +} + +pub trait TestExternalitiesExt { + fn execute_with_ext(&mut self, execute: E) -> R + where + E: for<'e> FnOnce(&'e ()) -> R; +} + +impl TestExternalitiesExt for frame_support::sp_io::TestExternalities { + fn execute_with_ext(&mut self, execute: E) -> R + where + E: for<'e> FnOnce(&'e ()) -> R, + { + let guard = runtime_lock(); + let result = self.execute_with(|| execute(&guard)); + drop(guard); + result + } +} diff --git a/crates/pallet-currency-swap/src/tests.rs b/crates/pallet-currency-swap/src/tests.rs new file mode 100644 index 000000000..ea7c8eeff --- /dev/null +++ b/crates/pallet-currency-swap/src/tests.rs @@ -0,0 +1,42 @@ +//! The tests for the pallet. + +use frame_support::{ + assert_noop, assert_ok, assert_storage_noop, sp_runtime::DispatchError, traits::Currency, +}; +use mockall::predicate; +use sp_core::H160; +use sp_std::str::FromStr; + +use crate::{mock::*, *}; + +#[test] +fn swap_works() { + new_test_ext().execute_with_ext(|_| { + Balances::make_free_balance_be(&42, 1000); + let alice_evm = H160::from_str("1000000000000000000000000000000000000001").unwrap(); + + assert_eq!(Balances::total_balance(&42), 1000); + assert_eq!(EvmBalances::total_balance(&alice_evm), 0); + + // Set mock expectations. + let swap_ctx = MockCurrencySwap::swap_context(); + swap_ctx + .expect() + .once() + .with( + predicate::eq(&42), + predicate::eq(alice_evm), + predicate::eq(100), + ) + .return_const(Ok(200)); + + // Invoke the function under test. + assert_ok!(CurrencySwap::swap( + RuntimeOrigin::signed(42), + alice_evm, + 100 + )); + + assert_eq!(EvmBalances::total_balance(&alice_evm), 200); + }); +} From 66b514ebe78d0145f3961a1aba8449882fffc8b2 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 29 Jun 2023 19:48:18 +0300 Subject: [PATCH 09/29] Working test with swap --- Cargo.lock | 1 - crates/pallet-currency-swap/Cargo.toml | 1 - crates/pallet-currency-swap/src/mock.rs | 47 ++++++++++++++---------- crates/pallet-currency-swap/src/tests.rs | 21 ++++------- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59bdee275..6046b21ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5914,7 +5914,6 @@ version = "0.1.0" dependencies = [ "frame-support", "frame-system", - "mockall", "pallet-balances", "pallet-evm-balances", "pallet-evm-system", diff --git a/crates/pallet-currency-swap/Cargo.toml b/crates/pallet-currency-swap/Cargo.toml index b86d13097..4002d8dbd 100644 --- a/crates/pallet-currency-swap/Cargo.toml +++ b/crates/pallet-currency-swap/Cargo.toml @@ -13,7 +13,6 @@ sp-runtime = { default-features = false, git = "https://github.com/humanode-netw sp-std = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } [dev-dependencies] -mockall = "0.11" pallet-balances = { git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } pallet-evm-balances = { git = "https://github.com/humanode-network/frontier", branch = "locked/polkadot-v0.9.38" } pallet-evm-system = { git = "https://github.com/humanode-network/frontier", branch = "locked/polkadot-v0.9.38" } diff --git a/crates/pallet-currency-swap/src/mock.rs b/crates/pallet-currency-swap/src/mock.rs index fc643d72d..e0097ee51 100644 --- a/crates/pallet-currency-swap/src/mock.rs +++ b/crates/pallet-currency-swap/src/mock.rs @@ -10,29 +10,12 @@ use frame_support::{ traits::{BlakeTwo256, IdentityLookup}, BuildStorage, DispatchError, }, - traits::{ConstU32, ConstU64}, + traits::{ConstU32, ConstU64, Currency, ExistenceRequirement, Imbalance, WithdrawReasons}, }; -use mockall::mock; use sp_core::{H160, H256}; use crate::{self as pallet_currency_swap, traits}; -mock! { - #[derive(Debug)] - pub CurrencySwap {} - impl traits::CurrencySwap for CurrencySwap { - type From = Balances; - type To = EvmBalances; - type Error = DispatchError; - - fn swap( - account_id_from: &u64, - account_id_to: &H160, - amount: u64, - ) -> Result; - } -} - type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -107,9 +90,35 @@ impl pallet_evm_balances::Config for Test { type DustRemoval = (); } +pub struct NativeToEvm; + +impl traits::CurrencySwap for NativeToEvm { + type From = Balances; + type To = EvmBalances; + type Error = DispatchError; + + fn swap( + account_id_from: &u64, + account_id_to: &H160, + amount: u64, + ) -> Result { + let withdraw_imbalance = Balances::withdraw( + account_id_from, + amount, + WithdrawReasons::TRANSFER, + ExistenceRequirement::AllowDeath, + )?; + + let deposit_imbalance = + EvmBalances::deposit_creating(account_id_to, withdraw_imbalance.peek()); + + Ok(deposit_imbalance.peek()) + } +} + impl pallet_currency_swap::Config for Test { type AccountIdTo = H160; - type CurrencySwap = MockCurrencySwap; + type CurrencySwap = NativeToEvm; type WeightInfo = (); } diff --git a/crates/pallet-currency-swap/src/tests.rs b/crates/pallet-currency-swap/src/tests.rs index ea7c8eeff..e94d12fb1 100644 --- a/crates/pallet-currency-swap/src/tests.rs +++ b/crates/pallet-currency-swap/src/tests.rs @@ -3,7 +3,6 @@ use frame_support::{ assert_noop, assert_ok, assert_storage_noop, sp_runtime::DispatchError, traits::Currency, }; -use mockall::predicate; use sp_core::H160; use sp_std::str::FromStr; @@ -14,22 +13,12 @@ fn swap_works() { new_test_ext().execute_with_ext(|_| { Balances::make_free_balance_be(&42, 1000); let alice_evm = H160::from_str("1000000000000000000000000000000000000001").unwrap(); + let balances_before = Balances::total_issuance(); + let evm_balances_before = EvmBalances::total_issuance(); assert_eq!(Balances::total_balance(&42), 1000); assert_eq!(EvmBalances::total_balance(&alice_evm), 0); - // Set mock expectations. - let swap_ctx = MockCurrencySwap::swap_context(); - swap_ctx - .expect() - .once() - .with( - predicate::eq(&42), - predicate::eq(alice_evm), - predicate::eq(100), - ) - .return_const(Ok(200)); - // Invoke the function under test. assert_ok!(CurrencySwap::swap( RuntimeOrigin::signed(42), @@ -37,6 +26,10 @@ fn swap_works() { 100 )); - assert_eq!(EvmBalances::total_balance(&alice_evm), 200); + assert_eq!(Balances::total_balance(&42), 900); + assert_eq!(EvmBalances::total_balance(&alice_evm), 100); + + assert_eq!(Balances::total_issuance(), balances_before - 100); + assert_eq!(EvmBalances::total_issuance(), evm_balances_before + 100); }); } From fd079fa7640e76c86829eb6ab14fed4fe177c5ed Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 29 Jun 2023 19:56:33 +0300 Subject: [PATCH 10/29] Refactor swap_works test --- crates/pallet-currency-swap/src/mock.rs | 6 ++--- crates/pallet-currency-swap/src/tests.rs | 31 +++++++++++++++++------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/crates/pallet-currency-swap/src/mock.rs b/crates/pallet-currency-swap/src/mock.rs index e0097ee51..fe6ce17a0 100644 --- a/crates/pallet-currency-swap/src/mock.rs +++ b/crates/pallet-currency-swap/src/mock.rs @@ -90,9 +90,9 @@ impl pallet_evm_balances::Config for Test { type DustRemoval = (); } -pub struct NativeToEvm; +pub struct NativeToEvmOneToOne; -impl traits::CurrencySwap for NativeToEvm { +impl traits::CurrencySwap for NativeToEvmOneToOne { type From = Balances; type To = EvmBalances; type Error = DispatchError; @@ -118,7 +118,7 @@ impl traits::CurrencySwap for NativeToEvm { impl pallet_currency_swap::Config for Test { type AccountIdTo = H160; - type CurrencySwap = NativeToEvm; + type CurrencySwap = NativeToEvmOneToOne; type WeightInfo = (); } diff --git a/crates/pallet-currency-swap/src/tests.rs b/crates/pallet-currency-swap/src/tests.rs index e94d12fb1..8bc7979e2 100644 --- a/crates/pallet-currency-swap/src/tests.rs +++ b/crates/pallet-currency-swap/src/tests.rs @@ -11,25 +11,38 @@ use crate::{mock::*, *}; #[test] fn swap_works() { new_test_ext().execute_with_ext(|_| { - Balances::make_free_balance_be(&42, 1000); + let alice = 42; let alice_evm = H160::from_str("1000000000000000000000000000000000000001").unwrap(); + let alice_balance = 1000; + let swap_balance = 100; + + // Prepare the test state. + Balances::make_free_balance_be(&alice, alice_balance); + let balances_before = Balances::total_issuance(); let evm_balances_before = EvmBalances::total_issuance(); - assert_eq!(Balances::total_balance(&42), 1000); + // Check test preconditions. + assert_eq!(Balances::total_balance(&alice), alice_balance); assert_eq!(EvmBalances::total_balance(&alice_evm), 0); // Invoke the function under test. assert_ok!(CurrencySwap::swap( - RuntimeOrigin::signed(42), + RuntimeOrigin::signed(alice), alice_evm, - 100 + swap_balance )); - assert_eq!(Balances::total_balance(&42), 900); - assert_eq!(EvmBalances::total_balance(&alice_evm), 100); - - assert_eq!(Balances::total_issuance(), balances_before - 100); - assert_eq!(EvmBalances::total_issuance(), evm_balances_before + 100); + // Assert state changes. + assert_eq!( + Balances::total_balance(&alice), + alice_balance - swap_balance + ); + assert_eq!(EvmBalances::total_balance(&alice_evm), swap_balance); + assert_eq!(Balances::total_issuance(), balances_before - swap_balance); + assert_eq!( + EvmBalances::total_issuance(), + evm_balances_before + swap_balance + ); }); } From cb72f9704e4dfbdc16a6a9cd19e002ae93365be0 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 29 Jun 2023 20:09:23 +0300 Subject: [PATCH 11/29] Add swap_fails test --- crates/pallet-currency-swap/src/tests.rs | 27 ++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/crates/pallet-currency-swap/src/tests.rs b/crates/pallet-currency-swap/src/tests.rs index 8bc7979e2..ce468253d 100644 --- a/crates/pallet-currency-swap/src/tests.rs +++ b/crates/pallet-currency-swap/src/tests.rs @@ -1,13 +1,12 @@ //! The tests for the pallet. -use frame_support::{ - assert_noop, assert_ok, assert_storage_noop, sp_runtime::DispatchError, traits::Currency, -}; +use frame_support::{assert_noop, assert_ok, traits::Currency}; use sp_core::H160; use sp_std::str::FromStr; -use crate::{mock::*, *}; +use crate::mock::*; +/// This test verifies that swap call works in the happy path. #[test] fn swap_works() { new_test_ext().execute_with_ext(|_| { @@ -46,3 +45,23 @@ fn swap_works() { ); }); } + +/// This test verifies that swap call fails in case some error happens during the actual swap logic. +#[test] +fn swap_fails() { + new_test_ext().execute_with_ext(|_| { + let alice = 42; + let alice_evm = H160::from_str("1000000000000000000000000000000000000001").unwrap(); + let alice_balance = 1000; + let swap_balance = 10000; + + // Prepare the test state. + Balances::make_free_balance_be(&alice, alice_balance); + + // Invoke the function under test. + assert_noop!( + CurrencySwap::swap(RuntimeOrigin::signed(alice), alice_evm, swap_balance), + pallet_balances::Error::::InsufficientBalance + ); + }); +} From 8b2d3f12b471d42f5d452b2135320ec7a2958763 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 29 Jun 2023 20:33:48 +0300 Subject: [PATCH 12/29] Add docs for pallet --- crates/pallet-currency-swap/src/lib.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index 09fa6bff2..211225df5 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -2,6 +2,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +use frame_support::traits::Currency; pub use pallet::*; pub use weights::*; @@ -14,12 +15,22 @@ mod mock; #[cfg(test)] mod tests; +/// The currency type related to `From` of `CurrencySwap` interface. +type CurrencyFromOf = <::CurrencySwap as traits::CurrencySwap< + ::AccountId, + ::AccountIdTo, +>>::From; + +/// The balance type related to `CurrencyFromOf`. +type BalanceFromOf = + as Currency<::AccountId>>::Balance; + // We have to temporarily allow some clippy lints. Later on we'll send patches to substrate to // fix them at their end. #[allow(clippy::missing_docs_in_private_items)] #[frame_support::pallet] pub mod pallet { - use frame_support::{pallet_prelude::*, traits::Currency}; + use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use sp_runtime::traits::MaybeDisplay; use sp_std::fmt::Debug; @@ -34,6 +45,7 @@ pub mod pallet { /// Configuration trait of this pallet. #[pallet::config] pub trait Config: frame_system::Config { + /// The user account identifier type balances send to. type AccountIdTo: Parameter + Member + MaybeSerializeDeserialize @@ -42,19 +54,22 @@ pub mod pallet { + Ord + MaxEncodedLen; + /// Interface into currency swap implementation. type CurrencySwap: CurrencySwap; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; } #[pallet::call] impl Pallet { + /// Swap balances. #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::swap())] pub fn swap( origin: OriginFor, account_id_to: T::AccountIdTo, - balance: <>::From as Currency>::Balance, + balance: BalanceFromOf, ) -> DispatchResult { let account_id_from = ensure_signed(origin)?; From bb7fe940eaf19fa7a726eca1d1a77eca949b2c34 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 29 Jun 2023 20:37:43 +0300 Subject: [PATCH 13/29] Add docs for currency swap interface --- crates/pallet-currency-swap/src/traits.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/pallet-currency-swap/src/traits.rs b/crates/pallet-currency-swap/src/traits.rs index 02bc8ef74..31b705e44 100644 --- a/crates/pallet-currency-swap/src/traits.rs +++ b/crates/pallet-currency-swap/src/traits.rs @@ -2,11 +2,16 @@ use frame_support::{sp_runtime::DispatchError, traits::Currency}; +/// Currency swap interface. pub trait CurrencySwap { + /// The currency type balances send from. type From: Currency; + /// The currency type balances send to. type To: Currency; + /// An error happens during the actual balances swap. type Error: Into; + /// Swap balances. fn swap( account_id_from: &AccountIdFrom, account_id_to: &AccountIdTo, From 3052831f3100c48ccda0b25f6ade735a57b468ab Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 29 Jun 2023 23:21:28 +0300 Subject: [PATCH 14/29] Use negative imbalances logic at pallet level --- crates/pallet-currency-swap/src/lib.rs | 34 +++++++++++++++++------ crates/pallet-currency-swap/src/traits.rs | 6 ++-- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index 211225df5..a22af9a84 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -15,22 +15,31 @@ mod mock; #[cfg(test)] mod tests; -/// The currency type related to `From` of `CurrencySwap` interface. +/// The currency to convert from (from a given config). type CurrencyFromOf = <::CurrencySwap as traits::CurrencySwap< ::AccountId, ::AccountIdTo, >>::From; -/// The balance type related to `CurrencyFromOf`. +/// The currency balance to convert from (from a given config). type BalanceFromOf = as Currency<::AccountId>>::Balance; +/// The currency to convert to (from a given config). +type CurrencyToOf = <::CurrencySwap as traits::CurrencySwap< + ::AccountId, + ::AccountIdTo, +>>::To; + // We have to temporarily allow some clippy lints. Later on we'll send patches to substrate to // fix them at their end. #[allow(clippy::missing_docs_in_private_items)] #[frame_support::pallet] pub mod pallet { - use frame_support::pallet_prelude::*; + use frame_support::{ + pallet_prelude::*, + traits::{ExistenceRequirement, WithdrawReasons}, + }; use frame_system::pallet_prelude::*; use sp_runtime::traits::MaybeDisplay; use sp_std::fmt::Debug; @@ -68,13 +77,22 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::swap())] pub fn swap( origin: OriginFor, - account_id_to: T::AccountIdTo, - balance: BalanceFromOf, + to: T::AccountIdTo, + amount: BalanceFromOf, ) -> DispatchResult { - let account_id_from = ensure_signed(origin)?; + let who = ensure_signed(origin)?; + + let from_imbalance = CurrencyFromOf::::withdraw( + &who, + amount, + WithdrawReasons::TRANSFER, + ExistenceRequirement::AllowDeath, + )?; + + let to_imbalance = T::CurrencySwap::swap(from_imbalance).map_err(Into::into)?; + + CurrencyToOf::::resolve_creating(&to, to_imbalance); - let _ = T::CurrencySwap::swap(&account_id_from, &account_id_to, balance) - .map_err(Into::into)?; Ok(()) } } diff --git a/crates/pallet-currency-swap/src/traits.rs b/crates/pallet-currency-swap/src/traits.rs index 31b705e44..0a508bcb1 100644 --- a/crates/pallet-currency-swap/src/traits.rs +++ b/crates/pallet-currency-swap/src/traits.rs @@ -13,8 +13,6 @@ pub trait CurrencySwap { /// Swap balances. fn swap( - account_id_from: &AccountIdFrom, - account_id_to: &AccountIdTo, - amount: >::Balance, - ) -> Result<>::Balance, Self::Error>; + imbalance: >::NegativeImbalance, + ) -> Result<>::NegativeImbalance, Self::Error>; } From 657e7c27ba15ccb924d3ab5fcbc5027a5c3c1f4b Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 29 Jun 2023 23:22:19 +0300 Subject: [PATCH 15/29] Fix docs --- crates/pallet-currency-swap/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index a22af9a84..6f8d7e857 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -54,7 +54,7 @@ pub mod pallet { /// Configuration trait of this pallet. #[pallet::config] pub trait Config: frame_system::Config { - /// The user account identifier type balances send to. + /// The user account identifier type to convert to. type AccountIdTo: Parameter + Member + MaybeSerializeDeserialize From f17e619479bb9c69d6e9eef23b9b1cd1fbf6fa44 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 29 Jun 2023 23:26:54 +0300 Subject: [PATCH 16/29] Mock CurrencySwap interface for tests --- Cargo.lock | 1 + crates/pallet-currency-swap/Cargo.toml | 1 + crates/pallet-currency-swap/src/mock.rs | 39 +++++++++---------------- 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6046b21ed..59bdee275 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5914,6 +5914,7 @@ version = "0.1.0" dependencies = [ "frame-support", "frame-system", + "mockall", "pallet-balances", "pallet-evm-balances", "pallet-evm-system", diff --git a/crates/pallet-currency-swap/Cargo.toml b/crates/pallet-currency-swap/Cargo.toml index 4002d8dbd..b86d13097 100644 --- a/crates/pallet-currency-swap/Cargo.toml +++ b/crates/pallet-currency-swap/Cargo.toml @@ -13,6 +13,7 @@ sp-runtime = { default-features = false, git = "https://github.com/humanode-netw sp-std = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } [dev-dependencies] +mockall = "0.11" pallet-balances = { git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } pallet-evm-balances = { git = "https://github.com/humanode-network/frontier", branch = "locked/polkadot-v0.9.38" } pallet-evm-system = { git = "https://github.com/humanode-network/frontier", branch = "locked/polkadot-v0.9.38" } diff --git a/crates/pallet-currency-swap/src/mock.rs b/crates/pallet-currency-swap/src/mock.rs index fe6ce17a0..4462b9c4c 100644 --- a/crates/pallet-currency-swap/src/mock.rs +++ b/crates/pallet-currency-swap/src/mock.rs @@ -10,8 +10,9 @@ use frame_support::{ traits::{BlakeTwo256, IdentityLookup}, BuildStorage, DispatchError, }, - traits::{ConstU32, ConstU64, Currency, ExistenceRequirement, Imbalance, WithdrawReasons}, + traits::{ConstU32, ConstU64, Currency}, }; +use mockall::mock; use sp_core::{H160, H256}; use crate::{self as pallet_currency_swap, traits}; @@ -90,35 +91,23 @@ impl pallet_evm_balances::Config for Test { type DustRemoval = (); } -pub struct NativeToEvmOneToOne; - -impl traits::CurrencySwap for NativeToEvmOneToOne { - type From = Balances; - type To = EvmBalances; - type Error = DispatchError; - - fn swap( - account_id_from: &u64, - account_id_to: &H160, - amount: u64, - ) -> Result { - let withdraw_imbalance = Balances::withdraw( - account_id_from, - amount, - WithdrawReasons::TRANSFER, - ExistenceRequirement::AllowDeath, - )?; - - let deposit_imbalance = - EvmBalances::deposit_creating(account_id_to, withdraw_imbalance.peek()); - - Ok(deposit_imbalance.peek()) +mock! { + #[derive(Debug)] + pub CurrencySwap {} + impl traits::CurrencySwap for CurrencySwap { + type From = Balances; + type To = EvmBalances; + type Error = DispatchError; + + fn swap( + imbalance: >::NegativeImbalance, + ) -> Result<>::NegativeImbalance, DispatchError>; } } impl pallet_currency_swap::Config for Test { type AccountIdTo = H160; - type CurrencySwap = NativeToEvmOneToOne; + type CurrencySwap = MockCurrencySwap; type WeightInfo = (); } From e9908da5ae08a65b4a2844ea0b57890b1c8bbc3c Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 30 Jun 2023 00:21:23 +0300 Subject: [PATCH 17/29] Use mocking in tests --- crates/pallet-currency-swap/src/tests.rs | 44 ++++++++++++++++++------ 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/crates/pallet-currency-swap/src/tests.rs b/crates/pallet-currency-swap/src/tests.rs index ce468253d..1e16494e0 100644 --- a/crates/pallet-currency-swap/src/tests.rs +++ b/crates/pallet-currency-swap/src/tests.rs @@ -1,7 +1,9 @@ //! The tests for the pallet. use frame_support::{assert_noop, assert_ok, traits::Currency}; +use mockall::predicate; use sp_core::H160; +use sp_runtime::DispatchError; use sp_std::str::FromStr; use crate::mock::*; @@ -18,13 +20,24 @@ fn swap_works() { // Prepare the test state. Balances::make_free_balance_be(&alice, alice_balance); - let balances_before = Balances::total_issuance(); - let evm_balances_before = EvmBalances::total_issuance(); - // Check test preconditions. assert_eq!(Balances::total_balance(&alice), alice_balance); assert_eq!(EvmBalances::total_balance(&alice_evm), 0); + // Set mock expectations. + let swap_ctx = MockCurrencySwap::swap_context(); + swap_ctx + .expect() + .once() + .with(predicate::eq( + >::NegativeImbalance::new(swap_balance), + )) + .return_once(move |_| { + Ok(>::NegativeImbalance::new( + swap_balance, + )) + }); + // Invoke the function under test. assert_ok!(CurrencySwap::swap( RuntimeOrigin::signed(alice), @@ -38,11 +51,9 @@ fn swap_works() { alice_balance - swap_balance ); assert_eq!(EvmBalances::total_balance(&alice_evm), swap_balance); - assert_eq!(Balances::total_issuance(), balances_before - swap_balance); - assert_eq!( - EvmBalances::total_issuance(), - evm_balances_before + swap_balance - ); + + // Assert mock invocations. + swap_ctx.checkpoint(); }); } @@ -53,15 +64,28 @@ fn swap_fails() { let alice = 42; let alice_evm = H160::from_str("1000000000000000000000000000000000000001").unwrap(); let alice_balance = 1000; - let swap_balance = 10000; + let swap_balance = 100; // Prepare the test state. Balances::make_free_balance_be(&alice, alice_balance); + // Set mock expectations. + let swap_ctx = MockCurrencySwap::swap_context(); + swap_ctx + .expect() + .once() + .with(predicate::eq( + >::NegativeImbalance::new(swap_balance), + )) + .return_once(move |_| Err(DispatchError::Other("currency swap failed"))); + // Invoke the function under test. assert_noop!( CurrencySwap::swap(RuntimeOrigin::signed(alice), alice_evm, swap_balance), - pallet_balances::Error::::InsufficientBalance + DispatchError::Other("currency swap failed") ); + + // Assert mock invocations. + swap_ctx.checkpoint(); }); } From 09891f7e134b532aa54384527e012f103e9464bc Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 30 Jun 2023 00:28:04 +0300 Subject: [PATCH 18/29] Use AccountId and EvmAccountId to simplify their usage --- crates/pallet-currency-swap/src/mock.rs | 24 ++++++++++++++---------- crates/pallet-currency-swap/src/tests.rs | 6 ++---- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/crates/pallet-currency-swap/src/mock.rs b/crates/pallet-currency-swap/src/mock.rs index 4462b9c4c..76149fca2 100644 --- a/crates/pallet-currency-swap/src/mock.rs +++ b/crates/pallet-currency-swap/src/mock.rs @@ -20,6 +20,10 @@ use crate::{self as pallet_currency_swap, traits}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; +pub(crate) type AccountId = u64; +pub(crate) type EvmAccountId = H160; +type Balance = u64; + frame_support::construct_runtime!( pub enum Test where Block = Block, @@ -45,14 +49,14 @@ impl frame_system::Config for Test { type BlockNumber = u64; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; + type AccountId = AccountId; + type Lookup = IdentityLookup; type Header = Header; type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU64<250>; type Version = (); type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; + type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); @@ -75,17 +79,17 @@ impl pallet_balances::Config for Test { impl pallet_evm_system::Config for Test { type RuntimeEvent = RuntimeEvent; - type AccountId = H160; + type AccountId = EvmAccountId; type Index = u64; - type AccountData = pallet_evm_balances::AccountData; + type AccountData = pallet_evm_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); } impl pallet_evm_balances::Config for Test { type RuntimeEvent = RuntimeEvent; - type AccountId = H160; - type Balance = u64; + type AccountId = EvmAccountId; + type Balance = Balance; type ExistentialDeposit = ConstU64<1>; type AccountStore = EvmSystem; type DustRemoval = (); @@ -94,14 +98,14 @@ impl pallet_evm_balances::Config for Test { mock! { #[derive(Debug)] pub CurrencySwap {} - impl traits::CurrencySwap for CurrencySwap { + impl traits::CurrencySwap for CurrencySwap { type From = Balances; type To = EvmBalances; type Error = DispatchError; fn swap( - imbalance: >::NegativeImbalance, - ) -> Result<>::NegativeImbalance, DispatchError>; + imbalance: >::NegativeImbalance, + ) -> Result<>::NegativeImbalance, DispatchError>; } } diff --git a/crates/pallet-currency-swap/src/tests.rs b/crates/pallet-currency-swap/src/tests.rs index 1e16494e0..b3493d045 100644 --- a/crates/pallet-currency-swap/src/tests.rs +++ b/crates/pallet-currency-swap/src/tests.rs @@ -30,12 +30,10 @@ fn swap_works() { .expect() .once() .with(predicate::eq( - >::NegativeImbalance::new(swap_balance), + >::NegativeImbalance::new(swap_balance), )) .return_once(move |_| { - Ok(>::NegativeImbalance::new( - swap_balance, - )) + Ok(>::NegativeImbalance::new(swap_balance)) }); // Invoke the function under test. From dae21e4245bca14428aad00737827722813f1e8d Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 30 Jun 2023 01:10:26 +0300 Subject: [PATCH 19/29] Rename utility aliases --- crates/pallet-currency-swap/src/lib.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index 6f8d7e857..62c2b318d 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -15,18 +15,18 @@ mod mock; #[cfg(test)] mod tests; -/// The currency to convert from (from a given config). -type CurrencyFromOf = <::CurrencySwap as traits::CurrencySwap< +/// Utility alias for easy access to [`CurrencySwap::From`] type from a given config. +type FromCurrencyOf = <::CurrencySwap as traits::CurrencySwap< ::AccountId, ::AccountIdTo, >>::From; -/// The currency balance to convert from (from a given config). -type BalanceFromOf = - as Currency<::AccountId>>::Balance; +/// Utility alias for easy access to [`CurrencySwap::From::Balance`] type from a given config. +type FromBalanceOf = + as Currency<::AccountId>>::Balance; -/// The currency to convert to (from a given config). -type CurrencyToOf = <::CurrencySwap as traits::CurrencySwap< +/// Utility alias for easy access to [`CurrencySwap::To`] type from a given config. +type ToCurrencyOf = <::CurrencySwap as traits::CurrencySwap< ::AccountId, ::AccountIdTo, >>::To; @@ -78,11 +78,11 @@ pub mod pallet { pub fn swap( origin: OriginFor, to: T::AccountIdTo, - amount: BalanceFromOf, + amount: FromBalanceOf, ) -> DispatchResult { let who = ensure_signed(origin)?; - let from_imbalance = CurrencyFromOf::::withdraw( + let from_imbalance = FromCurrencyOf::::withdraw( &who, amount, WithdrawReasons::TRANSFER, @@ -91,7 +91,7 @@ pub mod pallet { let to_imbalance = T::CurrencySwap::swap(from_imbalance).map_err(Into::into)?; - CurrencyToOf::::resolve_creating(&to, to_imbalance); + ToCurrencyOf::::resolve_creating(&to, to_imbalance); Ok(()) } From c376285bdc6b725b67ba94959862ec9406a6ef2f Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 30 Jun 2023 01:13:01 +0300 Subject: [PATCH 20/29] Add with_storage_layer --- crates/pallet-currency-swap/src/lib.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index 62c2b318d..e3bed73f0 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -38,6 +38,7 @@ type ToCurrencyOf = <::CurrencySwap as traits::CurrencySwap< pub mod pallet { use frame_support::{ pallet_prelude::*, + storage::with_storage_layer, traits::{ExistenceRequirement, WithdrawReasons}, }; use frame_system::pallet_prelude::*; @@ -82,18 +83,20 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; - let from_imbalance = FromCurrencyOf::::withdraw( - &who, - amount, - WithdrawReasons::TRANSFER, - ExistenceRequirement::AllowDeath, - )?; + with_storage_layer(move || { + let from_imbalance = FromCurrencyOf::::withdraw( + &who, + amount, + WithdrawReasons::TRANSFER, + ExistenceRequirement::AllowDeath, + )?; - let to_imbalance = T::CurrencySwap::swap(from_imbalance).map_err(Into::into)?; + let to_imbalance = T::CurrencySwap::swap(from_imbalance).map_err(Into::into)?; - ToCurrencyOf::::resolve_creating(&to, to_imbalance); + ToCurrencyOf::::resolve_creating(&to, to_imbalance); - Ok(()) + Ok(()) + }) } } } From 1cf01361270c53283a1d5b73d5231fdda2cfe62d Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 30 Jun 2023 01:31:36 +0300 Subject: [PATCH 21/29] Add BalancesSwapped event --- crates/pallet-currency-swap/src/lib.rs | 40 +++++++++++++++++++++--- crates/pallet-currency-swap/src/mock.rs | 1 + crates/pallet-currency-swap/src/tests.rs | 11 ++++++- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index e3bed73f0..139dc6ec7 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -31,6 +31,9 @@ type ToCurrencyOf = <::CurrencySwap as traits::CurrencySwap< ::AccountIdTo, >>::To; +/// Utility alias for easy access to [`CurrencySwap::To::Balance`] type from a given config. +type ToBalanceOf = as Currency<::AccountIdTo>>::Balance; + // We have to temporarily allow some clippy lints. Later on we'll send patches to substrate to // fix them at their end. #[allow(clippy::missing_docs_in_private_items)] @@ -39,7 +42,7 @@ pub mod pallet { use frame_support::{ pallet_prelude::*, storage::with_storage_layer, - traits::{ExistenceRequirement, WithdrawReasons}, + traits::{ExistenceRequirement, Imbalance, WithdrawReasons}, }; use frame_system::pallet_prelude::*; use sp_runtime::traits::MaybeDisplay; @@ -55,6 +58,9 @@ pub mod pallet { /// Configuration trait of this pallet. #[pallet::config] pub trait Config: frame_system::Config { + /// Overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The user account identifier type to convert to. type AccountIdTo: Parameter + Member @@ -71,6 +77,22 @@ pub mod pallet { type WeightInfo: WeightInfo; } + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Balances were swapped. + BalancesSwapped { + /// The account id balances withdrawed from. + from: T::AccountId, + /// The withdrawed balances amount. + withdrawed_amount: FromBalanceOf, + /// The account id balances deposited to. + to: T::AccountIdTo, + /// The deposited balances amount. + deposited_amount: ToBalanceOf, + }, + } + #[pallet::call] impl Pallet { /// Swap balances. @@ -84,16 +106,26 @@ pub mod pallet { let who = ensure_signed(origin)?; with_storage_layer(move || { - let from_imbalance = FromCurrencyOf::::withdraw( + let withdrawed_imbalance = FromCurrencyOf::::withdraw( &who, amount, WithdrawReasons::TRANSFER, ExistenceRequirement::AllowDeath, )?; + let withdrawed_amount = withdrawed_imbalance.peek(); + + let deposited_imbalance = + T::CurrencySwap::swap(withdrawed_imbalance).map_err(Into::into)?; + let deposited_amount = deposited_imbalance.peek(); - let to_imbalance = T::CurrencySwap::swap(from_imbalance).map_err(Into::into)?; + ToCurrencyOf::::resolve_creating(&to, deposited_imbalance); - ToCurrencyOf::::resolve_creating(&to, to_imbalance); + Self::deposit_event(Event::BalancesSwapped { + from: who, + withdrawed_amount, + to, + deposited_amount, + }); Ok(()) }) diff --git a/crates/pallet-currency-swap/src/mock.rs b/crates/pallet-currency-swap/src/mock.rs index 76149fca2..4dc3c2cec 100644 --- a/crates/pallet-currency-swap/src/mock.rs +++ b/crates/pallet-currency-swap/src/mock.rs @@ -110,6 +110,7 @@ mock! { } impl pallet_currency_swap::Config for Test { + type RuntimeEvent = RuntimeEvent; type AccountIdTo = H160; type CurrencySwap = MockCurrencySwap; type WeightInfo = (); diff --git a/crates/pallet-currency-swap/src/tests.rs b/crates/pallet-currency-swap/src/tests.rs index b3493d045..7dea62d20 100644 --- a/crates/pallet-currency-swap/src/tests.rs +++ b/crates/pallet-currency-swap/src/tests.rs @@ -6,7 +6,7 @@ use sp_core::H160; use sp_runtime::DispatchError; use sp_std::str::FromStr; -use crate::mock::*; +use crate::{mock::*, *}; /// This test verifies that swap call works in the happy path. #[test] @@ -24,6 +24,9 @@ fn swap_works() { assert_eq!(Balances::total_balance(&alice), alice_balance); assert_eq!(EvmBalances::total_balance(&alice_evm), 0); + // Set block number to enable events. + System::set_block_number(1); + // Set mock expectations. let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx @@ -49,6 +52,12 @@ fn swap_works() { alice_balance - swap_balance ); assert_eq!(EvmBalances::total_balance(&alice_evm), swap_balance); + System::assert_has_event(RuntimeEvent::CurrencySwap(Event::BalancesSwapped { + from: alice, + withdrawed_amount: swap_balance, + to: alice_evm, + deposited_amount: swap_balance, + })); // Assert mock invocations. swap_ctx.checkpoint(); From 8f393678bcacb6ba56dd4e181cf4be15552c87ac Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 30 Jun 2023 01:39:13 +0300 Subject: [PATCH 22/29] Use currency swap primitives --- Cargo.lock | 1 + crates/pallet-currency-swap/Cargo.toml | 19 +++++++++++++++++-- crates/pallet-currency-swap/src/lib.rs | 10 ++++------ crates/pallet-currency-swap/src/mock.rs | 4 ++-- crates/pallet-currency-swap/src/traits.rs | 18 ------------------ 5 files changed, 24 insertions(+), 28 deletions(-) delete mode 100644 crates/pallet-currency-swap/src/traits.rs diff --git a/Cargo.lock b/Cargo.lock index 59bdee275..a1ea06f24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5919,6 +5919,7 @@ dependencies = [ "pallet-evm-balances", "pallet-evm-system", "parity-scale-codec", + "primitives-currency-swap", "scale-info", "sp-core", "sp-runtime", diff --git a/crates/pallet-currency-swap/Cargo.toml b/crates/pallet-currency-swap/Cargo.toml index b86d13097..d34203c85 100644 --- a/crates/pallet-currency-swap/Cargo.toml +++ b/crates/pallet-currency-swap/Cargo.toml @@ -5,6 +5,8 @@ edition = "2021" publish = false [dependencies] +primitives-currency-swap = { version = "0.1", path = "../primitives-currency-swap", default-features = false } + codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } frame-support = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } frame-system = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } @@ -21,5 +23,18 @@ sp-core = { git = "https://github.com/humanode-network/substrate", branch = "loc [features] default = ["std"] -std = ["codec/std", "frame-support/std", "frame-system/std", "scale-info/std", "sp-runtime/std", "sp-std/std"] -try-runtime = ["frame-support/try-runtime", "frame-system/try-runtime", "sp-runtime/try-runtime"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "primitives-currency-swap/std", + "scale-info/std", + "sp-runtime/std", + "sp-std/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "primitives-currency-swap/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index 139dc6ec7..21e355546 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -4,19 +4,18 @@ use frame_support::traits::Currency; pub use pallet::*; +use primitives_currency_swap::CurrencySwap as CurrencySwapT; pub use weights::*; pub mod weights; -pub mod traits; - #[cfg(test)] mod mock; #[cfg(test)] mod tests; /// Utility alias for easy access to [`CurrencySwap::From`] type from a given config. -type FromCurrencyOf = <::CurrencySwap as traits::CurrencySwap< +type FromCurrencyOf = <::CurrencySwap as CurrencySwapT< ::AccountId, ::AccountIdTo, >>::From; @@ -26,7 +25,7 @@ type FromBalanceOf = as Currency<::AccountId>>::Balance; /// Utility alias for easy access to [`CurrencySwap::To`] type from a given config. -type ToCurrencyOf = <::CurrencySwap as traits::CurrencySwap< +type ToCurrencyOf = <::CurrencySwap as CurrencySwapT< ::AccountId, ::AccountIdTo, >>::To; @@ -47,7 +46,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; use sp_runtime::traits::MaybeDisplay; use sp_std::fmt::Debug; - use traits::CurrencySwap; use super::*; @@ -71,7 +69,7 @@ pub mod pallet { + MaxEncodedLen; /// Interface into currency swap implementation. - type CurrencySwap: CurrencySwap; + type CurrencySwap: CurrencySwapT; /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; diff --git a/crates/pallet-currency-swap/src/mock.rs b/crates/pallet-currency-swap/src/mock.rs index 4dc3c2cec..1ff0acae4 100644 --- a/crates/pallet-currency-swap/src/mock.rs +++ b/crates/pallet-currency-swap/src/mock.rs @@ -15,7 +15,7 @@ use frame_support::{ use mockall::mock; use sp_core::{H160, H256}; -use crate::{self as pallet_currency_swap, traits}; +use crate::{self as pallet_currency_swap}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -98,7 +98,7 @@ impl pallet_evm_balances::Config for Test { mock! { #[derive(Debug)] pub CurrencySwap {} - impl traits::CurrencySwap for CurrencySwap { + impl primitives_currency_swap::CurrencySwap for CurrencySwap { type From = Balances; type To = EvmBalances; type Error = DispatchError; diff --git a/crates/pallet-currency-swap/src/traits.rs b/crates/pallet-currency-swap/src/traits.rs deleted file mode 100644 index 0a508bcb1..000000000 --- a/crates/pallet-currency-swap/src/traits.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Traits we use and expose. - -use frame_support::{sp_runtime::DispatchError, traits::Currency}; - -/// Currency swap interface. -pub trait CurrencySwap { - /// The currency type balances send from. - type From: Currency; - /// The currency type balances send to. - type To: Currency; - /// An error happens during the actual balances swap. - type Error: Into; - - /// Swap balances. - fn swap( - imbalance: >::NegativeImbalance, - ) -> Result<>::NegativeImbalance, Self::Error>; -} From 9114caf16b5587b09305052c466bdc789d3090e4 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 30 Jun 2023 01:43:22 +0300 Subject: [PATCH 23/29] Fix features --- crates/pallet-currency-swap/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/pallet-currency-swap/Cargo.toml b/crates/pallet-currency-swap/Cargo.toml index d34203c85..092a8cd7f 100644 --- a/crates/pallet-currency-swap/Cargo.toml +++ b/crates/pallet-currency-swap/Cargo.toml @@ -35,6 +35,9 @@ std = [ try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", + "pallet-balances/try-runtime", + "pallet-evm-balances/try-runtime", + "pallet-evm-system/try-runtime", "primitives-currency-swap/try-runtime", "sp-runtime/try-runtime", ] From 30cfba7a24202bec3fae8034e9e70cd7c853c275 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 30 Jun 2023 10:47:03 +0300 Subject: [PATCH 24/29] Fix docs --- crates/pallet-currency-swap/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index 21e355546..0fb0d4157 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -14,23 +14,23 @@ mod mock; #[cfg(test)] mod tests; -/// Utility alias for easy access to [`CurrencySwap::From`] type from a given config. +/// Utility alias for easy access to [`primitives_currency_swap::CurrencySwap::From`] type from a given config. type FromCurrencyOf = <::CurrencySwap as CurrencySwapT< ::AccountId, ::AccountIdTo, >>::From; -/// Utility alias for easy access to [`CurrencySwap::From::Balance`] type from a given config. +/// The currency balance to convert from. type FromBalanceOf = as Currency<::AccountId>>::Balance; -/// Utility alias for easy access to [`CurrencySwap::To`] type from a given config. +/// Utility alias for easy access to [`primitives_currency_swap::CurrencySwap::To`] type from a given config. type ToCurrencyOf = <::CurrencySwap as CurrencySwapT< ::AccountId, ::AccountIdTo, >>::To; -/// Utility alias for easy access to [`CurrencySwap::To::Balance`] type from a given config. +/// The currency balance to convert to. type ToBalanceOf = as Currency<::AccountIdTo>>::Balance; // We have to temporarily allow some clippy lints. Later on we'll send patches to substrate to From d1ccbafe5b3146c5fb26790d1f723beb816e4e17 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 30 Jun 2023 13:02:30 +0300 Subject: [PATCH 25/29] Implement benchmarking --- Cargo.lock | 1 + crates/pallet-currency-swap/Cargo.toml | 8 ++ .../pallet-currency-swap/src/benchmarking.rs | 123 ++++++++++++++++++ crates/pallet-currency-swap/src/lib.rs | 2 + 4 files changed, 134 insertions(+) create mode 100644 crates/pallet-currency-swap/src/benchmarking.rs diff --git a/Cargo.lock b/Cargo.lock index a1ea06f24..5dede8ecb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5912,6 +5912,7 @@ dependencies = [ name = "pallet-currency-swap" version = "0.1.0" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "mockall", diff --git a/crates/pallet-currency-swap/Cargo.toml b/crates/pallet-currency-swap/Cargo.toml index 092a8cd7f..9f437c5ae 100644 --- a/crates/pallet-currency-swap/Cargo.toml +++ b/crates/pallet-currency-swap/Cargo.toml @@ -8,6 +8,7 @@ publish = false primitives-currency-swap = { version = "0.1", path = "../primitives-currency-swap", default-features = false } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } +frame-benchmarking = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38", optional = true } frame-support = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } frame-system = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "locked/polkadot-v0.9.38" } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } @@ -23,6 +24,13 @@ sp-core = { git = "https://github.com/humanode-network/substrate", branch = "loc [features] default = ["std"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] std = [ "codec/std", "frame-support/std", diff --git a/crates/pallet-currency-swap/src/benchmarking.rs b/crates/pallet-currency-swap/src/benchmarking.rs new file mode 100644 index 000000000..51bad0cf9 --- /dev/null +++ b/crates/pallet-currency-swap/src/benchmarking.rs @@ -0,0 +1,123 @@ +//! The benchmarks for the pallet. + +use frame_benchmarking::benchmarks; +use frame_support::{assert_ok, dispatch::DispatchResult, traits::Currency}; +use frame_system::RawOrigin; + +use crate::*; + +const WITHDRAW_BALANCE: u32 = 100; +const DEPOSIT_BALANCE: u32 = 100; + +/// The benchmarking extension for the currency swap interface. +pub trait CurrencySwap: + primitives_currency_swap::CurrencySwap +{ + /// The data to be passed from `prepare` to `verify`. + type Data; + + /// Prepare currency swap environment. + fn prepare() -> Self::Data; + /// Verify currency swap environment, + fn verify(data: Self::Data) -> DispatchResult; +} + +/// The benchmark interface into the environment. +pub trait Interface: super::Config { + /// Obtain an `AccountIdFrom`. + /// + /// This is an account id balances withdrawed from. + fn from() -> ::AccountId; + + /// Obtain an `AccountIdTo`. + /// + /// This is an account id balances deposited to. + fn to() -> ::AccountIdTo; +} + +benchmarks! { + where_clause { + where + T: Interface, + ::CurrencySwap: CurrencySwap< + ::AccountId, + ::AccountIdTo + >, + } + + swap { + let from = ::from(); + let to = ::to(); + let init_balance: u32 = 1000; + + let _ = >::deposit_creating(&from, init_balance.into()); + + let from_balance_before = >::total_balance(&from); + let to_balance_before = >::total_balance(&to); + + let currency_swap = ::CurrencySwap::prepare(); + + let origin = RawOrigin::Signed(from.clone()); + + }: _(origin, to.clone(), WITHDRAW_BALANCE.into()) + verify { + let from_balance_after = >::total_balance(&from); + let to_balance_after = >::total_balance(&to); + + assert_eq!(from_balance_before - from_balance_after, WITHDRAW_BALANCE.into()); + assert_eq!(to_balance_after - to_balance_before, DEPOSIT_BALANCE.into()); + + assert_ok!(::CurrencySwap::verify(currency_swap)); + } + + impl_benchmark_test_suite!( + Pallet, + crate::mock::new_test_ext(), + crate::mock::Test, + ); +} + +#[cfg(test)] +impl Interface for crate::mock::Test { + fn from() -> ::AccountId { + 42 + } + + fn to() -> ::AccountIdTo { + use sp_std::str::FromStr; + + mock::EvmAccountId::from_str("1000000000000000000000000000000000000001").unwrap() + } +} + +#[cfg(test)] +impl CurrencySwap + for ::CurrencySwap +{ + type Data = ( + std::sync::MutexGuard<'static, ()>, + mock::__mock_MockCurrencySwap_CurrencySwap_9230394375286242749::__swap::Context, + ); + + fn prepare() -> Self::Data { + let mock_runtime_guard = mock::runtime_lock(); + + let swap_ctx = mock::MockCurrencySwap::swap_context(); + swap_ctx.expect().times(1..).return_once(move |_| { + Ok( + >::NegativeImbalance::new( + DEPOSIT_BALANCE.into(), + ), + ) + }); + + (mock_runtime_guard, swap_ctx) + } + + fn verify(data: Self::Data) -> DispatchResult { + let (mock_runtime_guard, swap_ctx) = data; + swap_ctx.checkpoint(); + drop(mock_runtime_guard); + Ok(()) + } +} diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index 0fb0d4157..4062238f8 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -9,6 +9,8 @@ pub use weights::*; pub mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; #[cfg(test)] mod mock; #[cfg(test)] From 722a58ab7710246fddf55f8e2b134d4fc41072cd Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sat, 1 Jul 2023 11:15:28 +0300 Subject: [PATCH 26/29] Fix docs again --- crates/pallet-currency-swap/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index 4062238f8..d3b3ba0e7 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -22,7 +22,8 @@ type FromCurrencyOf = <::CurrencySwap as CurrencySwapT< ::AccountIdTo, >>::From; -/// The currency balance to convert from. +/// Utility alias for easy access to the [`Currency::Balance`] of +/// the [`primitives_currency_swap::CurrencySwap::From`] type. type FromBalanceOf = as Currency<::AccountId>>::Balance; @@ -32,7 +33,8 @@ type ToCurrencyOf = <::CurrencySwap as CurrencySwapT< ::AccountIdTo, >>::To; -/// The currency balance to convert to. +/// Utility alias for easy access to the [`Currency::Balance`] of +/// the [`primitives_currency_swap::CurrencySwap::To`] type. type ToBalanceOf = as Currency<::AccountIdTo>>::Balance; // We have to temporarily allow some clippy lints. Later on we'll send patches to substrate to From bd41a3618def7d343739850e4a7d1fdd369f4061 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sat, 1 Jul 2023 11:21:59 +0300 Subject: [PATCH 27/29] Fix swap error processing --- crates/pallet-currency-swap/src/lib.rs | 6 +++++- crates/pallet-currency-swap/src/mock.rs | 5 ++++- crates/pallet-currency-swap/src/tests.rs | 11 ++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/crates/pallet-currency-swap/src/lib.rs b/crates/pallet-currency-swap/src/lib.rs index d3b3ba0e7..cc14ed219 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -117,7 +117,11 @@ pub mod pallet { let withdrawed_amount = withdrawed_imbalance.peek(); let deposited_imbalance = - T::CurrencySwap::swap(withdrawed_imbalance).map_err(Into::into)?; + T::CurrencySwap::swap(withdrawed_imbalance).map_err(|error| { + // Here we undo the withdrawl to avoid having a dangling imbalance. + FromCurrencyOf::::resolve_creating(&who, error.incoming_imbalance); + error.cause.into() + })?; let deposited_amount = deposited_imbalance.peek(); ToCurrencyOf::::resolve_creating(&to, deposited_imbalance); diff --git a/crates/pallet-currency-swap/src/mock.rs b/crates/pallet-currency-swap/src/mock.rs index 1ff0acae4..99b987586 100644 --- a/crates/pallet-currency-swap/src/mock.rs +++ b/crates/pallet-currency-swap/src/mock.rs @@ -105,7 +105,10 @@ mock! { fn swap( imbalance: >::NegativeImbalance, - ) -> Result<>::NegativeImbalance, DispatchError>; + ) -> Result< + primitives_currency_swap::ToNegativeImbalanceFor, + primitives_currency_swap::ErrorFor + >; } } diff --git a/crates/pallet-currency-swap/src/tests.rs b/crates/pallet-currency-swap/src/tests.rs index 7dea62d20..6b143a248 100644 --- a/crates/pallet-currency-swap/src/tests.rs +++ b/crates/pallet-currency-swap/src/tests.rs @@ -84,7 +84,12 @@ fn swap_fails() { .with(predicate::eq( >::NegativeImbalance::new(swap_balance), )) - .return_once(move |_| Err(DispatchError::Other("currency swap failed"))); + .return_once(move |incoming_imbalance| { + Err(primitives_currency_swap::Error { + cause: sp_runtime::DispatchError::Other("currency swap failed"), + incoming_imbalance, + }) + }); // Invoke the function under test. assert_noop!( @@ -92,6 +97,10 @@ fn swap_fails() { DispatchError::Other("currency swap failed") ); + // Assert state changes. + assert_eq!(Balances::total_balance(&alice), alice_balance); + assert_eq!(EvmBalances::total_balance(&alice_evm), 0); + // Assert mock invocations. swap_ctx.checkpoint(); }); From 3585811ff8ad8da600e991df611abb312c764f52 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sat, 1 Jul 2023 11:56:06 +0300 Subject: [PATCH 28/29] Redesign benhcmark interface --- .../pallet-currency-swap/src/benchmarking.rs | 78 +++++++++---------- crates/precompile-currency-swap/src/lib.rs | 2 +- crates/primitives-currency-swap/src/lib.rs | 2 +- 3 files changed, 39 insertions(+), 43 deletions(-) diff --git a/crates/pallet-currency-swap/src/benchmarking.rs b/crates/pallet-currency-swap/src/benchmarking.rs index 51bad0cf9..c2a0dc092 100644 --- a/crates/pallet-currency-swap/src/benchmarking.rs +++ b/crates/pallet-currency-swap/src/benchmarking.rs @@ -6,48 +6,41 @@ use frame_system::RawOrigin; use crate::*; -const WITHDRAW_BALANCE: u32 = 100; -const DEPOSIT_BALANCE: u32 = 100; - -/// The benchmarking extension for the currency swap interface. -pub trait CurrencySwap: - primitives_currency_swap::CurrencySwap -{ +/// The benchmark interface into the environment. +pub trait Interface: super::Config { /// The data to be passed from `prepare` to `verify`. type Data; /// Prepare currency swap environment. fn prepare() -> Self::Data; + /// Verify currency swap environment, fn verify(data: Self::Data) -> DispatchResult; -} -/// The benchmark interface into the environment. -pub trait Interface: super::Config { - /// Obtain an `AccountIdFrom`. - /// - /// This is an account id balances withdrawed from. + /// Obtain an account id balances withdrawed from. fn from() -> ::AccountId; - /// Obtain an `AccountIdTo`. - /// - /// This is an account id balances deposited to. + /// Obtain withdraw balances amount. + fn withdraw_amount() -> FromBalanceOf; + + /// Obtain an account id balances deposited to. fn to() -> ::AccountIdTo; + + /// Obtain deposit balances amount. + fn deposit_amount() -> ToBalanceOf; } benchmarks! { where_clause { where T: Interface, - ::CurrencySwap: CurrencySwap< - ::AccountId, - ::AccountIdTo - >, } swap { let from = ::from(); + let withdraw_amount = ::withdraw_amount(); let to = ::to(); + let deposit_amount = ::deposit_amount(); let init_balance: u32 = 1000; let _ = >::deposit_creating(&from, init_balance.into()); @@ -55,19 +48,19 @@ benchmarks! { let from_balance_before = >::total_balance(&from); let to_balance_before = >::total_balance(&to); - let currency_swap = ::CurrencySwap::prepare(); + let currency_swap = ::prepare(); let origin = RawOrigin::Signed(from.clone()); - }: _(origin, to.clone(), WITHDRAW_BALANCE.into()) + }: _(origin, to.clone(), withdraw_amount) verify { let from_balance_after = >::total_balance(&from); let to_balance_after = >::total_balance(&to); - assert_eq!(from_balance_before - from_balance_after, WITHDRAW_BALANCE.into()); - assert_eq!(to_balance_after - to_balance_before, DEPOSIT_BALANCE.into()); + assert_eq!(from_balance_before - from_balance_after, withdraw_amount); + assert_eq!(to_balance_after - to_balance_before, deposit_amount); - assert_ok!(::CurrencySwap::verify(currency_swap)); + assert_ok!(::verify(currency_swap)); } impl_benchmark_test_suite!( @@ -79,21 +72,6 @@ benchmarks! { #[cfg(test)] impl Interface for crate::mock::Test { - fn from() -> ::AccountId { - 42 - } - - fn to() -> ::AccountIdTo { - use sp_std::str::FromStr; - - mock::EvmAccountId::from_str("1000000000000000000000000000000000000001").unwrap() - } -} - -#[cfg(test)] -impl CurrencySwap - for ::CurrencySwap -{ type Data = ( std::sync::MutexGuard<'static, ()>, mock::__mock_MockCurrencySwap_CurrencySwap_9230394375286242749::__swap::Context, @@ -106,7 +84,7 @@ impl CurrencySwap swap_ctx.expect().times(1..).return_once(move |_| { Ok( >::NegativeImbalance::new( - DEPOSIT_BALANCE.into(), + Self::deposit_amount().into(), ), ) }); @@ -120,4 +98,22 @@ impl CurrencySwap drop(mock_runtime_guard); Ok(()) } + + fn from() -> ::AccountId { + 42 + } + + fn withdraw_amount() -> FromBalanceOf { + 100 + } + + fn to() -> ::AccountIdTo { + use sp_std::str::FromStr; + + mock::EvmAccountId::from_str("1000000000000000000000000000000000000001").unwrap() + } + + fn deposit_amount() -> ToBalanceOf { + 100 + } } diff --git a/crates/precompile-currency-swap/src/lib.rs b/crates/precompile-currency-swap/src/lib.rs index d85031177..a47b8aff6 100644 --- a/crates/precompile-currency-swap/src/lib.rs +++ b/crates/precompile-currency-swap/src/lib.rs @@ -135,7 +135,7 @@ where })?; let imbalance = CurrencySwapT::swap(imbalance).map_err(|error| { - // Here we undo the withdrawl to avoid having a dangling imbalance. + // Here we undo the withdrawal to avoid having a dangling imbalance. CurrencySwapT::From::resolve_creating(&from, error.incoming_imbalance); PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, diff --git a/crates/primitives-currency-swap/src/lib.rs b/crates/primitives-currency-swap/src/lib.rs index 6bd3e0a7e..f43653f3f 100644 --- a/crates/primitives-currency-swap/src/lib.rs +++ b/crates/primitives-currency-swap/src/lib.rs @@ -37,7 +37,7 @@ pub type ToNegativeImbalanceFor = <>::To as Currency>::NegativeImbalance; -/// A type alias for compact delcaration of the error type for the [`CurrencySwap::swap`] call. +/// A type alias for compact declaration of the error type for the [`CurrencySwap::swap`] call. pub type ErrorFor = Error< FromNegativeImbalanceFor, >::Error, From 9396013b718f111af740aab0ecdb25eececad287 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sun, 2 Jul 2023 21:34:09 +0300 Subject: [PATCH 29/29] Improve docs and naming for benchmark interface --- .../pallet-currency-swap/src/benchmarking.rs | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/crates/pallet-currency-swap/src/benchmarking.rs b/crates/pallet-currency-swap/src/benchmarking.rs index c2a0dc092..df90412d4 100644 --- a/crates/pallet-currency-swap/src/benchmarking.rs +++ b/crates/pallet-currency-swap/src/benchmarking.rs @@ -17,17 +17,17 @@ pub trait Interface: super::Config { /// Verify currency swap environment, fn verify(data: Self::Data) -> DispatchResult; - /// Obtain an account id balances withdrawed from. - fn from() -> ::AccountId; + /// Obtain the Account ID the balance is swapped from. + fn from_account_id() -> ::AccountId; - /// Obtain withdraw balances amount. - fn withdraw_amount() -> FromBalanceOf; + /// Obtain the amount of balance to withdraw from the swap source account. + fn from_balance() -> FromBalanceOf; - /// Obtain an account id balances deposited to. - fn to() -> ::AccountIdTo; + /// Obtain the Account ID the balance is swapped to. + fn to_account_id() -> ::AccountIdTo; - /// Obtain deposit balances amount. - fn deposit_amount() -> ToBalanceOf; + /// Obtain the amount of balance to deposit to the swap destination account. + fn to_balance() -> ToBalanceOf; } benchmarks! { @@ -37,10 +37,10 @@ benchmarks! { } swap { - let from = ::from(); - let withdraw_amount = ::withdraw_amount(); - let to = ::to(); - let deposit_amount = ::deposit_amount(); + let from = ::from_account_id(); + let from_balance = ::from_balance(); + let to = ::to_account_id(); + let to_balance = ::to_balance(); let init_balance: u32 = 1000; let _ = >::deposit_creating(&from, init_balance.into()); @@ -52,13 +52,13 @@ benchmarks! { let origin = RawOrigin::Signed(from.clone()); - }: _(origin, to.clone(), withdraw_amount) + }: _(origin, to.clone(), from_balance) verify { let from_balance_after = >::total_balance(&from); let to_balance_after = >::total_balance(&to); - assert_eq!(from_balance_before - from_balance_after, withdraw_amount); - assert_eq!(to_balance_after - to_balance_before, deposit_amount); + assert_eq!(from_balance_before - from_balance_after, from_balance); + assert_eq!(to_balance_after - to_balance_before, to_balance); assert_ok!(::verify(currency_swap)); } @@ -84,7 +84,7 @@ impl Interface for crate::mock::Test { swap_ctx.expect().times(1..).return_once(move |_| { Ok( >::NegativeImbalance::new( - Self::deposit_amount().into(), + Self::to_balance().into(), ), ) }); @@ -99,21 +99,21 @@ impl Interface for crate::mock::Test { Ok(()) } - fn from() -> ::AccountId { + fn from_account_id() -> ::AccountId { 42 } - fn withdraw_amount() -> FromBalanceOf { + fn from_balance() -> FromBalanceOf { 100 } - fn to() -> ::AccountIdTo { + fn to_account_id() -> ::AccountIdTo { use sp_std::str::FromStr; mock::EvmAccountId::from_str("1000000000000000000000000000000000000001").unwrap() } - fn deposit_amount() -> ToBalanceOf { + fn to_balance() -> ToBalanceOf { 100 } }