diff --git a/Cargo.lock b/Cargo.lock index df0c8cf8c..a84663ef5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4621,7 +4621,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "224.0.0" +version = "225.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", @@ -8590,7 +8590,7 @@ dependencies = [ [[package]] name = "pallet-transaction-multi-payment" -version = "9.4.0" +version = "9.5.0" dependencies = [ "frame-support", "frame-system", @@ -8603,6 +8603,7 @@ dependencies = [ "pallet-evm", "pallet-evm-accounts", "pallet-transaction-payment", + "pallet-utility", "parity-scale-codec", "primitives", "scale-info", @@ -11365,7 +11366,7 @@ dependencies = [ [[package]] name = "runtime-integration-tests" -version = "1.19.10" +version = "1.19.11" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 7684cc98a..9c63774af 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime-integration-tests" -version = "1.19.10" +version = "1.19.11" description = "Integration tests" authors = ["GalacticCouncil"] edition = "2021" diff --git a/integration-tests/src/non_native_fee.rs b/integration-tests/src/non_native_fee.rs index 13fef8988..beb48fd69 100644 --- a/integration-tests/src/non_native_fee.rs +++ b/integration-tests/src/non_native_fee.rs @@ -80,6 +80,198 @@ fn non_native_fee_payment_works_with_oracle_price_based_on_onchain_route() { }); } +#[test] +fn set_currency_should_work_in_batch_transaction_when_first_tx() { + TestNet::reset(); + + // batch + Hydra::execute_with(|| { + let first_inner_call = hydradx_runtime::RuntimeCall::MultiTransactionPayment( + pallet_transaction_multi_payment::Call::set_currency { currency: BTC }, + ); + let second_inner_call = hydradx_runtime::RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + let call = hydradx_runtime::RuntimeCall::Utility(pallet_utility::Call::batch { + calls: vec![first_inner_call, second_inner_call], + }); + + let info = DispatchInfo { + weight: Weight::from_parts(106_957_000, 0), + ..Default::default() + }; + let len: usize = 10; + + assert_ok!( + pallet_transaction_payment::ChargeTransactionPayment::::from(0).pre_dispatch( + &AccountId::from(BOB), + &call, + &info, + len, + ) + ); + let bob_balance = hydradx_runtime::Tokens::free_balance(BTC, &AccountId::from(BOB)); + assert_eq!(bob_balance, 999991); + }); + + TestNet::reset(); + + // batch_all + Hydra::execute_with(|| { + let first_inner_call = hydradx_runtime::RuntimeCall::MultiTransactionPayment( + pallet_transaction_multi_payment::Call::set_currency { currency: BTC }, + ); + let second_inner_call = hydradx_runtime::RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + let call = hydradx_runtime::RuntimeCall::Utility(pallet_utility::Call::batch_all { + calls: vec![first_inner_call, second_inner_call], + }); + + let info = DispatchInfo { + weight: Weight::from_parts(106_957_000, 0), + ..Default::default() + }; + let len: usize = 10; + + assert_ok!( + pallet_transaction_payment::ChargeTransactionPayment::::from(0).pre_dispatch( + &AccountId::from(BOB), + &call, + &info, + len, + ) + ); + let bob_balance = hydradx_runtime::Tokens::free_balance(BTC, &AccountId::from(BOB)); + assert_eq!(bob_balance, 999991); + }); + + TestNet::reset(); + + // batch_all + Hydra::execute_with(|| { + let first_inner_call = hydradx_runtime::RuntimeCall::MultiTransactionPayment( + pallet_transaction_multi_payment::Call::set_currency { currency: BTC }, + ); + let second_inner_call = hydradx_runtime::RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + let call = hydradx_runtime::RuntimeCall::Utility(pallet_utility::Call::force_batch { + calls: vec![first_inner_call, second_inner_call], + }); + + let info = DispatchInfo { + weight: Weight::from_parts(106_957_000, 0), + ..Default::default() + }; + let len: usize = 10; + + assert_ok!( + pallet_transaction_payment::ChargeTransactionPayment::::from(0).pre_dispatch( + &AccountId::from(BOB), + &call, + &info, + len, + ) + ); + let bob_balance = hydradx_runtime::Tokens::free_balance(BTC, &AccountId::from(BOB)); + assert_eq!(bob_balance, 999991); + }); +} + +#[test] +fn set_currency_should_not_work_in_batch_transaction_when_not_first_tx() { + TestNet::reset(); + + // batch + Hydra::execute_with(|| { + let first_inner_call = hydradx_runtime::RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + let second_inner_call = hydradx_runtime::RuntimeCall::MultiTransactionPayment( + pallet_transaction_multi_payment::Call::set_currency { currency: BTC }, + ); + let call = hydradx_runtime::RuntimeCall::Utility(pallet_utility::Call::batch { + calls: vec![first_inner_call, second_inner_call], + }); + + let info = DispatchInfo { + weight: Weight::from_parts(106_957_000, 0), + ..Default::default() + }; + let len: usize = 10; + + let bob_initial_balance = hydradx_runtime::Tokens::free_balance(BTC, &AccountId::from(BOB)); + + assert_ok!( + pallet_transaction_payment::ChargeTransactionPayment::::from(0).pre_dispatch( + &AccountId::from(BOB), + &call, + &info, + len, + ) + ); + let bob_balance = hydradx_runtime::Tokens::free_balance(BTC, &AccountId::from(BOB)); + assert_eq!(bob_balance, bob_initial_balance); + }); + + TestNet::reset(); + + // batch_all + Hydra::execute_with(|| { + let first_inner_call = hydradx_runtime::RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + let second_inner_call = hydradx_runtime::RuntimeCall::MultiTransactionPayment( + pallet_transaction_multi_payment::Call::set_currency { currency: BTC }, + ); + let call = hydradx_runtime::RuntimeCall::Utility(pallet_utility::Call::batch_all { + calls: vec![first_inner_call, second_inner_call], + }); + + let info = DispatchInfo { + weight: Weight::from_parts(106_957_000, 0), + ..Default::default() + }; + let len: usize = 10; + + let bob_initial_balance = hydradx_runtime::Tokens::free_balance(BTC, &AccountId::from(BOB)); + + assert_ok!( + pallet_transaction_payment::ChargeTransactionPayment::::from(0).pre_dispatch( + &AccountId::from(BOB), + &call, + &info, + len, + ) + ); + let bob_balance = hydradx_runtime::Tokens::free_balance(BTC, &AccountId::from(BOB)); + assert_eq!(bob_balance, bob_initial_balance); + }); + + TestNet::reset(); + + // batch_all + Hydra::execute_with(|| { + let first_inner_call = hydradx_runtime::RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + let second_inner_call = hydradx_runtime::RuntimeCall::MultiTransactionPayment( + pallet_transaction_multi_payment::Call::set_currency { currency: BTC }, + ); + let call = hydradx_runtime::RuntimeCall::Utility(pallet_utility::Call::force_batch { + calls: vec![first_inner_call, second_inner_call], + }); + + let info = DispatchInfo { + weight: Weight::from_parts(106_957_000, 0), + ..Default::default() + }; + let len: usize = 10; + + let bob_initial_balance = hydradx_runtime::Tokens::free_balance(BTC, &AccountId::from(BOB)); + + assert_ok!( + pallet_transaction_payment::ChargeTransactionPayment::::from(0).pre_dispatch( + &AccountId::from(BOB), + &call, + &info, + len, + ) + ); + let bob_balance = hydradx_runtime::Tokens::free_balance(BTC, &AccountId::from(BOB)); + assert_eq!(bob_balance, bob_initial_balance); + }); +} + const HITCHHIKER: [u8; 32] = [42u8; 32]; #[test] diff --git a/pallets/transaction-multi-payment/Cargo.toml b/pallets/transaction-multi-payment/Cargo.toml index c69295519..493d3b5eb 100644 --- a/pallets/transaction-multi-payment/Cargo.toml +++ b/pallets/transaction-multi-payment/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-transaction-multi-payment" -version = "9.4.0" +version = "9.5.0" description = "Transaction multi currency payment support module" authors = ["GalacticCoucil"] edition = "2021" @@ -30,6 +30,7 @@ sp-core = { workspace = true } sp-std = { workspace = true } sp-runtime = { workspace = true } pallet-transaction-payment = { workspace = true } +pallet-utility = { workspace = true } pallet-evm = { workspace = true, optional = true } diff --git a/pallets/transaction-multi-payment/src/lib.rs b/pallets/transaction-multi-payment/src/lib.rs index 55f101511..42fd58552 100644 --- a/pallets/transaction-multi-payment/src/lib.rs +++ b/pallets/transaction-multi-payment/src/lib.rs @@ -405,13 +405,14 @@ pub struct TransferFees(PhantomData<(MC, DF, FR)>); impl OnChargeTransaction for TransferFees where - T: Config, + T: Config + pallet_utility::Config, MC: MultiCurrency<::AccountId>, AssetIdOf: Into, MC::Balance: FixedPointOperand, FR: Get, DF: DepositFee, - ::RuntimeCall: IsSubType>, + ::RuntimeCall: IsSubType> + IsSubType>, + ::RuntimeCall: IsSubType>, BalanceOf: FixedPointOperand, { type LiquidityInfo = Option, Price>>; @@ -422,8 +423,8 @@ where /// Note: The `fee` already includes the `tip`. fn withdraw_fee( who: &T::AccountId, - call: &T::RuntimeCall, - _info: &DispatchInfoOf, + call: &::RuntimeCall, + _info: &DispatchInfoOf<::RuntimeCall>, fee: Self::Balance, _tip: Self::Balance, ) -> Result { @@ -431,9 +432,22 @@ where return Ok(None); } - let currency = match call.is_sub_type() { - Some(Call::set_currency { currency }) => *currency, - _ => Pallet::::account_currency(who), + let currency = if let Some(Call::set_currency { currency }) = call.is_sub_type() { + *currency + } else if let Some(pallet_utility::pallet::Call::batch { calls }) + | Some(pallet_utility::pallet::Call::batch_all { calls }) + | Some(pallet_utility::pallet::Call::force_batch { calls }) = call.is_sub_type() + { + // `calls` can be empty Vec + match calls.first() { + Some(first_call) => match first_call.is_sub_type() { + Some(Call::set_currency { currency }) => *currency, + _ => Pallet::::account_currency(who), + }, + _ => Pallet::::account_currency(who), + } + } else { + Pallet::::account_currency(who) }; let price = Pallet::::get_currency_price(currency) @@ -460,8 +474,8 @@ where /// Note: The `fee` already includes the `tip`. fn correct_and_deposit_fee( who: &T::AccountId, - _dispatch_info: &DispatchInfoOf, - _post_info: &PostDispatchInfoOf, + _dispatch_info: &DispatchInfoOf<::RuntimeCall>, + _post_info: &PostDispatchInfoOf<::RuntimeCall>, corrected_fee: Self::Balance, tip: Self::Balance, already_withdrawn: Self::LiquidityInfo, diff --git a/pallets/transaction-multi-payment/src/mock.rs b/pallets/transaction-multi-payment/src/mock.rs index d2705dfba..b4e2f3c79 100644 --- a/pallets/transaction-multi-payment/src/mock.rs +++ b/pallets/transaction-multi-payment/src/mock.rs @@ -90,6 +90,7 @@ frame_support::construct_runtime!( Currencies: pallet_currencies, Tokens: orml_tokens, EVMAccounts: pallet_evm_accounts, + Utility: pallet_utility, } ); @@ -287,6 +288,13 @@ impl pallet_evm_accounts::Config for Test { type WeightInfo = (); } +impl pallet_utility::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + pub struct ExtBuilder { base_weight: Weight, native_balances: Vec<(AccountId, Balance)>, diff --git a/pallets/transaction-multi-payment/src/tests.rs b/pallets/transaction-multi-payment/src/tests.rs index d49e05eed..20148acad 100644 --- a/pallets/transaction-multi-payment/src/tests.rs +++ b/pallets/transaction-multi-payment/src/tests.rs @@ -127,6 +127,207 @@ fn set_supported_currency_without_spot_price_should_charge_fee_in_correct_curren }); } +#[test] +fn set_supported_currency_in_batch_should_charge_fee_in_correct_currency() { + // batch + ExtBuilder::default().base_weight(5).build().execute_with(|| { + let first_inner_call = RuntimeCall::PaymentPallet(crate::Call::set_currency { + currency: SUPPORTED_CURRENCY, + }); + let second_inner_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + let call = RuntimeCall::Utility(pallet_utility::Call::batch { + calls: vec![first_inner_call, second_inner_call], + }); + + let len = 10; + let info = info_from_weight(Weight::from_parts(5, 0)); + + let pre = ChargeTransactionPayment::::from(0).pre_dispatch(&ALICE, &call, &info, len); + assert!(pre.is_ok()); + + assert_eq!( + Currencies::free_balance(SUPPORTED_CURRENCY, &ALICE), + 999_999_999_999_970 + ); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre.unwrap()), + &info, + &default_post_info(), + len, + &Ok(()) + )); + assert_eq!(Currencies::free_balance(SUPPORTED_CURRENCY, &FEE_RECEIVER), 30); + }); + + // batch_all + ExtBuilder::default().base_weight(5).build().execute_with(|| { + let first_inner_call = RuntimeCall::PaymentPallet(crate::Call::set_currency { + currency: SUPPORTED_CURRENCY, + }); + let second_inner_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + let call = RuntimeCall::Utility(pallet_utility::Call::batch_all { + calls: vec![first_inner_call, second_inner_call], + }); + + let len = 10; + let info = info_from_weight(Weight::from_parts(5, 0)); + + let pre = ChargeTransactionPayment::::from(0).pre_dispatch(&ALICE, &call, &info, len); + assert!(pre.is_ok()); + + assert_eq!( + Currencies::free_balance(SUPPORTED_CURRENCY, &ALICE), + 999_999_999_999_970 + ); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre.unwrap()), + &info, + &default_post_info(), + len, + &Ok(()) + )); + assert_eq!(Currencies::free_balance(SUPPORTED_CURRENCY, &FEE_RECEIVER), 30); + }); + + // batch_all + ExtBuilder::default().base_weight(5).build().execute_with(|| { + let first_inner_call = RuntimeCall::PaymentPallet(crate::Call::set_currency { + currency: SUPPORTED_CURRENCY, + }); + let second_inner_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + let call = RuntimeCall::Utility(pallet_utility::Call::force_batch { + calls: vec![first_inner_call, second_inner_call], + }); + + let len = 10; + let info = info_from_weight(Weight::from_parts(5, 0)); + + let pre = ChargeTransactionPayment::::from(0).pre_dispatch(&ALICE, &call, &info, len); + assert!(pre.is_ok()); + + assert_eq!( + Currencies::free_balance(SUPPORTED_CURRENCY, &ALICE), + 999_999_999_999_970 + ); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre.unwrap()), + &info, + &default_post_info(), + len, + &Ok(()) + )); + assert_eq!(Currencies::free_balance(SUPPORTED_CURRENCY, &FEE_RECEIVER), 30); + }); +} + +#[test] +fn set_supported_currency_in_batch_should_not_work_if_not_first_transaction() { + // batch + ExtBuilder::default().base_weight(5).build().execute_with(|| { + let first_inner_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + let second_inner_call = RuntimeCall::PaymentPallet(crate::Call::set_currency { + currency: SUPPORTED_CURRENCY, + }); + let call = RuntimeCall::Utility(pallet_utility::Call::batch { + calls: vec![first_inner_call, second_inner_call], + }); + + let len = 10; + let info = info_from_weight(Weight::from_parts(5, 0)); + + let alice_initial_non_native_balance = Currencies::free_balance(SUPPORTED_CURRENCY, &ALICE); + + let pre = ChargeTransactionPayment::::from(0).pre_dispatch(&ALICE, &call, &info, len); + assert!(pre.is_ok()); + + assert_eq!(Currencies::free_balance(HDX, &ALICE), 999_999_999_999_980); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre.unwrap()), + &info, + &default_post_info(), + len, + &Ok(()) + )); + assert_eq!(Currencies::free_balance(HDX, &FEE_RECEIVER), 20); + assert_eq!( + Currencies::free_balance(SUPPORTED_CURRENCY, &ALICE), + alice_initial_non_native_balance + ); + }); + + // batch_all + ExtBuilder::default().base_weight(5).build().execute_with(|| { + let first_inner_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + let second_inner_call = RuntimeCall::PaymentPallet(crate::Call::set_currency { + currency: SUPPORTED_CURRENCY, + }); + let call = RuntimeCall::Utility(pallet_utility::Call::batch_all { + calls: vec![first_inner_call, second_inner_call], + }); + + let len = 10; + let info = info_from_weight(Weight::from_parts(5, 0)); + + let alice_initial_non_native_balance = Currencies::free_balance(SUPPORTED_CURRENCY, &ALICE); + + let pre = ChargeTransactionPayment::::from(0).pre_dispatch(&ALICE, &call, &info, len); + assert!(pre.is_ok()); + + assert_eq!(Currencies::free_balance(HDX, &ALICE), 999_999_999_999_980); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre.unwrap()), + &info, + &default_post_info(), + len, + &Ok(()) + )); + assert_eq!(Currencies::free_balance(HDX, &FEE_RECEIVER), 20); + assert_eq!( + Currencies::free_balance(SUPPORTED_CURRENCY, &ALICE), + alice_initial_non_native_balance + ); + }); + + // batch_all + ExtBuilder::default().base_weight(5).build().execute_with(|| { + let first_inner_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + let second_inner_call = RuntimeCall::PaymentPallet(crate::Call::set_currency { + currency: SUPPORTED_CURRENCY, + }); + let call = RuntimeCall::Utility(pallet_utility::Call::force_batch { + calls: vec![first_inner_call, second_inner_call], + }); + + let len = 10; + let info = info_from_weight(Weight::from_parts(5, 0)); + + let alice_initial_non_native_balance = Currencies::free_balance(SUPPORTED_CURRENCY, &ALICE); + + let pre = ChargeTransactionPayment::::from(0).pre_dispatch(&ALICE, &call, &info, len); + assert!(pre.is_ok()); + + assert_eq!(Currencies::free_balance(HDX, &ALICE), 999_999_999_999_980); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre.unwrap()), + &info, + &default_post_info(), + len, + &Ok(()) + )); + assert_eq!(Currencies::free_balance(HDX, &FEE_RECEIVER), 20); + assert_eq!( + Currencies::free_balance(SUPPORTED_CURRENCY, &ALICE), + alice_initial_non_native_balance + ); + }); +} + #[test] fn set_supported_currency_with_spot_price_should_charge_fee_in_correct_currency() { ExtBuilder::default().base_weight(5).build().execute_with(|| { diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index c90d4e2a5..aceda94c3 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "224.0.0" +version = "225.0.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index f6586e87d..8e2faf1c4 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -109,7 +109,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("hydradx"), impl_name: create_runtime_str!("hydradx"), authoring_version: 1, - spec_version: 224, + spec_version: 225, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1,