diff --git a/crates/bridge-pot-currency-swap/src/existence_optional.rs b/crates/bridge-pot-currency-swap/src/existence_optional.rs index fb3a5e515..0a7bd3693 100644 --- a/crates/bridge-pot-currency-swap/src/existence_optional.rs +++ b/crates/bridge-pot-currency-swap/src/existence_optional.rs @@ -45,4 +45,10 @@ impl primitives_currency_swap::CurrencySwap>::Balance, + ) -> >::Balance { + T::BalanceConverter::convert(balance) + } } diff --git a/crates/bridge-pot-currency-swap/src/existence_required.rs b/crates/bridge-pot-currency-swap/src/existence_required.rs index 7f2dbd5b7..fb0103999 100644 --- a/crates/bridge-pot-currency-swap/src/existence_required.rs +++ b/crates/bridge-pot-currency-swap/src/existence_required.rs @@ -73,4 +73,10 @@ impl primitives_currency_swap::CurrencySwap>::Balance, + ) -> >::Balance { + T::BalanceConverter::convert(balance) + } } diff --git a/crates/bridge-pot-currency-swap/src/lib.rs b/crates/bridge-pot-currency-swap/src/lib.rs index 3d8f76dfb..c9da023f2 100644 --- a/crates/bridge-pot-currency-swap/src/lib.rs +++ b/crates/bridge-pot-currency-swap/src/lib.rs @@ -6,7 +6,7 @@ use frame_support::{ sp_runtime::traits::Convert, sp_std::marker::PhantomData, - traits::{Currency, Get}, + traits::{fungible::Inspect, Currency, Get}, }; pub mod existence_optional; @@ -24,10 +24,18 @@ pub trait Config { type AccountIdTo; /// The currency to swap from. - type CurrencyFrom: Currency; + type CurrencyFrom: Currency + + Inspect< + Self::AccountIdFrom, + Balance = >::Balance, + >; /// The currency to swap to. - type CurrencyTo: Currency; + type CurrencyTo: Currency + + Inspect< + Self::AccountIdTo, + Balance = >::Balance, + >; /// The converter to determine how the balance amount should be converted from one currency to /// another. diff --git a/crates/pallet-currency-swap/src/benchmarking.rs b/crates/pallet-currency-swap/src/benchmarking.rs index df90412d4..f00f9058f 100644 --- a/crates/pallet-currency-swap/src/benchmarking.rs +++ b/crates/pallet-currency-swap/src/benchmarking.rs @@ -74,12 +74,19 @@ benchmarks! { impl Interface for crate::mock::Test { type Data = ( std::sync::MutexGuard<'static, ()>, + mock::__mock_MockCurrencySwap_CurrencySwap_9230394375286242749::__estimate_swapped_balance::Context, mock::__mock_MockCurrencySwap_CurrencySwap_9230394375286242749::__swap::Context, ); fn prepare() -> Self::Data { let mock_runtime_guard = mock::runtime_lock(); + let estimate_swapped_balance_ctx = + mock::MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx + .expect() + .times(1..) + .return_const(Self::to_balance()); let swap_ctx = mock::MockCurrencySwap::swap_context(); swap_ctx.expect().times(1..).return_once(move |_| { Ok( @@ -89,11 +96,12 @@ impl Interface for crate::mock::Test { ) }); - (mock_runtime_guard, swap_ctx) + (mock_runtime_guard, estimate_swapped_balance_ctx, swap_ctx) } fn verify(data: Self::Data) -> DispatchResult { - let (mock_runtime_guard, swap_ctx) = data; + let (mock_runtime_guard, estimate_swapped_balance_ctx, swap_ctx) = data; + estimate_swapped_balance_ctx.checkpoint(); 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 df97777de..0cb1b4f21 100644 --- a/crates/pallet-currency-swap/src/lib.rs +++ b/crates/pallet-currency-swap/src/lib.rs @@ -2,7 +2,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::traits::Currency; +use frame_support::traits::{fungible::Inspect, Currency}; pub use pallet::*; use primitives_currency_swap::CurrencySwap as CurrencySwapT; pub use weights::*; @@ -140,6 +140,9 @@ pub mod pallet { amount: FromBalanceOf, existence_requirement: ExistenceRequirement, ) -> DispatchResult { + let estimated_swapped_balance = T::CurrencySwap::estimate_swapped_balance(amount); + ToCurrencyOf::::can_deposit(&to, estimated_swapped_balance, false).into_result()?; + let withdrawed_imbalance = FromCurrencyOf::::withdraw( &who, amount, @@ -150,7 +153,7 @@ pub mod pallet { let deposited_imbalance = T::CurrencySwap::swap(withdrawed_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. FromCurrencyOf::::resolve_creating(&who, error.incoming_imbalance); error.cause.into() })?; diff --git a/crates/pallet-currency-swap/src/mock.rs b/crates/pallet-currency-swap/src/mock.rs index d43932396..2a73c5b2d 100644 --- a/crates/pallet-currency-swap/src/mock.rs +++ b/crates/pallet-currency-swap/src/mock.rs @@ -17,6 +17,8 @@ use sp_core::{H160, H256}; use crate::{self as pallet_currency_swap}; +pub(crate) const EXISTENTIAL_DEPOSIT: u64 = 10; + type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -69,7 +71,7 @@ impl pallet_balances::Config for Test { type Balance = u64; type RuntimeEvent = RuntimeEvent; type DustRemoval = (); - type ExistentialDeposit = ConstU64<10>; + type ExistentialDeposit = ConstU64; type AccountStore = System; type MaxLocks = (); type MaxReserves = (); @@ -90,7 +92,7 @@ impl pallet_evm_balances::Config for Test { type RuntimeEvent = RuntimeEvent; type AccountId = EvmAccountId; type Balance = Balance; - type ExistentialDeposit = ConstU64<10>; + type ExistentialDeposit = ConstU64; type AccountStore = EvmSystem; type DustRemoval = (); } @@ -109,6 +111,10 @@ mock! { primitives_currency_swap::ToNegativeImbalanceFor, primitives_currency_swap::ErrorFor >; + + fn estimate_swapped_balance( + balance: >::Balance, + ) -> >::Balance; } } diff --git a/crates/pallet-currency-swap/src/tests.rs b/crates/pallet-currency-swap/src/tests.rs index 1764410dc..f7aba7d6d 100644 --- a/crates/pallet-currency-swap/src/tests.rs +++ b/crates/pallet-currency-swap/src/tests.rs @@ -3,7 +3,7 @@ use frame_support::{assert_noop, assert_ok, traits::Currency}; use mockall::predicate; use sp_core::H160; -use sp_runtime::DispatchError; +use sp_runtime::{DispatchError, TokenError}; use sp_std::str::FromStr; use crate::{mock::*, *}; @@ -29,6 +29,12 @@ fn swap_works() { System::set_block_number(1); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx + .expect() + .once() + .with(predicate::eq(swap_balance)) + .return_const(swap_balance); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx .expect() @@ -61,6 +67,7 @@ fn swap_works() { })); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } @@ -86,6 +93,12 @@ fn swap_works_kill_origin() { System::set_block_number(1); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx + .expect() + .once() + .with(predicate::eq(swap_balance)) + .return_const(swap_balance); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx .expect() @@ -115,6 +128,7 @@ fn swap_works_kill_origin() { })); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } @@ -139,6 +153,12 @@ fn swap_keep_alive_works() { System::set_block_number(1); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx + .expect() + .once() + .with(predicate::eq(swap_balance)) + .return_const(swap_balance); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx .expect() @@ -171,6 +191,7 @@ fn swap_keep_alive_works() { })); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } @@ -188,6 +209,12 @@ fn swap_fails() { Balances::make_free_balance_be(&alice, alice_balance); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx + .expect() + .once() + .with(predicate::eq(swap_balance)) + .return_const(swap_balance); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx .expect() @@ -213,6 +240,46 @@ fn swap_fails() { assert_eq!(EvmBalances::total_balance(&alice_evm), 0); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); + swap_ctx.checkpoint(); + }); +} + +/// This test verifies that swap call fails in case estimated swapped balance less or equal +/// than target currency existential deposit. +#[test] +fn swap_below_ed_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 = 100; + + // Prepare the test state. + Balances::make_free_balance_be(&alice, alice_balance); + + // // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx + .expect() + .once() + .with(predicate::eq(swap_balance)) + .return_const(EXISTENTIAL_DEPOSIT - 1); + let swap_ctx = MockCurrencySwap::swap_context(); + swap_ctx.expect().never(); + + // Invoke the function under test. + assert_noop!( + CurrencySwap::swap(RuntimeOrigin::signed(alice), alice_evm, swap_balance), + DispatchError::Token(TokenError::BelowMinimum) + ); + + // Assert state changes. + assert_eq!(Balances::total_balance(&alice), alice_balance); + assert_eq!(EvmBalances::total_balance(&alice_evm), 0); + + // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } @@ -231,6 +298,12 @@ fn swap_keep_alive_fails() { Balances::make_free_balance_be(&alice, alice_balance); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx + .expect() + .once() + .with(predicate::eq(swap_balance)) + .return_const(swap_balance); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx.expect().never(); @@ -245,6 +318,46 @@ fn swap_keep_alive_fails() { assert_eq!(EvmBalances::total_balance(&alice_evm), 0); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); + swap_ctx.checkpoint(); + }); +} + +/// This test verifies that `swap_keep_alive` call fails in case estimated swapped balance less or equal +/// than target currency existential deposit. +#[test] +fn swap_keep_alive_below_ed_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 = 100; + + // Prepare the test state. + Balances::make_free_balance_be(&alice, alice_balance); + + // // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx + .expect() + .once() + .with(predicate::eq(swap_balance)) + .return_const(EXISTENTIAL_DEPOSIT - 1); + let swap_ctx = MockCurrencySwap::swap_context(); + swap_ctx.expect().never(); + + // Invoke the function under test. + assert_noop!( + CurrencySwap::swap_keep_alive(RuntimeOrigin::signed(alice), alice_evm, swap_balance), + DispatchError::Token(TokenError::BelowMinimum) + ); + + // Assert state changes. + assert_eq!(Balances::total_balance(&alice), alice_balance); + assert_eq!(EvmBalances::total_balance(&alice_evm), 0); + + // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } diff --git a/crates/precompile-currency-swap/src/lib.rs b/crates/precompile-currency-swap/src/lib.rs index a47b8aff6..dec771735 100644 --- a/crates/precompile-currency-swap/src/lib.rs +++ b/crates/precompile-currency-swap/src/lib.rs @@ -5,7 +5,7 @@ use frame_support::{ sp_runtime, sp_std::{marker::PhantomData, prelude::*}, - traits::tokens::currency::Currency, + traits::{fungible::Inspect, tokens::currency::Currency}, }; use pallet_evm::{ ExitError, ExitRevert, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, @@ -117,6 +117,20 @@ where }); } + let estimated_swapped_balance = CurrencySwapT::estimate_swapped_balance(value); + CurrencySwapT::To::can_deposit(&to, estimated_swapped_balance, false) + .into_result() + .map_err(|error| match error { + sp_runtime::DispatchError::Token(sp_runtime::TokenError::BelowMinimum) => { + PrecompileFailure::Error { + exit_status: ExitError::OutOfFund, + } + } + _ => PrecompileFailure::Error { + exit_status: ExitError::Other("unable to deposit funds".into()), + }, + })?; + let imbalance = CurrencySwapT::From::withdraw( &from, value, diff --git a/crates/precompile-currency-swap/src/mock.rs b/crates/precompile-currency-swap/src/mock.rs index 44fc45ca0..cf73bbac8 100644 --- a/crates/precompile-currency-swap/src/mock.rs +++ b/crates/precompile-currency-swap/src/mock.rs @@ -195,6 +195,10 @@ mock! { primitives_currency_swap::ToNegativeImbalanceFor, primitives_currency_swap::ErrorFor, >; + + fn estimate_swapped_balance( + balance: primitives_currency_swap::FromBalanceFor, + ) -> primitives_currency_swap::ToBalanceFor; } } diff --git a/crates/precompile-currency-swap/src/tests.rs b/crates/precompile-currency-swap/src/tests.rs index 4566c3ee3..a59d9fea0 100644 --- a/crates/precompile-currency-swap/src/tests.rs +++ b/crates/precompile-currency-swap/src/tests.rs @@ -39,6 +39,12 @@ fn swap_works() { System::set_block_number(1); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx + .expect() + .once() + .with(predicate::eq(swap_balance)) + .return_const(swap_balance); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx .expect() @@ -91,6 +97,7 @@ fn swap_works() { assert_eq!(EvmBalances::total_balance(&PRECOMPILE_ADDRESS), 0); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } @@ -125,6 +132,12 @@ fn swap_works_almost_full_balance() { System::set_block_number(1); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx + .expect() + .once() + .with(predicate::eq(swap_balance)) + .return_const(swap_balance); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx .expect() @@ -178,6 +191,7 @@ fn swap_works_almost_full_balance() { assert_eq!(EvmBalances::total_balance(&PRECOMPILE_ADDRESS), 0); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } @@ -208,6 +222,8 @@ fn swap_fail_no_funds() { System::set_block_number(1); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx.expect().never(); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx.expect().never(); @@ -248,6 +264,90 @@ fn swap_fail_no_funds() { assert_eq!(EvmBalances::total_balance(&PRECOMPILE_ADDRESS), 0); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); + swap_ctx.checkpoint(); + }); +} + +/// This test verifies that the swap precompile call behaves as expected when +/// estimated swapped balance less or equal than target currency existential deposit. +/// All fee (up to specified max fee limit!) will be consumed, but not the value. +#[test] +fn swap_fail_below_ed() { + new_test_ext().execute_with_ext(|_| { + let alice_evm = H160::from(hex_literal::hex!( + "1000000000000000000000000000000000000001" + )); + let alice = AccountId::from(hex_literal::hex!( + "1000000000000000000000000000000000000000000000000000000000000001" + )); + + let expected_gas_usage: u64 = 50_123; // all fee will be consumed + let expected_fee: Balance = gas_to_fee(expected_gas_usage); + + let alice_evm_balance = 100 * 10u128.pow(18); + let swap_balance = 10 * 10u128.pow(18); + + // Prepare the test state. + EvmBalances::make_free_balance_be(&alice_evm, alice_evm_balance); + + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice_evm), alice_evm_balance); + assert_eq!(Balances::total_balance(&alice), 0); + + // Set block number to enable events. + System::set_block_number(1); + + // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx + .expect() + .once() + .with(predicate::eq(swap_balance)) + .return_const(1_u128); + let swap_ctx = MockCurrencySwap::swap_context(); + swap_ctx.expect().never(); + + // Prepare EVM call. + let input = EvmDataWriter::new_with_selector(Action::Swap) + .write(H256::from(alice.as_ref())) + .build(); + + // Invoke the function under test. + let config = ::config(); + let execinfo = ::Runner::call( + alice_evm, + *PRECOMPILE_ADDRESS, + input, + swap_balance.into(), + 50_123, // a reasonable upper bound for tests + Some(*GAS_PRICE), + Some(*GAS_PRICE), + None, + Vec::new(), + true, + true, + config, + ) + .unwrap(); + assert_eq!( + execinfo.exit_reason, + fp_evm::ExitReason::Error(fp_evm::ExitError::OutOfFund) + ); + assert_eq!(execinfo.used_gas, expected_gas_usage.into()); + assert_eq!(execinfo.value, Vec::::new()); + assert_eq!(execinfo.logs, Vec::new()); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice_evm), + alice_evm_balance - expected_fee + ); + assert_eq!(Balances::total_balance(&alice), 0); + assert_eq!(EvmBalances::total_balance(&PRECOMPILE_ADDRESS), 0); + + // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } @@ -282,6 +382,12 @@ fn swap_fail_trait_error() { System::set_block_number(1); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx + .expect() + .once() + .with(predicate::eq(swap_balance)) + .return_const(swap_balance); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx .expect() @@ -336,6 +442,7 @@ fn swap_fail_trait_error() { assert_eq!(EvmBalances::total_balance(&PRECOMPILE_ADDRESS), 0); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } @@ -373,6 +480,8 @@ fn swap_fail_full_balance() { System::set_block_number(1); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx.expect().never(); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx.expect().never(); @@ -413,6 +522,7 @@ fn swap_fail_full_balance() { assert_eq!(EvmBalances::total_balance(&PRECOMPILE_ADDRESS), 0); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } @@ -446,6 +556,8 @@ fn swap_fail_bad_selector() { System::set_block_number(1); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx.expect().never(); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx.expect().never(); @@ -488,6 +600,7 @@ fn swap_fail_bad_selector() { assert_eq!(EvmBalances::total_balance(&PRECOMPILE_ADDRESS), 0); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } @@ -522,6 +635,8 @@ fn swap_fail_value_overflow() { System::set_block_number(1); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx.expect().never(); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx.expect().never(); @@ -561,6 +676,7 @@ fn swap_fail_value_overflow() { assert_eq!(EvmBalances::total_balance(&PRECOMPILE_ADDRESS), 0); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } @@ -594,6 +710,8 @@ fn swap_fail_no_arguments() { System::set_block_number(1); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx.expect().never(); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx.expect().never(); @@ -636,6 +754,7 @@ fn swap_fail_no_arguments() { assert_eq!(EvmBalances::total_balance(&PRECOMPILE_ADDRESS), 0); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } @@ -669,6 +788,8 @@ fn swap_fail_short_argument() { System::set_block_number(1); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx.expect().never(); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx.expect().never(); @@ -712,6 +833,7 @@ fn swap_fail_short_argument() { assert_eq!(EvmBalances::total_balance(&PRECOMPILE_ADDRESS), 0); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } @@ -745,6 +867,8 @@ fn swap_fail_trailing_junk() { System::set_block_number(1); // Set mock expectations. + let estimate_swapped_balance_ctx = MockCurrencySwap::estimate_swapped_balance_context(); + estimate_swapped_balance_ctx.expect().never(); let swap_ctx = MockCurrencySwap::swap_context(); swap_ctx.expect().never(); @@ -788,6 +912,7 @@ fn swap_fail_trailing_junk() { assert_eq!(EvmBalances::total_balance(&PRECOMPILE_ADDRESS), 0); // Assert mock invocations. + estimate_swapped_balance_ctx.checkpoint(); swap_ctx.checkpoint(); }); } diff --git a/crates/primitives-currency-swap/src/lib.rs b/crates/primitives-currency-swap/src/lib.rs index f43653f3f..7b8601c9f 100644 --- a/crates/primitives-currency-swap/src/lib.rs +++ b/crates/primitives-currency-swap/src/lib.rs @@ -3,15 +3,20 @@ // Either generate code at stadard mode, or `no_std`, based on the `std` feature presence. #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::{sp_runtime::DispatchError, traits::Currency}; +use frame_support::{ + sp_runtime::DispatchError, + traits::{fungible::Inspect, Currency}, +}; /// Currency swap interface. pub trait CurrencySwap { /// The currency to convert from. - type From: Currency; + type From: Currency + + Inspect>::Balance>; /// The currency to convert to. - type To: Currency; + type To: Currency + + Inspect>::Balance>; /// A possible error happens during the actual swap logic. type Error: Into; @@ -23,14 +28,27 @@ pub trait CurrencySwap { ToNegativeImbalanceFor, ErrorFor, >; + + /// Obtain the estimated resulted balance value. + fn estimate_swapped_balance( + balance: FromBalanceFor, + ) -> ToBalanceFor; } +/// An easy way to access the [`Currency::Balance`] of [`CurrencySwap::From`] of `T`. +pub type FromBalanceFor = + <>::From as Currency>::Balance; + /// An easy way to access the [`Currency::NegativeImbalance`] of [`CurrencySwap::From`] of `T`. pub type FromNegativeImbalanceFor = <>::From as Currency>::NegativeImbalance; +/// An easy way to access the [`Currency::Balance`] of [`CurrencySwap::To`] of `T`. +pub type ToBalanceFor = + <>::To as Currency>::Balance; + /// An easy way to access the [`Currency::NegativeImbalance`] of [`CurrencySwap::To`] of `T`. pub type ToNegativeImbalanceFor = <