diff --git a/Cargo.lock b/Cargo.lock index 42f48ba527e..ec13f70e040 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5963,6 +5963,7 @@ dependencies = [ "serde", "sha3 0.8.2", "sha3 0.9.1", + "smallvec 1.8.0", "sp-api", "sp-block-builder", "sp-core", diff --git a/runtime/moonbase/src/lib.rs b/runtime/moonbase/src/lib.rs index d9e64066fc4..65b9741d6c2 100644 --- a/runtime/moonbase/src/lib.rs +++ b/runtime/moonbase/src/lib.rs @@ -40,7 +40,7 @@ use frame_support::{ traits::{ Contains, Currency as CurrencyT, EnsureOneOf, EqualPrivilegeOnly, Everything, FindAuthor, Get, Imbalance, InstanceFilter, Nothing, OffchainWorker, OnFinalize, OnIdle, OnInitialize, - OnRuntimeUpgrade, OnUnbalanced, PalletInfo as PalletInfoTrait, + OnRuntimeUpgrade, OnUnbalanced, PalletInfoAccess, }, weights::{ constants::{RocksDbWeight, WEIGHT_PER_SECOND}, @@ -1019,19 +1019,17 @@ parameter_types! { parents:1, interior: Junctions::X2( Parachain(ParachainInfo::parachain_id().into()), - PalletInstance( - ::PalletInfo::index::().unwrap() as u8 - ) + PalletInstance(::index() as u8) ) }; - // Bew Self Reserve location, defines the multilocation identifiying the self-reserve currency + // New Self Reserve location, defines the multilocation identifiying the self-reserve currency // This is used to match it also against our Balances pallet when we receive such // a MultiLocation: (Self Balances pallet index) // This is the new anchoring way pub NewAnchoringSelfReserve: MultiLocation = MultiLocation { parents:0, interior: Junctions::X1( - PalletInstance(::PalletInfo::index::().unwrap() as u8) + PalletInstance(::index() as u8) ) }; @@ -1447,11 +1445,11 @@ where { fn convert(currency: CurrencyId) -> Option { match currency { - // For now (and until we upgrade to 0.9.16 is adapted) we need to use the old anchoring - // here - // This is not a problem in either cases, since the view of the destination chain - // does not change - // TODO! change this to NewAnchoringSelfReserve once we uprade to 0.9.16 + // For now and until Xtokens is adapted to handle 0.9.16 version we use + // the old anchoring here + // This is not a problem in either cases, since the view of the destination + // chain does not change + // TODO! change this to NewAnchoringSelfReserve once xtokens is adapted for it CurrencyId::SelfReserve => { let multi: MultiLocation = OldAnchoringSelfReserve::get(); Some(multi) diff --git a/runtime/moonbase/tests/xcm_mock/parachain.rs b/runtime/moonbase/tests/xcm_mock/parachain.rs index ec5f74b2b6e..0d73b782c36 100644 --- a/runtime/moonbase/tests/xcm_mock/parachain.rs +++ b/runtime/moonbase/tests/xcm_mock/parachain.rs @@ -18,7 +18,7 @@ use frame_support::{ construct_runtime, parameter_types, - traits::{Everything, Get, Nothing, PalletInfo as PalletInfoTrait}, + traits::{Everything, Get, Nothing, PalletInfoAccess}, weights::Weight, PalletId, }; @@ -271,21 +271,17 @@ parameter_types! { parents:1, interior: Junctions::X2( Parachain(MsgQueue::parachain_id().into()), - PalletInstance( - ::PalletInfo::index::().unwrap() as u8 - ) + PalletInstance(::index() as u8) ) }; - // Bew Self Reserve location, defines the multilocation identifiying the self-reserve currency + // New Self Reserve location, defines the multilocation identifiying the self-reserve currency // This is used to match it also against our Balances pallet when we receive such // a MultiLocation: (Self Balances pallet index) // This is the new anchoring way pub NewAnchoringSelfReserve: MultiLocation = MultiLocation { parents:0, interior: Junctions::X1( - PalletInstance( - ::PalletInfo::index::().unwrap() as u8 - ) + PalletInstance(::index() as u8) ) }; // The Locations we accept to refer to our own currency. We need to support both pre and @@ -344,11 +340,11 @@ where fn convert(currency: CurrencyId) -> Option { match currency { CurrencyId::SelfReserve => { - // For now (and until we upgrade to 0.9.16 is adapted) we need to use + // For now and until Xtokens is adapted to handle 0.9.16 version we use // the old anchoring here // This is not a problem in either cases, since the view of the destination // chain does not change - // TODO! change this to NewAnchoringSelfReserve once we uprade to 0.9.16 + // TODO! change this to NewAnchoringSelfReserve once xtokens is adapted for it let multi: MultiLocation = OldAnchoringSelfReserve::get(); Some(multi) } diff --git a/runtime/moonbeam/src/lib.rs b/runtime/moonbeam/src/lib.rs index dbd964e4a5c..4696a79b4f2 100644 --- a/runtime/moonbeam/src/lib.rs +++ b/runtime/moonbeam/src/lib.rs @@ -36,7 +36,7 @@ use frame_support::{ traits::{ Contains, EnsureOneOf, EqualPrivilegeOnly, Everything, Get, Imbalance, InstanceFilter, Nothing, OffchainWorker, OnFinalize, OnIdle, OnInitialize, OnRuntimeUpgrade, OnUnbalanced, - PalletInfo as PalletInfoTrait, + PalletInfoAccess, }, weights::{ constants::{RocksDbWeight, WEIGHT_PER_SECOND}, @@ -943,9 +943,7 @@ parameter_types! { parents:1, interior: Junctions::X2( Parachain(ParachainInfo::parachain_id().into()), - PalletInstance( - ::PalletInfo::index::().unwrap() as u8 - ) + PalletInstance(::index() as u8) ) }; } diff --git a/runtime/moonbeam/tests/xcm_mock/parachain.rs b/runtime/moonbeam/tests/xcm_mock/parachain.rs index 604a37a7aae..0411d929c9c 100644 --- a/runtime/moonbeam/tests/xcm_mock/parachain.rs +++ b/runtime/moonbeam/tests/xcm_mock/parachain.rs @@ -18,7 +18,7 @@ use frame_support::{ construct_runtime, parameter_types, - traits::{Everything, Get, Nothing, PalletInfo as PalletInfoTrait}, + traits::{Everything, Get, Nothing, PalletInfoAccess}, weights::Weight, PalletId, }; @@ -244,7 +244,7 @@ parameter_types! { parents:1, interior: Junctions::X2( Parachain(MsgQueue::parachain_id().into()), - PalletInstance(::PalletInfo::index::().unwrap() as u8) + PalletInstance(::index() as u8) ) }; } diff --git a/runtime/moonriver/Cargo.toml b/runtime/moonriver/Cargo.toml index a0268a9c002..0839b1f7327 100644 --- a/runtime/moonriver/Cargo.toml +++ b/runtime/moonriver/Cargo.toml @@ -13,6 +13,7 @@ log = "0.4" rlp = { version = "0.5", optional = true, default-features = false } serde = { version = "1.0.101", optional = true, default-features = false, features = [ "derive" ] } sha3 = { version = "0.8", optional = true, default-features = false } +smallvec = "1.8.0" # Moonbeam account = { path = "../../primitives/account/", default-features = false } diff --git a/runtime/moonriver/src/lib.rs b/runtime/moonriver/src/lib.rs index 901c6a5c721..88bd614f5d9 100644 --- a/runtime/moonriver/src/lib.rs +++ b/runtime/moonriver/src/lib.rs @@ -36,11 +36,12 @@ use frame_support::{ traits::{ Contains, EnsureOneOf, EqualPrivilegeOnly, Everything, Get, Imbalance, InstanceFilter, Nothing, OffchainWorker, OnFinalize, OnIdle, OnInitialize, OnRuntimeUpgrade, OnUnbalanced, - PalletInfo as PalletInfoTrait, + PalletInfoAccess, }, weights::{ constants::{RocksDbWeight, WEIGHT_PER_SECOND}, - DispatchClass, GetDispatchInfo, IdentityFee, Weight, + DispatchClass, GetDispatchInfo, IdentityFee, Weight, WeightToFeeCoefficient, + WeightToFeeCoefficients, WeightToFeePolynomial, }, PalletId, }; @@ -82,16 +83,18 @@ use sp_std::{convert::TryFrom, prelude::*}; use xcm_builder::{ AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, ConvertedConcreteAssetId, EnsureXcmOrigin, FixedWeightBounds, - FungiblesAdapter, LocationInverter, ParentIsDefault, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountKey20AsNative, - SovereignSignedViaLocation, TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, ConvertedConcreteAssetId, + CurrencyAdapter as XcmCurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, + LocationInverter, ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, + TakeWeightCredit, UsingComponents, }; use xcm_executor::traits::JustTry; use xcm::latest::prelude::*; +use smallvec::smallvec; use xcm_primitives::{ AccountIdToCurrencyId, AccountIdToMultiLocation, AsAssetType, FirstAssetTrader, MultiNativeAsset, SignedToAccountId20, UtilityAvailableCalls, UtilityEncodeCall, XcmTransact, @@ -132,6 +135,7 @@ pub mod currency { pub const TRANSACTION_BYTE_FEE: Balance = 10 * MICROMOVR * SUPPLY_FACTOR; pub const STORAGE_BYTE_FEE: Balance = 100 * MICROMOVR * SUPPLY_FACTOR; + pub const WEIGHT_FEE: Balance = 100 * KILOWEI * SUPPLY_FACTOR; pub const fn deposit(items: u32, bytes: u32) -> Balance { items as Balance * 1 * MOVR * SUPPLY_FACTOR + (bytes as Balance) * STORAGE_BYTE_FEE @@ -316,6 +320,28 @@ parameter_types! { pub OperationalFeeMultiplier: u8 = 5; } +pub struct WeightToFee; +impl WeightToFeePolynomial for WeightToFee { + type Balance = Balance; + + /// Return a vec of coefficients. Here we just use one coefficient and reduce it to a constant + /// modifier in order to closely match Ethereum-based fees. + /// + /// Calculation, per the documentation in `frame_support`: + /// + /// ```ignore + /// coeff_integer * x^(degree) + coeff_frac * x^(degree) + /// ``` + fn polynomial() -> WeightToFeeCoefficients { + smallvec![WeightToFeeCoefficient { + degree: 1, + coeff_frac: Perbill::zero(), + coeff_integer: currency::WEIGHT_FEE, + negative: false, + }] + } +} + impl pallet_transaction_payment::Config for Runtime { type OnChargeTransaction = CurrencyAdapter>; type TransactionByteFee = TransactionByteFee; @@ -943,18 +969,34 @@ parameter_types! { pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); // The ancestry, defines the multilocation describing this consensus system pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); - // Self Reserve location, defines the multilocation identifiying the self-reserve currency + // Old Self Reserve location, defines the multilocation identifiying the self-reserve currency // This is used to match it against our Balances pallet when we receive such a MultiLocation // (Parent, Self Para Id, Self Balances pallet index) - pub SelfReserve: MultiLocation = MultiLocation { + // This is the old anchoring way + pub OldAnchoringSelfReserve: MultiLocation = MultiLocation { parents:1, interior: Junctions::X2( Parachain(ParachainInfo::parachain_id().into()), - PalletInstance( - ::PalletInfo::index::().unwrap() as u8 - ) + PalletInstance(::index() as u8) ) }; + // New Self Reserve location, defines the multilocation identifiying the self-reserve currency + // This is used to match it also against our Balances pallet when we receive such + // a MultiLocation: (Self Balances pallet index) + // This is the new anchoring way + pub NewAnchoringSelfReserve: MultiLocation = MultiLocation { + parents:0, + interior: Junctions::X1( + PalletInstance(::index() as u8) + ) + }; + + // The Locations we accept to refer to our own currency. We need to support both pre and + // post 0.9.16 versions, hence the reason for this being a Vec + pub SelfReserveRepresentations: Vec = vec![ + OldAnchoringSelfReserve::get(), + NewAnchoringSelfReserve::get() + ]; } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used @@ -993,10 +1035,24 @@ pub type FungiblesTransactor = FungiblesAdapter< (), >; +pub type LocalAssetTransactor = XcmCurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching any of the locations in + // SelfReserveRepresentations + xcm_primitives::MultiIsConcrete, + // We can convert the MultiLocations with our converter above: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont allow teleport + (), +>; + // We use only fungiblesAdapter transactor for now // The idea is that we only accept the relay token, hence no need to handle the local token // As long as this does not contain the local transactor, we are good -pub type AssetTransactors = FungiblesTransactor; +pub type AssetTransactors = (LocalAssetTransactor, FungiblesTransactor); /// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, /// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can @@ -1073,9 +1129,28 @@ impl xcm_executor::Config for XcmExecutorConfig { type LocationInverter = LocationInverter; type Barrier = XcmBarrier; type Weigher = XcmWeigher; + // We use three traders + // When we receive either representation of the self-reserve asset, + // we use UsingComponents and the local way of handling fees // When we receive a non-reserve asset, we use AssetManager to fetch how many // units per second we should charge - type Trader = FirstAssetTrader; + type Trader = ( + UsingComponents< + WeightToFee, + OldAnchoringSelfReserve, + AccountId, + Balances, + DealWithFees, + >, + UsingComponents< + WeightToFee, + NewAnchoringSelfReserve, + AccountId, + Balances, + DealWithFees, + >, + FirstAssetTrader, + ); type ResponseHandler = PolkadotXcm; type SubscriptionService = PolkadotXcm; type AssetTrap = PolkadotXcm; @@ -1349,8 +1424,13 @@ where { fn convert(currency: CurrencyId) -> Option { match currency { + // For now and until Xtokens is adapted to handle 0.9.16 version we use + // the old anchoring here + // This is not a problem in either cases, since the view of the destination + // chain does not change + // TODO! change this to NewAnchoringSelfReserve once xtokens is adapted for it CurrencyId::SelfReserve => { - let multi: MultiLocation = SelfReserve::get(); + let multi: MultiLocation = OldAnchoringSelfReserve::get(); Some(multi) } CurrencyId::OtherReserve(asset) => AssetXConverter::reverse_ref(asset).ok(), diff --git a/runtime/moonriver/tests/xcm_mock/mod.rs b/runtime/moonriver/tests/xcm_mock/mod.rs index 69191146478..ab83e0b2964 100644 --- a/runtime/moonriver/tests/xcm_mock/mod.rs +++ b/runtime/moonriver/tests/xcm_mock/mod.rs @@ -193,3 +193,4 @@ pub type XTokens = orml_xtokens::Pallet; pub type RelayBalances = pallet_balances::Pallet; pub type ParaBalances = pallet_balances::Pallet; pub type XcmTransactor = xcm_transactor::Pallet; +pub type ParachainPalletXcm = pallet_xcm::Pallet; diff --git a/runtime/moonriver/tests/xcm_mock/parachain.rs b/runtime/moonriver/tests/xcm_mock/parachain.rs index 5449f00904a..f12eea1e8c7 100644 --- a/runtime/moonriver/tests/xcm_mock/parachain.rs +++ b/runtime/moonriver/tests/xcm_mock/parachain.rs @@ -18,7 +18,7 @@ use frame_support::{ construct_runtime, parameter_types, - traits::{Everything, Get, Nothing, PalletInfo as PalletInfoTrait}, + traits::{Everything, Get, Nothing, PalletInfo as PalletInfoTrait, PalletInfoAccess}, weights::Weight, PalletId, }; @@ -43,7 +43,8 @@ use xcm::latest::{ }; use xcm_builder::{ AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, ConvertedConcreteAssetId, EnsureXcmOrigin, FixedWeightBounds, + AllowTopLevelPaidExecutionFrom, ConvertedConcreteAssetId, + CurrencyAdapter as XcmCurrencyAdapter, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FungiblesAdapter, LocationInverter, ParentAsSuperuser, ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, TakeWeightCredit, @@ -197,8 +198,22 @@ pub type FungiblesTransactor = FungiblesAdapter< (), >; +pub type LocalAssetTransactor = XcmCurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching any of the locations in + // SelfReserveRepresentations + xcm_primitives::MultiIsConcrete, + // We can convert the MultiLocations with our converter above: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We dont allow teleport + (), +>; + // These will be our transactors -pub type AssetTransactors = FungiblesTransactor; +pub type AssetTransactors = (LocalAssetTransactor, FungiblesTransactor); pub type XcmRouter = super::ParachainXcmRouter; pub type Barrier = ( @@ -233,20 +248,51 @@ pub type XcmFeesToAccount_ = xcm_primitives::XcmFeesToAccount< parameter_types! { // We cannot skip the native trader for some specific tests, so we will have to work with // a native trader that charges same number of units as weight - pub ParaTokensPerSecond: (XcmAssetId, u128) = (Concrete(SelfReserve::get()), 1000000000000); + // We use both the old and new anchoring logics + pub ParaTokensPerSecondOld: (XcmAssetId, u128) = ( + Concrete(OldAnchoringSelfReserve::get()), + 1000000000000 + ); + pub ParaTokensPerSecondNew: (XcmAssetId, u128) = ( + Concrete(NewAnchoringSelfReserve::get()), + 1000000000000 + ); } parameter_types! { pub const RelayNetwork: NetworkId = NetworkId::Polkadot; pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); pub Ancestry: MultiLocation = Parachain(MsgQueue::parachain_id().into()).into(); - pub SelfReserve: MultiLocation = MultiLocation { + // Old Self Reserve location, defines the multilocation identifiying the self-reserve currency + // This is used to match it against our Balances pallet when we receive such a MultiLocation + // (Parent, Self Para Id, Self Balances pallet index) + pub OldAnchoringSelfReserve: MultiLocation = MultiLocation { parents:1, interior: Junctions::X2( Parachain(MsgQueue::parachain_id().into()), - PalletInstance(::PalletInfo::index::().unwrap() as u8) + PalletInstance( + ::index() as u8 + ) + ) + }; + // New Self Reserve location, defines the multilocation identifiying the self-reserve currency + // This is used to match it also against our Balances pallet when we receive such + // a MultiLocation: (Self Balances pallet index) + // This is the new anchoring way + pub NewAnchoringSelfReserve: MultiLocation = MultiLocation { + parents:0, + interior: Junctions::X1( + PalletInstance( + ::PalletInfo::index::().unwrap() as u8 + ) ) }; + // The Locations we accept to refer to our own currency. We need to support both pre and + // post 0.9.16 versions, hence the reason for this being a Vec + pub SelfReserveRepresentations: Vec = vec![ + OldAnchoringSelfReserve::get(), + NewAnchoringSelfReserve::get() + ]; } pub struct XcmConfig; impl Config for XcmConfig { @@ -259,7 +305,15 @@ impl Config for XcmConfig { type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; - type Trader = (xcm_primitives::FirstAssetTrader,); + // We use three traders + // When we receive either representation of the self-reserve asset, + // When we receive a non-reserve asset, we use AssetManager to fetch how many + // units per second we should charge + type Trader = ( + FixedRateOfFungible, + FixedRateOfFungible, + xcm_primitives::FirstAssetTrader, + ); type ResponseHandler = PolkadotXcm; type SubscriptionService = PolkadotXcm; @@ -289,7 +343,12 @@ where fn convert(currency: CurrencyId) -> Option { match currency { CurrencyId::SelfReserve => { - let multi: MultiLocation = SelfReserve::get(); + // For now and until Xtokens is adapted to handle 0.9.16 version we use + // the old anchoring here + // This is not a problem in either cases, since the view of the destination + // chain does not change + // TODO! change this to NewAnchoringSelfReserve once xtokens is adapted for it + let multi: MultiLocation = OldAnchoringSelfReserve::get(); Some(multi) } CurrencyId::OtherReserve(asset) => AssetXConverter::reverse_ref(asset).ok(), diff --git a/runtime/moonriver/tests/xcm_tests.rs b/runtime/moonriver/tests/xcm_tests.rs index fca958e2928..32c3deacb91 100644 --- a/runtime/moonriver/tests/xcm_tests.rs +++ b/runtime/moonriver/tests/xcm_tests.rs @@ -18,23 +18,17 @@ mod xcm_mock; use frame_support::{ - assert_noop, assert_ok, + assert_ok, traits::{PalletInfo, PalletInfoAccess}, weights::constants::WEIGHT_PER_SECOND, }; +use xcm::latest::prelude::*; use xcm::{VersionedMultiLocation, WrapVersion}; +use xcm_executor::traits::Convert; use xcm_mock::parachain; use xcm_mock::relay_chain; use xcm_mock::*; use xcm_primitives::UtilityEncodeCall; - -use xcm::latest::prelude::QueryResponse; -use xcm::latest::{ - Junction::{self, AccountId32, AccountKey20, PalletInstance, Parachain}, - Junctions::*, - MultiLocation, NetworkId, Response, Xcm, -}; -use xcm_executor::traits::Convert; use xcm_simulator::TestExt; // Send a relay asset (like DOT) to a parachain A @@ -311,28 +305,25 @@ fn send_para_a_asset_to_para_b() { ParaA::execute_with(|| { // free execution, full amount received - assert_noop!( - XTokens::transfer( - parachain::Origin::signed(PARAALICE.into()), - parachain::CurrencyId::SelfReserve, - 100, - Box::new(VersionedMultiLocation::V1(dest)), - 800000 - ), - orml_xtokens::Error::::XcmExecutionFailed - ); + assert_ok!(XTokens::transfer( + parachain::Origin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedMultiLocation::V1(dest)), + 800000 + )); }); ParaA::execute_with(|| { // free execution, full amount received assert_eq!( ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE + INITIAL_BALANCE - 100 ); }); ParaB::execute_with(|| { // free execution, full amount received - assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 0); + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); }); } @@ -391,30 +382,26 @@ fn send_para_a_asset_from_para_b_to_para_c() { ), }; ParaA::execute_with(|| { - // We cannot transfer Self Reserve - assert_noop!( - XTokens::transfer( - parachain::Origin::signed(PARAALICE.into()), - parachain::CurrencyId::SelfReserve, - 100, - Box::new(VersionedMultiLocation::V1(dest)), - 80 - ), - orml_xtokens::Error::::XcmExecutionFailed - ); + assert_ok!(XTokens::transfer( + parachain::Origin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedMultiLocation::V1(dest)), + 80 + )); }); ParaA::execute_with(|| { - // Nothing deducted + // free execution, full amount received assert_eq!( ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE + INITIAL_BALANCE - 100 ); }); ParaB::execute_with(|| { - // Nothing transferred - assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 0); + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); }); let dest = MultiLocation { @@ -429,22 +416,19 @@ fn send_para_a_asset_from_para_b_to_para_c() { }; ParaB::execute_with(|| { - // first transfer did not go through, so this fails - assert_noop!( - XTokens::transfer( - parachain::Origin::signed(PARAALICE.into()), - parachain::CurrencyId::OtherReserve(source_id), - 100, - Box::new(VersionedMultiLocation::V1(dest)), - 80 - ), - orml_xtokens::Error::::XcmExecutionFailed - ); + assert_ok!(XTokens::transfer( + parachain::Origin::signed(PARAALICE.into()), + parachain::CurrencyId::OtherReserve(source_id), + 100, + Box::new(VersionedMultiLocation::V1(dest)), + 80 + )); }); - // Para C never receives tokens + // The message passed through parachainA so we needed to pay since its the native token ParaC::execute_with(|| { - assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 0); + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 96); }); } @@ -488,30 +472,26 @@ fn send_para_a_asset_to_para_b_and_back_to_para_a() { ), }; ParaA::execute_with(|| { - // We cannot transfer Self Reserve - assert_noop!( - XTokens::transfer( - parachain::Origin::signed(PARAALICE.into()), - parachain::CurrencyId::SelfReserve, - 100, - Box::new(VersionedMultiLocation::V1(dest)), - 80 - ), - orml_xtokens::Error::::XcmExecutionFailed - ); + assert_ok!(XTokens::transfer( + parachain::Origin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedMultiLocation::V1(dest)), + 80 + )); }); ParaA::execute_with(|| { - // Nothing deducted + // free execution, full amount received assert_eq!( ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE + INITIAL_BALANCE - 100 ); }); ParaB::execute_with(|| { - // Nothing received - assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 0); + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); }); let dest = MultiLocation { @@ -525,24 +505,132 @@ fn send_para_a_asset_to_para_b_and_back_to_para_a() { ), }; ParaB::execute_with(|| { - // The token was not received - assert_noop!( - XTokens::transfer( - parachain::Origin::signed(PARAALICE.into()), - parachain::CurrencyId::OtherReserve(source_id), - 100, - Box::new(VersionedMultiLocation::V1(dest)), - 80 - ), - orml_xtokens::Error::::XcmExecutionFailed + assert_ok!(XTokens::transfer( + parachain::Origin::signed(PARAALICE.into()), + parachain::CurrencyId::OtherReserve(source_id), + 100, + Box::new(VersionedMultiLocation::V1(dest)), + 80 + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + // Weight used is 4 + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 4 + ); + }); +} + +#[test] +fn send_para_a_asset_to_para_b_and_back_to_para_a_with_new_reanchoring() { + MockNet::reset(); + + let para_a_balances = MultiLocation::new(1, X2(Parachain(1), PalletInstance(1u8))); + let source_location = parachain::AssetType::Xcm(para_a_balances); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_asset( + parachain::Origin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::Origin::root(), + source_location, + 0u128 + )); + }); + + let dest = MultiLocation { + parents: 1, + interior: X2( + Parachain(2), + AccountKey20 { + network: NetworkId::Any, + key: PARAALICE.into(), + }, + ), + }; + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::Origin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedMultiLocation::V1(dest)), + 80 + )); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 ); }); + ParaB::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); + + // This time we will force the new reanchoring by manually sending the + // Message through polkadotXCM pallet + + let dest = MultiLocation { + parents: 1, + interior: X1(Parachain(1)), + }; + + let reanchored_para_a_balances = MultiLocation::new(0, X1(PalletInstance(1u8))); + + let message = xcm::VersionedXcm::<()>::V2(Xcm(vec![ + WithdrawAsset((reanchored_para_a_balances.clone(), 100).into()), + ClearOrigin, + BuyExecution { + fees: (reanchored_para_a_balances, 100).into(), + weight_limit: Limited(80), + }, + DepositAsset { + assets: All.into(), + max_assets: 1, + beneficiary: MultiLocation::new( + 0, + X1(AccountKey20 { + network: Any, + key: PARAALICE, + }), + ), + }, + ])); + ParaB::execute_with(|| { + // Send a message to the sovereign account in ParaA to withdraw + // and deposit asset + assert_ok!(ParachainPalletXcm::send( + parachain::Origin::root(), + Box::new(dest.into()), + Box::new(message), + )); + }); + ParaA::execute_with(|| { - // Nothing sent + // Weight used is 4 assert_eq!( ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE + INITIAL_BALANCE - 4 ); }); } @@ -607,6 +695,146 @@ fn receive_relay_asset_with_trader() { }); } +#[test] +fn send_para_a_asset_to_para_b_with_trader() { + MockNet::reset(); + + let para_a_balances = MultiLocation::new(1, X2(Parachain(1), PalletInstance(1u8))); + let source_location = parachain::AssetType::Xcm(para_a_balances); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_asset( + parachain::Origin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::Origin::root(), + source_location, + 2500000000000u128 + )); + }); + + let dest = MultiLocation { + parents: 1, + interior: X2( + Parachain(2), + AccountKey20 { + network: NetworkId::Any, + key: PARAALICE.into(), + }, + ), + }; + + // In destination chain, we only need 4 weight + // We put 10 weight, 6 of which should be refunded and 4 of which should go to treasury + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer( + parachain::Origin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedMultiLocation::V1(dest)), + 10 + )); + }); + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + // We are sending 100 tokens from para A. + // Amount spent in fees is Units per second * weight / 1_000_000_000_000 (weight per second) + // weight is 4 since we are executing 4 instructions with a unitweightcost of 1. + // Units per second should be 2_500_000_000_000_000 + // Since we set 10 weight in destination chain, 25 will be charged upfront + // 15 of those will be refunded, while 10 will go to treasury as the true weight used + // will be 4 + ParaB::execute_with(|| { + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 90); + // Fee should have been received by treasury + assert_eq!(Assets::balance(source_id, &Treasury::account_id()), 10); + }); +} + +#[test] +fn send_para_a_asset_to_para_b_with_trader_and_fee() { + MockNet::reset(); + + let para_a_balances = MultiLocation::new(1, X2(Parachain(1), PalletInstance(1u8))); + let source_location = parachain::AssetType::Xcm(para_a_balances); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + + ParaB::execute_with(|| { + assert_ok!(AssetManager::register_asset( + parachain::Origin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + // With these units per second, 80K weight convrets to 1 asset unit + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::Origin::root(), + source_location, + 12500000u128 + )); + }); + + let dest = MultiLocation { + parents: 1, + interior: X2( + Parachain(2), + AccountKey20 { + network: NetworkId::Any, + key: PARAALICE.into(), + }, + ), + }; + + // we use transfer_with_fee + ParaA::execute_with(|| { + assert_ok!(XTokens::transfer_with_fee( + parachain::Origin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + 1, + Box::new(VersionedMultiLocation::V1(dest)), + 800000 + )); + }); + ParaA::execute_with(|| { + // 100 tokens transferred plus 1 taken from fees + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 - 1 + ); + }); + + ParaB::execute_with(|| { + // free execution, full amount received because trully the xcm instruction does not cost + // what it is specified + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 101); + }); +} + #[test] fn error_when_not_paying_enough() { MockNet::reset(); @@ -1082,6 +1310,156 @@ fn test_automatic_versioning_on_runtime_upgrade_with_relay() { }); } +#[test] +fn test_automatic_versioning_on_runtime_upgrade_with_para_b() { + MockNet::reset(); + + let para_a_balances = MultiLocation::new(1, X2(Parachain(1), PalletInstance(1u8))); + let source_location = parachain::AssetType::Xcm(para_a_balances); + let source_id: parachain::AssetId = source_location.clone().into(); + + let asset_metadata = parachain::AssetMetadata { + name: b"ParaAToken".to_vec(), + symbol: b"ParaA".to_vec(), + decimals: 18, + }; + let response = Response::Version(2); + + // This is irrelevant, nothing will be done with this message, + // but we need to pass a message as an argument to trigger the storage change + let mock_message: Xcm<()> = Xcm(vec![QueryResponse { + query_id: 0, + response, + max_weight: 0, + }]); + + ParaA::execute_with(|| { + // advertised version + parachain::XcmVersioner::set_version(2); + }); + + ParaB::execute_with(|| { + // Let's try with v0 + parachain::XcmVersioner::set_version(0); + + assert_ok!(AssetManager::register_asset( + parachain::Origin::root(), + source_location.clone(), + asset_metadata, + 1u128, + true + )); + assert_ok!(AssetManager::set_asset_units_per_second( + parachain::Origin::root(), + source_location, + 0u128 + )); + }); + + ParaA::execute_with(|| { + // This sets the default version, for not known destinations + assert_ok!(ParachainPalletXcm::force_default_xcm_version( + parachain::Origin::root(), + Some(2) + )); + // Wrap version, which sets VersionedStorage + assert_ok!(::wrap_version( + &MultiLocation::new(1, X1(Parachain(2))).into(), + mock_message + )); + + parachain::para_roll_to(2); + + // queries should have been updated + assert!(ParachainPalletXcm::query(0).is_some()); + }); + + let expected_supported_version: parachain::Event = pallet_xcm::Event::SupportedVersionChanged( + MultiLocation { + parents: 1, + interior: X1(Parachain(2)), + }, + 0, + ) + .into(); + + ParaA::execute_with(|| { + // Assert that the events vector contains the version change + assert!(parachain::para_events().contains(&expected_supported_version)); + }); + + // Let's ensure talking in v0 works + let dest = MultiLocation { + parents: 1, + interior: X2( + Parachain(2), + AccountKey20 { + network: NetworkId::Any, + key: PARAALICE.into(), + }, + ), + }; + ParaA::execute_with(|| { + // free execution, full amount received + assert_ok!(XTokens::transfer( + parachain::Origin::signed(PARAALICE.into()), + parachain::CurrencyId::SelfReserve, + 100, + Box::new(VersionedMultiLocation::V1(dest)), + 80 + )); + // free execution, full amount received + assert_eq!( + ParaBalances::free_balance(&PARAALICE.into()), + INITIAL_BALANCE - 100 + ); + }); + + ParaB::execute_with(|| { + // free execution, full amount received + assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100); + }); + + let expected_version_notified: parachain::Event = pallet_xcm::Event::VersionChangeNotified( + MultiLocation { + parents: 1, + interior: X1(Parachain(1)), + }, + 2, + ) + .into(); + + // ParaB changes version to 2, and calls on_runtime_upgrade. This should notify the targets + // of the new version change + ParaB::execute_with(|| { + // Set version + parachain::XcmVersioner::set_version(2); + // Do runtime upgrade + parachain::on_runtime_upgrade(); + // Initialize block, to call on_initialize and notify targets + parachain::para_roll_to(2); + // Expect the event in the parachain + assert!(parachain::para_events().contains(&expected_version_notified)); + }); + + // This event should have been seen in para A + let expected_supported_version_2: parachain::Event = + pallet_xcm::Event::SupportedVersionChanged( + MultiLocation { + parents: 1, + interior: X1(Parachain(2)), + }, + 2, + ) + .into(); + + // Para A should have received the version change + ParaA::execute_with(|| { + // Assert that the events vector contains the new version change + assert!(parachain::para_events().contains(&expected_supported_version_2)); + }); +} + #[test] fn receive_asset_with_no_sufficients_not_possible_if_non_existent_account() { MockNet::reset();