diff --git a/pallets/xcm-transactor/src/benchmarks.rs b/pallets/xcm-transactor/src/benchmarks.rs index 8d0febe19b8..1c5c3099983 100644 --- a/pallets/xcm-transactor/src/benchmarks.rs +++ b/pallets/xcm-transactor/src/benchmarks.rs @@ -177,7 +177,7 @@ benchmarks! { let result = Pallet::::transact_through_sovereign( RawOrigin::Root.into(), Box::new(xcm::VersionedLocation::V4(location.clone())), - user.clone(), + Some(user.clone()), CurrencyPayment { // This might involve a db Read when translating, therefore worst case currency: Currency::AsCurrencyId(currency), diff --git a/pallets/xcm-transactor/src/lib.rs b/pallets/xcm-transactor/src/lib.rs index f17d10c16ca..0dd8ecd0cfa 100644 --- a/pallets/xcm-transactor/src/lib.rs +++ b/pallets/xcm-transactor/src/lib.rs @@ -365,7 +365,7 @@ pub mod pallet { }, /// Transacted the call through the sovereign account in a destination chain. TransactedSovereign { - fee_payer: T::AccountId, + fee_payer: Option, dest: Location, call: Vec, }, @@ -598,7 +598,7 @@ pub mod pallet { // destination to which the message should be sent dest: Box, // account paying for fees - fee_payer: T::AccountId, + fee_payer: Option, // fee to be used fee: CurrencyPayment>, // call to be executed in destination @@ -657,7 +657,7 @@ pub mod pallet { // Grab the destination Self::transact_in_dest_chain_asset_non_signed( dest.clone(), - Some(fee_payer.clone()), + fee_payer.clone(), fee, call.clone(), origin_kind, diff --git a/pallets/xcm-transactor/src/tests.rs b/pallets/xcm-transactor/src/tests.rs index bb55624d8c6..3aea4681657 100644 --- a/pallets/xcm-transactor/src/tests.rs +++ b/pallets/xcm-transactor/src/tests.rs @@ -483,7 +483,7 @@ fn test_root_can_transact_through_sovereign() { XcmTransactor::transact_through_sovereign( RuntimeOrigin::signed(1), Box::new(xcm::VersionedLocation::V4(Location::parent())), - 1u64, + Some(1u64), CurrencyPayment { currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( Location::parent() @@ -521,7 +521,7 @@ fn test_root_can_transact_through_sovereign() { assert_ok!(XcmTransactor::transact_through_sovereign( RuntimeOrigin::root(), Box::new(xcm::VersionedLocation::V4(Location::parent())), - 1u64, + Some(1u64), CurrencyPayment { currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( Location::parent() @@ -551,7 +551,7 @@ fn test_root_can_transact_through_sovereign() { fee_per_second: 1, }, crate::Event::TransactedSovereign { - fee_payer: 1u64, + fee_payer: Some(1u64), dest: Location::parent(), call: vec![1u8], }, @@ -982,7 +982,7 @@ fn test_send_through_sovereign_with_custom_weight_and_fee() { assert_ok!(XcmTransactor::transact_through_sovereign( RuntimeOrigin::root(), Box::new(xcm::VersionedLocation::V4(Location::parent())), - 1u64, + Some(1u64), CurrencyPayment { currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( Location::parent() @@ -1004,7 +1004,7 @@ fn test_send_through_sovereign_with_custom_weight_and_fee() { index: 1, }, crate::Event::TransactedSovereign { - fee_payer: 1u64, + fee_payer: Some(1u64), dest: Location::parent(), call: vec![1u8], }, @@ -1028,6 +1028,71 @@ fn test_send_through_sovereign_with_custom_weight_and_fee() { }) } +#[test] +fn test_transact_through_sovereign_with_fee_payer_none() { + ExtBuilder::default() + .with_balances(vec![]) + .build() + .execute_with(|| { + // Root can register + assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1)); + + let total_weight: Weight = 10_100u64.into(); + let tx_weight: Weight = 100_u64.into(); + let total_fee = 100u128; + + assert_ok!(XcmTransactor::transact_through_sovereign( + RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // We don't specify any fee_payer, instead we pay fees with the + // sovereign account funds directly on the destination. + None, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: Some(total_fee) + }, + vec![1u8], + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: tx_weight, + overall_weight: Some(Limited(total_weight)) + }, + false + )); + + let expected = vec![ + crate::Event::RegisteredDerivative { + account_id: 1u64, + index: 1, + }, + crate::Event::TransactedSovereign { + fee_payer: None, + dest: Location::parent(), + call: vec![1u8], + }, + ]; + assert_eq!(events(), expected); + let sent_messages = mock::sent_xcm(); + let (_, sent_message) = sent_messages.first().unwrap(); + // Lets make sure the message is as expected even if we haven't indicated a + // fee_payer. + assert!(sent_message + .0 + .contains(&WithdrawAsset((Location::here(), total_fee).into()))); + assert!(sent_message.0.contains(&BuyExecution { + fees: (Location::here(), total_fee).into(), + weight_limit: Limited(total_weight), + })); + assert!(sent_message.0.contains(&Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: tx_weight, + call: vec![1u8].into(), + })); + }) +} + #[test] fn test_send_through_signed_with_custom_weight_and_fee() { ExtBuilder::default() diff --git a/runtime/moonbase/tests/xcm_tests.rs b/runtime/moonbase/tests/xcm_tests.rs index eca9ba91dd7..f11ebf2c9cf 100644 --- a/runtime/moonbase/tests/xcm_tests.rs +++ b/runtime/moonbase/tests/xcm_tests.rs @@ -1574,7 +1574,7 @@ fn transact_through_sovereign() { assert_ok!(XcmTransactor::transact_through_sovereign( parachain::RuntimeOrigin::root(), Box::new(xcm::VersionedLocation::V4(dest)), - PARAALICE.into(), + Some(PARAALICE.into()), CurrencyPayment { currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( Location::parent() @@ -1599,6 +1599,111 @@ fn transact_through_sovereign() { }); } +#[test] +fn transact_through_sovereign_fee_payer_none() { + MockNet::reset(); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + None + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + }); + + let derivative_address = derivative_account_id(para_a_account(), 0); + + Relay::execute_with(|| { + // Transfer 100 tokens to derivative_address on the relay + assert_ok!(RelayBalances::transfer_keep_alive( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derivative_address.clone(), + 100u128 + )); + + // Transfer the XCM execution fee amount to ParaA's sovereign account + assert_ok!(RelayBalances::transfer_keep_alive( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_a_account(), + 4000003000u128 + )); + }); + + // Check balances before the transact call + Relay::execute_with(|| { + assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000003000); + assert_eq!(RelayBalances::free_balance(&derivative_address), 100); + assert_eq!(RelayBalances::free_balance(&RELAYBOB), 0); + }); + + // Encode the call. Balances transfer of 100 relay tokens to RELAYBOB + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: RELAYBOB, + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + // The final call will be an AsDerivative using index 0 + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: /* Here */ [].into(), + }; + + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + // No fee_payer here. The sovereign account will pay the fees on destination. + None, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + // Check balances after the transact call are correct + Relay::execute_with(|| { + assert_eq!(RelayBalances::free_balance(¶_a_account()), 0); + assert_eq!(RelayBalances::free_balance(&derivative_address), 0); + assert_eq!(RelayBalances::free_balance(&RELAYBOB), 100); + }); +} + #[test] fn transact_through_sovereign_with_custom_fee_weight() { MockNet::reset(); @@ -1726,7 +1831,7 @@ fn transact_through_sovereign_with_custom_fee_weight() { assert_ok!(XcmTransactor::transact_through_sovereign( parachain::RuntimeOrigin::root(), Box::new(xcm::VersionedLocation::V4(dest)), - PARAALICE.into(), + Some(PARAALICE.into()), CurrencyPayment { currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( Location::parent() @@ -1879,7 +1984,7 @@ fn transact_through_sovereign_with_custom_fee_weight_refund() { assert_ok!(XcmTransactor::transact_through_sovereign( parachain::RuntimeOrigin::root(), Box::new(xcm::VersionedLocation::V4(dest)), - PARAALICE.into(), + Some(PARAALICE.into()), CurrencyPayment { currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( Location::parent() diff --git a/runtime/moonbeam/tests/xcm_tests.rs b/runtime/moonbeam/tests/xcm_tests.rs index 92a36fec770..20e551468c2 100644 --- a/runtime/moonbeam/tests/xcm_tests.rs +++ b/runtime/moonbeam/tests/xcm_tests.rs @@ -1453,7 +1453,7 @@ fn transact_through_sovereign() { assert_ok!(XcmTransactor::transact_through_sovereign( parachain::RuntimeOrigin::root(), Box::new(xcm::VersionedLocation::V4(dest)), - PARAALICE.into(), + Some(PARAALICE.into()), CurrencyPayment { currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( Location::parent() @@ -1478,6 +1478,111 @@ fn transact_through_sovereign() { }); } +#[test] +fn transact_through_sovereign_fee_payer_none() { + MockNet::reset(); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + None + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + }); + + let derivative_address = derivative_account_id(para_a_account(), 0); + + Relay::execute_with(|| { + // Transfer 100 tokens to derivative_address on the relay + assert_ok!(RelayBalances::transfer_keep_alive( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derivative_address.clone(), + 100u128 + )); + + // Transfer the XCM execution fee amount to ParaA's sovereign account + assert_ok!(RelayBalances::transfer_keep_alive( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_a_account(), + 4000003000u128 + )); + }); + + // Check balances before the transact call + Relay::execute_with(|| { + assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000003000); + assert_eq!(RelayBalances::free_balance(&derivative_address), 100); + assert_eq!(RelayBalances::free_balance(&RELAYBOB), 0); + }); + + // Encode the call. Balances transfer of 100 relay tokens to RELAYBOB + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: RELAYBOB, + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + // The final call will be an AsDerivative using index 0 + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: /* Here */ [].into(), + }; + + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + // No fee_payer here. The sovereign account will pay the fees on destination. + None, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + // Check balances after the transact call are correct + Relay::execute_with(|| { + assert_eq!(RelayBalances::free_balance(¶_a_account()), 0); + assert_eq!(RelayBalances::free_balance(&derivative_address), 0); + assert_eq!(RelayBalances::free_balance(&RELAYBOB), 100); + }); +} + #[test] fn transact_through_sovereign_with_custom_fee_weight() { MockNet::reset(); @@ -1605,7 +1710,7 @@ fn transact_through_sovereign_with_custom_fee_weight() { assert_ok!(XcmTransactor::transact_through_sovereign( parachain::RuntimeOrigin::root(), Box::new(xcm::VersionedLocation::V4(dest)), - PARAALICE.into(), + Some(PARAALICE.into()), CurrencyPayment { currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( Location::parent() @@ -1758,7 +1863,7 @@ fn transact_through_sovereign_with_custom_fee_weight_refund() { assert_ok!(XcmTransactor::transact_through_sovereign( parachain::RuntimeOrigin::root(), Box::new(xcm::VersionedLocation::V4(dest)), - PARAALICE.into(), + Some(PARAALICE.into()), CurrencyPayment { currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( Location::parent() diff --git a/runtime/moonriver/tests/xcm_tests.rs b/runtime/moonriver/tests/xcm_tests.rs index f2ec8718573..0eab574ba0a 100644 --- a/runtime/moonriver/tests/xcm_tests.rs +++ b/runtime/moonriver/tests/xcm_tests.rs @@ -1608,7 +1608,7 @@ fn transact_through_sovereign() { assert_ok!(XcmTransactor::transact_through_sovereign( parachain::RuntimeOrigin::root(), Box::new(xcm::VersionedLocation::V4(dest)), - PARAALICE.into(), + Some(PARAALICE.into()), CurrencyPayment { currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( Location::parent() @@ -1633,6 +1633,111 @@ fn transact_through_sovereign() { }); } +#[test] +fn transact_through_sovereign_fee_payer_none() { + MockNet::reset(); + + ParaA::execute_with(|| { + // Root can set transact info + assert_ok!(XcmTransactor::set_transact_info( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + // Relay charges 1000 for every instruction, and we have 3, so 3000 + 3000.into(), + 20000000000.into(), + None + )); + // Root can set transact info + assert_ok!(XcmTransactor::set_fee_per_second( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(Location::parent())), + WEIGHT_REF_TIME_PER_SECOND as u128, + )); + }); + + let derivative_address = derivative_account_id(para_a_account(), 0); + + Relay::execute_with(|| { + // Transfer 100 tokens to derivative_address on the relay + assert_ok!(RelayBalances::transfer_keep_alive( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + derivative_address.clone(), + 100u128 + )); + + // Transfer the XCM execution fee amount to ParaA's sovereign account + assert_ok!(RelayBalances::transfer_keep_alive( + relay_chain::RuntimeOrigin::signed(RELAYALICE), + para_a_account(), + 4000003000u128 + )); + }); + + // Check balances before the transact call + Relay::execute_with(|| { + assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000003000); + assert_eq!(RelayBalances::free_balance(&derivative_address), 100); + assert_eq!(RelayBalances::free_balance(&RELAYBOB), 0); + }); + + // Encode the call. Balances transfer of 100 relay tokens to RELAYBOB + let mut encoded: Vec = Vec::new(); + let index = ::PalletInfo::index::< + relay_chain::Balances, + >() + .unwrap() as u8; + + encoded.push(index); + + let mut call_bytes = pallet_balances::Call::::transfer_allow_death { + dest: RELAYBOB, + value: 100u32.into(), + } + .encode(); + encoded.append(&mut call_bytes); + + // The final call will be an AsDerivative using index 0 + let utility_bytes = parachain::MockTransactors::Relay.encode_call( + xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), + ); + + // We send the xcm transact operation to parent + let dest = Location { + parents: 1, + interior: /* Here */ [].into(), + }; + + // Root can directly pass the execution byes to the sovereign + ParaA::execute_with(|| { + assert_ok!(XcmTransactor::transact_through_sovereign( + parachain::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::V4(dest)), + // No fee_payer here. The sovereign account will pay the fees on destination. + None, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( + Location::parent() + ))), + fee_amount: None + }, + utility_bytes, + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 4000000000.into(), + overall_weight: None + }, + false + )); + }); + + // Check balances after the transact call are correct + Relay::execute_with(|| { + assert_eq!(RelayBalances::free_balance(¶_a_account()), 0); + assert_eq!(RelayBalances::free_balance(&derivative_address), 0); + assert_eq!(RelayBalances::free_balance(&RELAYBOB), 100); + }); +} + #[test] fn transact_through_sovereign_with_custom_fee_weight() { MockNet::reset(); @@ -1760,7 +1865,7 @@ fn transact_through_sovereign_with_custom_fee_weight() { assert_ok!(XcmTransactor::transact_through_sovereign( parachain::RuntimeOrigin::root(), Box::new(xcm::VersionedLocation::V4(dest)), - PARAALICE.into(), + Some(PARAALICE.into()), CurrencyPayment { currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( Location::parent() @@ -1913,7 +2018,7 @@ fn transact_through_sovereign_with_custom_fee_weight_refund() { assert_ok!(XcmTransactor::transact_through_sovereign( parachain::RuntimeOrigin::root(), Box::new(xcm::VersionedLocation::V4(dest)), - PARAALICE.into(), + Some(PARAALICE.into()), CurrencyPayment { currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4( Location::parent()