diff --git a/delay-tasks/src/lib.rs b/delay-tasks/src/lib.rs index 97c758741..7990bf05b 100644 --- a/delay-tasks/src/lib.rs +++ b/delay-tasks/src/lib.rs @@ -13,6 +13,7 @@ use orml_traits::{ task::{DelayTaskHooks, DelayTasksManager, DispatchableTask}, MultiCurrency, NamedMultiReservableCurrency, }; +use orml_xtokens::XtokensTask; use parity_scale_codec::FullCodec; use scale_info::TypeInfo; use sp_runtime::{ @@ -271,7 +272,7 @@ pub mod module { } pub struct DelayedXtokensTaskHooks(PhantomData); - impl DelayTaskHooks> for DelayedXtokensTaskHooks + impl DelayTaskHooks> for DelayedXtokensTaskHooks where ::Currency: MultiCurrency< T::AccountId, @@ -279,9 +280,9 @@ pub mod module { Balance = ::Balance, >, { - fn pre_delay(task: &orml_xtokens::XtokensTask) -> DispatchResult { + fn pre_delay(task: &XtokensTask) -> DispatchResult { match task { - orml_xtokens::XtokensTask::::TransferAssets { who, assets, .. } => { + XtokensTask::::TransferAssets { who, assets, .. } => { let asset_len = assets.len(); for i in 0..asset_len { let asset = assets.get(i).ok_or(Error::::AssetIndexNonExistent)?; @@ -303,9 +304,9 @@ pub mod module { Ok(()) } - fn pre_delayed_execute(task: &orml_xtokens::XtokensTask) -> DispatchResult { + fn pre_delayed_execute(task: &XtokensTask) -> DispatchResult { match task { - orml_xtokens::XtokensTask::::TransferAssets { who, assets, .. } => { + XtokensTask::::TransferAssets { who, assets, .. } => { let asset_len = assets.len(); for i in 0..asset_len { let asset = assets.get(i).ok_or(Error::::AssetIndexNonExistent)?; @@ -327,7 +328,7 @@ pub mod module { Ok(()) } - fn pre_cancel(task: &orml_xtokens::XtokensTask) -> DispatchResult { + fn pre_cancel(task: &XtokensTask) -> DispatchResult { Self::pre_delayed_execute(task) } } diff --git a/delay-tasks/src/mock.rs b/delay-tasks/src/mock.rs index 08b16531c..7edd1e1f2 100644 --- a/delay-tasks/src/mock.rs +++ b/delay-tasks/src/mock.rs @@ -5,10 +5,13 @@ use super::*; use frame_support::{ construct_runtime, derive_impl, parameter_types, - traits::{ConstU128, EqualPrivilegeOnly, Everything}, + traits::{EqualPrivilegeOnly, Everything}, }; use frame_system::EnsureRoot; -use orml_traits::{define_combined_task_and_bind_delay_hooks, parameter_type_with_key, task::TaskResult}; +use orml_traits::{ + define_combined_task_and_bind_delay_hooks, location::AbsoluteReserveProvider, parameter_type_with_key, + task::TaskResult, +}; use serde::{Deserialize, Serialize}; use sp_runtime::{traits::IdentityLookup, AccountId32, BuildStorage, DispatchError}; use sp_std::cell::RefCell; @@ -29,23 +32,6 @@ impl frame_system::Config for Runtime { type AccountId = AccountId; type Lookup = IdentityLookup; type Block = Block; - type AccountData = pallet_balances::AccountData; -} - -impl pallet_balances::Config for Runtime { - type MaxLocks = ConstU32<50>; - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU128<1>; - type AccountStore = System; - type WeightInfo = (); - type MaxReserves = ConstU32<50>; - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; - type FreezeIdentifier = [u8; 8]; - type MaxFreezes = (); } impl pallet_preimage::Config for Runtime { @@ -181,6 +167,85 @@ impl orml_tokens::Config for Runtime { type DustRemovalWhitelist = Everything; } +parameter_types! { + pub SelfLocation: Location = Location::new(1, [Parachain(2000)]); +} + +pub struct AccountIdToLocation; +impl Convert for AccountIdToLocation { + fn convert(account: AccountId) -> Location { + [Junction::AccountId32 { + network: None, + id: account.into(), + }] + .into() + } +} + +parameter_type_with_key! { + pub ParachainMinFee: |location: Location| -> Option { + #[allow(clippy::match_ref_pats)] // false positive + match (location.parents, location.first_interior()) { + (1, Some(Parachain(3))) => Some(100), + _ => None, + } + }; +} + +pub enum Weightless {} +impl PreparedMessage for Weightless { + fn weight_of(&self) -> Weight { + unreachable!() + } +} + +pub struct MockExec; +impl ExecuteXcm for MockExec { + type Prepared = Weightless; + + fn prepare(_message: Xcm) -> Result> { + unreachable!() + } + + fn execute(_origin: impl Into, _pre: Weightless, _hash: &mut XcmHash, _weight_credit: Weight) -> Outcome { + unreachable!() + } + + fn charge_fees(_location: impl Into, _fees: Assets) -> XcmResult { + Err(XcmError::Unimplemented) + } +} + +parameter_types! { + pub UniversalLocation: InteriorLocation = Here; + pub const UnitWeightCost: Weight = Weight::from_parts(10, 10); + pub const BaseXcmWeight: Weight = Weight::from_parts(100_000_000, 100_000_000); + pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; + pub const MaxAssetsForTransfer: usize = 2; +} + +impl orml_xtokens::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type CurrencyId = CurrencyId; + type CurrencyIdConvert = CurrencyIdConvert; + type AccountIdToLocation = AccountIdToLocation; + type SelfLocation = SelfLocation; + type XcmExecutor = MockExec; + type Weigher = xcm_builder::FixedWeightBounds; + type BaseXcmWeight = BaseXcmWeight; + type UniversalLocation = UniversalLocation; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; + type LocationsFilter = Everything; + type ReserveProvider = AbsoluteReserveProvider; + type RateLimiter = (); + type RateLimiterId = (); + type Task = MockTaskType; + type DelayTasks = DelayTasks; +} + thread_local! { pub static DISPATCH_SUCCEEDED: RefCell = RefCell::new(0); pub static DISPATCH_FAILED: RefCell = RefCell::new(0); @@ -325,6 +390,7 @@ define_combined_task_and_bind_delay_hooks! { FailPreDelay(FailPreDelayTask, FailPreDelayTaskHook), FailPreDelayedExecute(FailPreDelayedExecuteTask, FailPreDelayedExecuteTaskHook), FailPreCancel(FailPreCancelTask, FailPreCancelTaskHook), + Xtokens(XtokensTask, DelayedXtokensTaskHooks), } } @@ -356,7 +422,7 @@ construct_runtime!( Scheduler: pallet_scheduler, Preimage: pallet_preimage, Tokens: orml_tokens, - Balances: pallet_balances, + XTokens: orml_xtokens, } ); diff --git a/delay-tasks/src/tests.rs b/delay-tasks/src/tests.rs index 816e81e1e..0c720c497 100644 --- a/delay-tasks/src/tests.rs +++ b/delay-tasks/src/tests.rs @@ -301,3 +301,134 @@ fn do_delayed_execute_work() { assert_eq!(PRE_DELAYED_EXECUTE_SUCCEEDED.with(|v| *v.borrow()), 2); }); } + +#[test] +fn delayed_xtokens_task_hooks_work() { + ExtBuilder::default().build().execute_with(|| { + let assets: Assets = Assets::from(vec![ + (Location::parent(), 1000).into(), + ( + ( + Parent, + Parachain(1), + Junction::from(BoundedVec::try_from(b"A".to_vec()).unwrap()), + ), + 2000, + ) + .into(), + ( + ( + Parent, + Parachain(2), + Junction::from(BoundedVec::try_from(b"B".to_vec()).unwrap()), + ), + 3000, + ) + .into(), + ]); + let fee: Asset = (Location::parent(), 1000).into(); + let dest: Location = ( + Parent, + Parachain(2), + Junction::AccountId32 { + network: None, + id: BOB.into(), + }, + ) + .into(); + let task = XtokensTask::::TransferAssets { + who: ALICE, + assets, + fee, + dest, + dest_weight_limit: WeightLimit::Unlimited, + }; + + assert_ok!(Tokens::deposit(CurrencyId::R, &ALICE, 3000)); + assert_ok!(Tokens::deposit(CurrencyId::A, &ALICE, 3000)); + assert_ok!(Tokens::deposit(CurrencyId::B, &ALICE, 3000)); + assert_eq!(Tokens::free_balance(CurrencyId::R, &ALICE), 3000); + assert_eq!(Tokens::free_balance(CurrencyId::A, &ALICE), 3000); + assert_eq!(Tokens::free_balance(CurrencyId::B, &ALICE), 3000); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::R, &ALICE), + 0 + ); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::A, &ALICE), + 0 + ); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::B, &ALICE), + 0 + ); + + assert_ok!(DelayedXtokensTaskHooks::::pre_delay(&task)); + assert_eq!(Tokens::free_balance(CurrencyId::R, &ALICE), 2000); + assert_eq!(Tokens::free_balance(CurrencyId::A, &ALICE), 1000); + assert_eq!(Tokens::free_balance(CurrencyId::B, &ALICE), 0); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::R, &ALICE), + 1000 + ); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::A, &ALICE), + 2000 + ); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::B, &ALICE), + 3000 + ); + + assert_ok!(DelayedXtokensTaskHooks::::pre_delayed_execute(&task)); + assert_eq!(Tokens::free_balance(CurrencyId::R, &ALICE), 3000); + assert_eq!(Tokens::free_balance(CurrencyId::A, &ALICE), 3000); + assert_eq!(Tokens::free_balance(CurrencyId::B, &ALICE), 3000); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::R, &ALICE), + 0 + ); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::A, &ALICE), + 0 + ); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::B, &ALICE), + 0 + ); + + assert_ok!(DelayedXtokensTaskHooks::::pre_delay(&task)); + assert_eq!(Tokens::free_balance(CurrencyId::R, &ALICE), 2000); + assert_eq!(Tokens::free_balance(CurrencyId::A, &ALICE), 1000); + assert_eq!(Tokens::free_balance(CurrencyId::B, &ALICE), 0); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::R, &ALICE), + 1000 + ); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::A, &ALICE), + 2000 + ); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::B, &ALICE), + 3000 + ); + + assert_ok!(DelayedXtokensTaskHooks::::pre_cancel(&task)); + assert_eq!(Tokens::free_balance(CurrencyId::R, &ALICE), 3000); + assert_eq!(Tokens::free_balance(CurrencyId::A, &ALICE), 3000); + assert_eq!(Tokens::free_balance(CurrencyId::B, &ALICE), 3000); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::R, &ALICE), + 0 + ); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::A, &ALICE), + 0 + ); + assert_eq!( + Tokens::reserved_balance_named(&ReserveId::get(), CurrencyId::B, &ALICE), + 0 + ); + }); +}