Asset conversion nits (#2860)

* [asset-conv] Unused Balances type

* [asset-conv] native asset change

* Dedicated `AssetBalance` type for `pallet_assets` instances

* Improved local vs foreign asset handling + test for not allowing pool_assets in pool

* Removed `into_multiasset_id`

* Fix

* Refactor

* Fixed create_pool for benchmark with LocalAndForeignAssets (finally found it)

* ".git/.scripts/commands/fmt/fmt.sh"

* Revert

* fmt

* Migrates pools with `MultiLocation { parents: 0, interior: Here }` to `MultiLocation { parents: 1, interior: Here }`

* Allow `set_storage` for `AllowMultiAssetPools` / `LiquidityWithdrawalFee`

* Benchmarks work

* Removed comment + more defensive migration

* `T::Currency::transfer` -> `Balances::transfer_all` in migration

* Change pool_id in migration

* Update parachains/runtimes/assets/asset-hub-westend/src/lib.rs

* Bump substrate (manually)

* Bump polkadot

* Fixes from polkadot + clippy

* Fix for xcm-emulator tests (thank you Nacho)

---------

Co-authored-by: command-bot <>
Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com>
This commit is contained in:
Branislav Kontur
2023-07-21 23:53:51 +02:00
committed by GitHub
parent 849964bf18
commit 8171890f9b
25 changed files with 1060 additions and 486 deletions
+266 -263
View File
File diff suppressed because it is too large Load Diff
@@ -50,7 +50,7 @@ fn reserve_transfer_native_asset_from_relay_to_assets() {
Kusama, Kusama,
vec![ vec![
RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { outcome: Outcome::Complete(weight) }) => { RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { outcome: Outcome::Complete(weight) }) => {
weight: weight_within_threshold((REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD), Weight::from_parts(754_244_000, 0), *weight), weight: weight_within_threshold((REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD), Weight::from_parts(630_092_000, 6196), *weight),
}, },
] ]
); );
@@ -50,7 +50,7 @@ fn reserve_transfer_native_asset_from_relay_to_assets() {
Polkadot, Polkadot,
vec![ vec![
RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { outcome: Outcome::Complete(weight) }) => { RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { outcome: Outcome::Complete(weight) }) => {
weight: weight_within_threshold((REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD), Weight::from_parts(2_000_000_000, 0), *weight), weight: weight_within_threshold((REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD), Weight::from_parts(686_043_000, 6196), *weight),
}, },
] ]
); );
@@ -7,6 +7,7 @@ description = "Asset Hub Westend runtime integration tests with xcm-emulator"
[dependencies] [dependencies]
codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false } codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false }
assert_matches = "1.5.0"
# Substrate # Substrate
sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
@@ -50,7 +50,7 @@ fn reserve_transfer_native_asset_from_relay_to_assets() {
Westend, Westend,
vec![ vec![
RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { outcome: Outcome::Complete(weight) }) => { RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { outcome: Outcome::Complete(weight) }) => {
weight: weight_within_threshold((REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD), Weight::from_parts(731_495_000, 0), *weight), weight: weight_within_threshold((REF_TIME_THRESHOLD, PROOF_SIZE_THRESHOLD), Weight::from_parts(629_384_000, 6196), *weight),
}, },
] ]
); );
@@ -1,12 +1,13 @@
use crate::*; use crate::*;
use frame_support::{instances::Instance2, BoundedVec}; use frame_support::{instances::Instance2, BoundedVec};
use sp_runtime::{DispatchError, ModuleError};
use xcm_emulator::Parachain; use xcm_emulator::Parachain;
#[test] #[test]
fn swap_locally_on_chain_using_local_assets() { fn swap_locally_on_chain_using_local_assets() {
const ASSET_ID: u32 = 1; const ASSET_ID: u32 = 1;
let asset_native = Box::new(MultiLocation { parents: 0, interior: Here }); let asset_native = Box::new(asset_hub_westend_runtime::xcm_config::WestendLocation::get());
let asset_one = Box::new(MultiLocation { let asset_one = Box::new(MultiLocation {
parents: 0, parents: 0,
interior: X2(PalletInstance(50), GeneralIndex(ASSET_ID.into())), interior: X2(PalletInstance(50), GeneralIndex(ASSET_ID.into())),
@@ -99,7 +100,7 @@ fn swap_locally_on_chain_using_foreign_assets() {
use frame_support::weights::WeightToFee; use frame_support::weights::WeightToFee;
const ASSET_ID: u32 = 1; const ASSET_ID: u32 = 1;
let asset_native = Box::new(MultiLocation { parents: 0, interior: Here }); let asset_native = Box::new(asset_hub_westend_runtime::xcm_config::WestendLocation::get());
let foreign_asset1_at_asset_hub_westend = Box::new(MultiLocation { let foreign_asset1_at_asset_hub_westend = Box::new(MultiLocation {
parents: 1, parents: 1,
@@ -305,3 +306,40 @@ fn swap_locally_on_chain_using_foreign_assets() {
)); ));
}); });
} }
#[test]
fn cannot_create_pool_from_pool_assets() {
const ASSET_ID: u32 = 1;
let asset_native = Box::new(asset_hub_westend_runtime::xcm_config::WestendLocation::get());
let mut asset_one = asset_hub_westend_runtime::xcm_config::PoolAssetsPalletLocation::get();
asset_one.append_with(GeneralIndex(ASSET_ID.into())).expect("pool assets");
AssetHubWestend::execute_with(|| {
let pool_owner_account_id = asset_hub_westend_runtime::AssetConversionOrigin::get();
assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::PoolAssets::create(
<AssetHubWestend as Parachain>::RuntimeOrigin::signed(pool_owner_account_id.clone()),
ASSET_ID.into(),
pool_owner_account_id.clone().into(),
1000,
));
assert!(<AssetHubWestend as AssetHubWestendPallet>::PoolAssets::asset_exists(ASSET_ID));
assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::PoolAssets::mint(
<AssetHubWestend as Parachain>::RuntimeOrigin::signed(pool_owner_account_id),
ASSET_ID.into(),
AssetHubWestendSender::get().into(),
3_000_000_000_000,
));
assert_matches::assert_matches!(
<AssetHubWestend as AssetHubWestendPallet>::AssetConversion::create_pool(
<AssetHubWestend as Parachain>::RuntimeOrigin::signed(AssetHubWestendSender::get()),
asset_native.clone(),
Box::new(asset_one),
),
Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("UnsupportedAsset"))
);
});
}
@@ -289,6 +289,7 @@ decl_test_parachains! {
PolkadotXcm: asset_hub_westend_runtime::PolkadotXcm, PolkadotXcm: asset_hub_westend_runtime::PolkadotXcm,
Assets: asset_hub_westend_runtime::Assets, Assets: asset_hub_westend_runtime::Assets,
ForeignAssets: asset_hub_westend_runtime::ForeignAssets, ForeignAssets: asset_hub_westend_runtime::ForeignAssets,
PoolAssets: asset_hub_westend_runtime::PoolAssets,
AssetConversion: asset_hub_westend_runtime::AssetConversion, AssetConversion: asset_hub_westend_runtime::AssetConversion,
} }
}, },
@@ -1139,7 +1139,7 @@ impl_runtime_apis! {
MultiAsset { fun: Fungible(UNITS), id: Concrete(KsmLocation::get()) }, MultiAsset { fun: Fungible(UNITS), id: Concrete(KsmLocation::get()) },
)); ));
pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None; pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None;
pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = None;
} }
impl pallet_xcm_benchmarks::fungible::Config for Runtime { impl pallet_xcm_benchmarks::fungible::Config for Runtime {
@@ -1147,6 +1147,7 @@ impl_runtime_apis! {
type CheckedAccount = CheckedAccount; type CheckedAccount = CheckedAccount;
type TrustedTeleporter = TrustedTeleporter; type TrustedTeleporter = TrustedTeleporter;
type TrustedReserve = TrustedReserve;
fn get_multi_asset() -> MultiAsset { fn get_multi_asset() -> MultiAsset {
MultiAsset { MultiAsset {
@@ -1121,6 +1121,7 @@ impl_runtime_apis! {
MultiAsset { fun: Fungible(UNITS), id: Concrete(DotLocation::get()) }, MultiAsset { fun: Fungible(UNITS), id: Concrete(DotLocation::get()) },
)); ));
pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None; pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None;
pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = None;
} }
impl pallet_xcm_benchmarks::fungible::Config for Runtime { impl pallet_xcm_benchmarks::fungible::Config for Runtime {
@@ -1128,6 +1129,7 @@ impl_runtime_apis! {
type CheckedAccount = CheckedAccount; type CheckedAccount = CheckedAccount;
type TrustedTeleporter = TrustedTeleporter; type TrustedTeleporter = TrustedTeleporter;
type TrustedReserve = TrustedReserve;
fn get_multi_asset() -> MultiAsset { fn get_multi_asset() -> MultiAsset {
MultiAsset { MultiAsset {
@@ -28,8 +28,13 @@ pub mod constants;
mod weights; mod weights;
pub mod xcm_config; pub mod xcm_config;
use crate::xcm_config::{TrustBackedAssetsPalletLocation, UniversalLocation}; use crate::xcm_config::{
use assets_common::local_and_foreign_assets::{LocalAndForeignAssets, MultiLocationConverter}; LocalAndForeignAssetsMultiLocationMatcher, TrustBackedAssetsPalletLocation,
};
use assets_common::{
local_and_foreign_assets::{LocalAndForeignAssets, MultiLocationConverter},
AssetIdForTrustBackedAssetsConvert,
};
use codec::{Decode, Encode, MaxEncodedLen}; use codec::{Decode, Encode, MaxEncodedLen};
use constants::{currency::*, fee::WeightToFee}; use constants::{currency::*, fee::WeightToFee};
use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases;
@@ -300,9 +305,13 @@ impl pallet_asset_conversion::Config for Runtime {
type Balance = Balance; type Balance = Balance;
type HigherPrecisionBalance = sp_core::U256; type HigherPrecisionBalance = sp_core::U256;
type Currency = Balances; type Currency = Balances;
type AssetBalance = <Self as pallet_balances::Config>::Balance; type AssetBalance = Balance;
type AssetId = MultiLocation; type AssetId = MultiLocation;
type Assets = LocalAndForeignAssets<Assets, ForeignAssets, TrustBackedAssetsPalletLocation>; type Assets = LocalAndForeignAssets<
Assets,
AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation>,
ForeignAssets,
>;
type PoolAssets = PoolAssets; type PoolAssets = PoolAssets;
type PoolAssetId = u32; type PoolAssetId = u32;
type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam
@@ -314,11 +323,12 @@ impl pallet_asset_conversion::Config for Runtime {
type MaxSwapPathLength = ConstU32<4>; type MaxSwapPathLength = ConstU32<4>;
type MultiAssetId = Box<MultiLocation>; type MultiAssetId = Box<MultiLocation>;
type MultiAssetIdConverter = MultiLocationConverter<Balances, UniversalLocation>; type MultiAssetIdConverter =
MultiLocationConverter<WestendLocation, LocalAndForeignAssetsMultiLocationMatcher>;
type MintMinLiquidity = ConstU128<100>; type MintMinLiquidity = ConstU128<100>;
type WeightInfo = (); type WeightInfo = weights::pallet_asset_conversion::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")] #[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = type BenchmarkHelper =
@@ -659,7 +669,11 @@ impl pallet_collator_selection::Config for Runtime {
impl pallet_asset_conversion_tx_payment::Config for Runtime { impl pallet_asset_conversion_tx_payment::Config for Runtime {
type RuntimeEvent = RuntimeEvent; type RuntimeEvent = RuntimeEvent;
type Fungibles = LocalAndForeignAssets<Assets, ForeignAssets, TrustBackedAssetsPalletLocation>; type Fungibles = LocalAndForeignAssets<
Assets,
AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation>,
ForeignAssets,
>;
type OnChargeAssetTransaction = AssetConversionAdapter<Balances, AssetConversion>; type OnChargeAssetTransaction = AssetConversionAdapter<Balances, AssetConversion>;
} }
@@ -835,6 +849,8 @@ pub type Migrations = (
pallet_nfts::migration::v1::MigrateToV1<Runtime>, pallet_nfts::migration::v1::MigrateToV1<Runtime>,
// unreleased // unreleased
pallet_collator_selection::migration::v1::MigrateToV1<Runtime>, pallet_collator_selection::migration::v1::MigrateToV1<Runtime>,
// unreleased
migrations::NativeAssetParents0ToParents1Migration<Runtime>,
); );
/// Executive: handles dispatch to the various modules. /// Executive: handles dispatch to the various modules.
@@ -1240,7 +1256,7 @@ impl_runtime_apis! {
MultiAsset { fun: Fungible(UNITS), id: Concrete(WestendLocation::get()) }, MultiAsset { fun: Fungible(UNITS), id: Concrete(WestendLocation::get()) },
)); ));
pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None; pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None;
pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = None;
} }
impl pallet_xcm_benchmarks::fungible::Config for Runtime { impl pallet_xcm_benchmarks::fungible::Config for Runtime {
@@ -1248,6 +1264,7 @@ impl_runtime_apis! {
type CheckedAccount = CheckedAccount; type CheckedAccount = CheckedAccount;
type TrustedTeleporter = TrustedTeleporter; type TrustedTeleporter = TrustedTeleporter;
type TrustedReserve = TrustedReserve;
fn get_multi_asset() -> MultiAsset { fn get_multi_asset() -> MultiAsset {
MultiAsset { MultiAsset {
@@ -1360,3 +1377,120 @@ cumulus_pallet_parachain_system::register_validate_block! {
BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>, BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,
CheckInherents = CheckInherents, CheckInherents = CheckInherents,
} }
pub mod migrations {
use super::*;
use frame_support::{
pallet_prelude::Get,
traits::{
fungibles::{Inspect, Mutate},
tokens::Preservation,
OnRuntimeUpgrade, OriginTrait,
},
};
use parachains_common::impls::AccountIdOf;
use sp_runtime::{traits::StaticLookup, Saturating};
use xcm::latest::prelude::*;
/// Temporary migration because of bug with native asset, it can be removed once applied on `AssetHubWestend`.
/// Migrates pools with `MultiLocation { parents: 0, interior: Here }` to `MultiLocation { parents: 1, interior: Here }`
pub struct NativeAssetParents0ToParents1Migration<T>(sp_std::marker::PhantomData<T>);
impl<
T: pallet_asset_conversion::Config<
MultiAssetId = Box<MultiLocation>,
AssetId = MultiLocation,
>,
> OnRuntimeUpgrade for NativeAssetParents0ToParents1Migration<T>
where
<T as pallet_asset_conversion::Config>::PoolAssetId: Into<u32>,
AccountIdOf<Runtime>: Into<[u8; 32]>,
<T as frame_system::Config>::AccountId:
Into<<<T as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
<<T as frame_system::Config>::Lookup as StaticLookup>::Source:
From<<T as frame_system::Config>::AccountId>,
sp_runtime::AccountId32: From<<T as frame_system::Config>::AccountId>,
{
fn on_runtime_upgrade() -> Weight {
let invalid_native_asset = MultiLocation { parents: 0, interior: Here };
let valid_native_asset = WestendLocation::get();
let mut reads: u64 = 1;
let mut writes: u64 = 0;
// migrate pools with invalid native asset
let pools = pallet_asset_conversion::Pools::<T>::iter().collect::<Vec<_>>();
reads.saturating_accrue(1);
for (old_pool_id, pool_info) in pools {
let old_pool_account =
pallet_asset_conversion::Pallet::<T>::get_pool_account(&old_pool_id);
reads.saturating_accrue(1);
let pool_asset_id = pool_info.lp_token.clone();
if old_pool_id.0.as_ref() != &invalid_native_asset {
// skip, if ok
continue
}
// fix new account
let new_pool_id = pallet_asset_conversion::Pallet::<T>::get_pool_id(
Box::new(valid_native_asset),
old_pool_id.1.clone(),
);
let new_pool_account =
pallet_asset_conversion::Pallet::<T>::get_pool_account(&new_pool_id);
frame_system::Pallet::<T>::inc_providers(&new_pool_account);
reads.saturating_accrue(2);
writes.saturating_accrue(1);
// move currency
let _ = Balances::transfer_all(
RuntimeOrigin::signed(sp_runtime::AccountId32::from(old_pool_account.clone())),
sp_runtime::AccountId32::from(new_pool_account.clone()).into(),
false,
);
reads.saturating_accrue(2);
writes.saturating_accrue(2);
// move LP token
let _ = T::PoolAssets::transfer(
pool_asset_id.clone(),
&old_pool_account,
&new_pool_account,
T::PoolAssets::balance(pool_asset_id.clone(), &old_pool_account),
Preservation::Expendable,
);
reads.saturating_accrue(1);
writes.saturating_accrue(2);
// change the ownership of LP token
let _ = pallet_assets::Pallet::<Runtime, PoolAssetsInstance>::transfer_ownership(
RuntimeOrigin::signed(sp_runtime::AccountId32::from(old_pool_account.clone())),
pool_asset_id.into(),
sp_runtime::AccountId32::from(new_pool_account.clone()).into(),
);
reads.saturating_accrue(1);
writes.saturating_accrue(2);
// move LocalOrForeignAssets
let _ = T::Assets::transfer(
*old_pool_id.1.as_ref(),
&old_pool_account,
&new_pool_account,
T::Assets::balance(*old_pool_id.1.as_ref(), &old_pool_account),
Preservation::Expendable,
);
reads.saturating_accrue(1);
writes.saturating_accrue(2);
// dec providers for old account
let _ = frame_system::Pallet::<T>::dec_providers(&old_pool_account);
writes.saturating_accrue(1);
// change pool key
pallet_asset_conversion::Pools::<T>::insert(new_pool_id, pool_info);
pallet_asset_conversion::Pools::<T>::remove(old_pool_id);
}
T::DbWeight::get().reads_writes(reads, writes)
}
}
}
@@ -2,6 +2,7 @@ pub mod block_weights;
pub mod cumulus_pallet_xcmp_queue; pub mod cumulus_pallet_xcmp_queue;
pub mod extrinsic_weights; pub mod extrinsic_weights;
pub mod frame_system; pub mod frame_system;
pub mod pallet_asset_conversion;
pub mod pallet_assets_foreign; pub mod pallet_assets_foreign;
pub mod pallet_assets_local; pub mod pallet_assets_local;
pub mod pallet_assets_pool; pub mod pallet_assets_pool;
@@ -0,0 +1,157 @@
// Copyright Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Cumulus is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Cumulus is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
//! Autogenerated weights for `pallet_asset_conversion`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-07-18, STEPS: `2`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz`
//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024
// Executed Command:
// ./target/release/polkadot-parachain
// benchmark
// pallet
// --chain=asset-hub-westend-dev
// --wasm-execution=compiled
// --pallet=pallet_asset_conversion
// --no-storage-info
// --no-median-slopes
// --no-min-squares
// --extrinsic=*
// --steps=2
// --repeat=1
// --json
// --header=./file_header.txt
// --output=./parachains/runtimes/assets/asset-hub-westend/src/weights/
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]
use frame_support::{traits::Get, weights::Weight};
use core::marker::PhantomData;
/// Weight functions for `pallet_asset_conversion`.
pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> pallet_asset_conversion::WeightInfo for WeightInfo<T> {
/// Storage: `AssetConversion::Pools` (r:1 w:1)
/// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`)
/// Storage: UNKNOWN KEY `0x76a2c49709deec21d9c05f96c1f47351` (r:1 w:0)
/// Proof: UNKNOWN KEY `0x76a2c49709deec21d9c05f96c1f47351` (r:1 w:0)
/// Storage: `System::Account` (r:2 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `ForeignAssets::Account` (r:1 w:1)
/// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`)
/// Storage: `ForeignAssets::Asset` (r:1 w:1)
/// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`)
/// Storage: `AssetConversion::NextPoolAssetId` (r:1 w:1)
/// Proof: `AssetConversion::NextPoolAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
/// Storage: `PoolAssets::Asset` (r:1 w:1)
/// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `PoolAssets::Account` (r:1 w:1)
/// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
fn create_pool() -> Weight {
// Proof Size summary in bytes:
// Measured: `480`
// Estimated: `6196`
// Minimum execution time: 115_870_000 picoseconds.
Weight::from_parts(115_870_000, 0)
.saturating_add(Weight::from_parts(0, 6196))
.saturating_add(T::DbWeight::get().reads(9))
.saturating_add(T::DbWeight::get().writes(7))
}
/// Storage: `AssetConversion::Pools` (r:1 w:0)
/// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`)
/// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `ForeignAssets::Asset` (r:1 w:1)
/// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`)
/// Storage: `ForeignAssets::Account` (r:2 w:2)
/// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`)
/// Storage: `PoolAssets::Asset` (r:1 w:1)
/// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: `PoolAssets::Account` (r:2 w:2)
/// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
fn add_liquidity() -> Weight {
// Proof Size summary in bytes:
// Measured: `1117`
// Estimated: `7404`
// Minimum execution time: 183_835_000 picoseconds.
Weight::from_parts(183_835_000, 0)
.saturating_add(Weight::from_parts(0, 7404))
.saturating_add(T::DbWeight::get().reads(8))
.saturating_add(T::DbWeight::get().writes(7))
}
/// Storage: `AssetConversion::Pools` (r:1 w:0)
/// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`)
/// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `ForeignAssets::Asset` (r:1 w:1)
/// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`)
/// Storage: `ForeignAssets::Account` (r:2 w:2)
/// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`)
/// Storage: `PoolAssets::Asset` (r:1 w:1)
/// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
/// Storage: UNKNOWN KEY `0x2433d831722b1f4aeb1666953f1c0e77` (r:1 w:0)
/// Proof: UNKNOWN KEY `0x2433d831722b1f4aeb1666953f1c0e77` (r:1 w:0)
/// Storage: `PoolAssets::Account` (r:1 w:1)
/// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
fn remove_liquidity() -> Weight {
// Proof Size summary in bytes:
// Measured: `1106`
// Estimated: `7404`
// Minimum execution time: 166_533_000 picoseconds.
Weight::from_parts(166_533_000, 0)
.saturating_add(Weight::from_parts(0, 7404))
.saturating_add(T::DbWeight::get().reads(8))
.saturating_add(T::DbWeight::get().writes(6))
}
/// Storage: `ForeignAssets::Asset` (r:2 w:2)
/// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`)
/// Storage: `ForeignAssets::Account` (r:4 w:4)
/// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`)
/// Storage: `System::Account` (r:2 w:2)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
fn swap_exact_tokens_for_tokens() -> Weight {
// Proof Size summary in bytes:
// Measured: `1148`
// Estimated: `13818`
// Minimum execution time: 206_165_000 picoseconds.
Weight::from_parts(206_165_000, 0)
.saturating_add(Weight::from_parts(0, 13818))
.saturating_add(T::DbWeight::get().reads(8))
.saturating_add(T::DbWeight::get().writes(8))
}
/// Storage: `System::Account` (r:2 w:2)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// Storage: `ForeignAssets::Asset` (r:2 w:2)
/// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`)
/// Storage: `ForeignAssets::Account` (r:4 w:4)
/// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`)
fn swap_tokens_for_exact_tokens() -> Weight {
// Proof Size summary in bytes:
// Measured: `1148`
// Estimated: `13818`
// Minimum execution time: 208_694_000 picoseconds.
Weight::from_parts(208_694_000, 0)
.saturating_add(Weight::from_parts(0, 13818))
.saturating_add(T::DbWeight::get().reads(8))
.saturating_add(T::DbWeight::get().writes(8))
}
}
@@ -18,9 +18,12 @@ use super::{
ParachainSystem, PolkadotXcm, PoolAssets, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, ParachainSystem, PolkadotXcm, PoolAssets, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin,
TrustBackedAssetsInstance, WeightToFee, XcmpQueue, TrustBackedAssetsInstance, WeightToFee, XcmpQueue,
}; };
use crate::ForeignAssets; use crate::{AllowMultiAssetPools, ForeignAssets, LiquidityWithdrawalFee};
use assets_common::matching::{ use assets_common::{
local_and_foreign_assets::MatchesLocalAndForeignAssetsMultiLocation,
matching::{
FromSiblingParachain, IsForeignConcreteAsset, StartsWith, StartsWithExplicitGlobalConsensus, FromSiblingParachain, IsForeignConcreteAsset, StartsWith, StartsWithExplicitGlobalConsensus,
},
}; };
use frame_support::{ use frame_support::{
match_types, parameter_types, match_types, parameter_types,
@@ -163,6 +166,25 @@ pub type PoolFungiblesTransactor = FungiblesAdapter<
pub type AssetTransactors = pub type AssetTransactors =
(CurrencyTransactor, FungiblesTransactor, ForeignFungiblesTransactor, PoolFungiblesTransactor); (CurrencyTransactor, FungiblesTransactor, ForeignFungiblesTransactor, PoolFungiblesTransactor);
/// Simple `MultiLocation` matcher for Local and Foreign asset `MultiLocation`.
pub struct LocalAndForeignAssetsMultiLocationMatcher;
impl MatchesLocalAndForeignAssetsMultiLocation for LocalAndForeignAssetsMultiLocationMatcher {
fn is_local(location: &MultiLocation) -> bool {
use assets_common::fungible_conversion::MatchesMultiLocation;
TrustBackedAssetsConvertedConcreteId::contains(location)
}
fn is_foreign(location: &MultiLocation) -> bool {
use assets_common::fungible_conversion::MatchesMultiLocation;
ForeignAssetsConvertedConcreteId::contains(location)
}
}
impl Contains<MultiLocation> for LocalAndForeignAssetsMultiLocationMatcher {
fn contains(location: &MultiLocation) -> bool {
Self::is_local(location) || Self::is_foreign(location)
}
}
/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, /// 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 /// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can
/// biases the kind of local `Origin` it will become. /// biases the kind of local `Origin` it will become.
@@ -217,6 +239,16 @@ impl Contains<RuntimeCall> for SafeCallFilter {
} }
} }
// Allow to change dedicated storage items (called by governance-like)
match call {
RuntimeCall::System(frame_system::Call::set_storage { items })
if items.iter().any(|(k, _)| {
k.eq(&AllowMultiAssetPools::key()) | k.eq(&LiquidityWithdrawalFee::key())
}) =>
return true,
_ => (),
};
matches!( matches!(
call, call,
RuntimeCall::PolkadotXcm(pallet_xcm::Call::force_xcm_version { .. }) | RuntimeCall::PolkadotXcm(pallet_xcm::Call::force_xcm_version { .. }) |
@@ -540,7 +572,8 @@ pub struct BenchmarkMultiLocationConverter<SelfParaId> {
} }
#[cfg(feature = "runtime-benchmarks")] #[cfg(feature = "runtime-benchmarks")]
impl<SelfParaId> pallet_asset_conversion::BenchmarkHelper<MultiLocation> impl<SelfParaId>
pallet_asset_conversion::BenchmarkHelper<MultiLocation, sp_std::boxed::Box<MultiLocation>>
for BenchmarkMultiLocationConverter<SelfParaId> for BenchmarkMultiLocationConverter<SelfParaId>
where where
SelfParaId: Get<ParaId>, SelfParaId: Get<ParaId>,
@@ -555,4 +588,8 @@ where
), ),
} }
} }
fn multiasset_id(asset_id: u32) -> sp_std::boxed::Box<MultiLocation> {
sp_std::boxed::Box::new(Self::asset_id(asset_id))
}
} }
@@ -28,7 +28,8 @@ use asset_hub_westend_runtime::{
AssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, AssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf,
WestendLocation, WestendLocation,
}, },
MetadataDepositBase, MetadataDepositPerByte, RuntimeCall, RuntimeEvent, AllowMultiAssetPools, LiquidityWithdrawalFee, MetadataDepositBase, MetadataDepositPerByte,
RuntimeCall, RuntimeEvent,
}; };
use asset_test_utils::{CollatorSessionKeys, ExtBuilder, RuntimeHelper, XcmReceivedFrom}; use asset_test_utils::{CollatorSessionKeys, ExtBuilder, RuntimeHelper, XcmReceivedFrom};
use codec::{Decode, DecodeLimit, Encode}; use codec::{Decode, DecodeLimit, Encode};
@@ -39,7 +40,10 @@ use frame_support::{
weights::{Weight, WeightToFee as WeightToFeeT}, weights::{Weight, WeightToFee as WeightToFeeT},
}; };
use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance}; use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance};
use sp_runtime::traits::MaybeEquivalence; use sp_runtime::{
traits::{CheckedAdd, CheckedSub, MaybeEquivalence},
Permill,
};
use std::convert::Into; use std::convert::Into;
use xcm::{latest::prelude::*, VersionedXcm, MAX_XCM_DECODE_DEPTH}; use xcm::{latest::prelude::*, VersionedXcm, MAX_XCM_DECODE_DEPTH};
use xcm_executor::{ use xcm_executor::{
@@ -652,3 +656,39 @@ fn plain_receive_teleported_asset_works() {
assert_eq!(outcome.ensure_complete(), Ok(())); assert_eq!(outcome.ensure_complete(), Ok(()));
}) })
} }
#[test]
fn change_allow_multi_asset_pools_by_governance_works() {
asset_test_utils::test_cases::change_storage_constant_by_governance_works::<
Runtime,
AllowMultiAssetPools,
bool,
>(
collator_session_keys(),
1000,
Box::new(|call| RuntimeCall::System(call).encode()),
|| (AllowMultiAssetPools::key().to_vec(), AllowMultiAssetPools::get()),
|old_value| !old_value,
)
}
#[test]
fn change_liquidity_withdrawal_fee_by_governance_works() {
asset_test_utils::test_cases::change_storage_constant_by_governance_works::<
Runtime,
LiquidityWithdrawalFee,
Permill,
>(
collator_session_keys(),
1000,
Box::new(|call| RuntimeCall::System(call).encode()),
|| (LiquidityWithdrawalFee::key().to_vec(), LiquidityWithdrawalFee::get()),
|old_value| {
if let Some(new_value) = old_value.checked_add(&Permill::from_percent(2)) {
new_value
} else {
old_value.checked_sub(&Permill::from_percent(2)).unwrap()
}
},
)
}
@@ -9,6 +9,7 @@ description = "Assets common utilities"
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] }
log = { version = "0.4.19", default-features = false } log = { version = "0.4.19", default-features = false }
impl-trait-for-tuples = "0.2.2"
# Substrate # Substrate
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
@@ -37,6 +37,19 @@ where
) -> Result<MultiAsset, FungiblesAccessError>; ) -> Result<MultiAsset, FungiblesAccessError>;
} }
/// Checks for `MultiLocation`.
pub trait MatchesMultiLocation<AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertBalance>:
MatchesFungibles<AssetId, Balance>
where
AssetId: Clone,
Balance: Clone,
MatchAssetId: Contains<MultiLocation>,
ConvertAssetId: MaybeEquivalence<MultiLocation, AssetId>,
ConvertBalance: MaybeEquivalence<u128, Balance>,
{
fn contains(location: &MultiLocation) -> bool;
}
impl< impl<
AssetId: Clone, AssetId: Clone,
Balance: Clone, Balance: Clone,
@@ -82,6 +95,38 @@ impl<
} }
} }
impl<
AssetId: Clone,
Balance: Clone,
MatchAssetId: Contains<MultiLocation>,
ConvertAssetId: MaybeEquivalence<MultiLocation, AssetId>,
ConvertBalance: MaybeEquivalence<u128, Balance>,
> MatchesMultiLocation<AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertBalance>
for MatchedConvertedConcreteId<AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertBalance>
{
fn contains(location: &MultiLocation) -> bool {
MatchAssetId::contains(location)
}
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<
AssetId: Clone,
Balance: Clone,
MatchAssetId: Contains<MultiLocation>,
ConvertAssetId: MaybeEquivalence<MultiLocation, AssetId>,
ConvertBalance: MaybeEquivalence<u128, Balance>,
> MatchesMultiLocation<AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertBalance> for Tuple
{
fn contains(location: &MultiLocation) -> bool {
for_tuples!( #(
match Tuple::contains(location) { o @ true => return o, _ => () }
)* );
log::trace!(target: "xcm::contains", "did not match location: {:?}", &location);
false
}
}
/// Helper function to convert collections with [`(AssetId, Balance)`] to [`MultiAsset`] /// Helper function to convert collections with [`(AssetId, Balance)`] to [`MultiAsset`]
pub fn convert<'a, AssetId, Balance, ConvertAssetId, ConvertBalance, Converter>( pub fn convert<'a, AssetId, Balance, ConvertAssetId, ConvertBalance, Converter>(
items: impl Iterator<Item = &'a (AssetId, Balance)>, items: impl Iterator<Item = &'a (AssetId, Balance)>,
@@ -13,92 +13,81 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use crate::local_and_foreign_assets::fungibles::Inspect;
use cumulus_primitives_core::InteriorMultiLocation;
use frame_support::{ use frame_support::{
pallet_prelude::DispatchError, pallet_prelude::DispatchError,
traits::{ traits::{
fungibles::{ fungibles::{Balanced, Create, HandleImbalanceDrop, Inspect, Mutate, Unbalanced},
self, Balanced, Create, HandleImbalanceDrop, Mutate as MutateFungible, Unbalanced, tokens::{
DepositConsequence, Fortitude, Precision, Preservation, Provenance, WithdrawConsequence,
}, },
tokens::{DepositConsequence, Fortitude, Preservation, Provenance, WithdrawConsequence}, AccountTouch, Contains, ContainsPair, Get, PalletInfoAccess,
AccountTouch, ContainsPair, Get, PalletInfoAccess,
}, },
}; };
use pallet_asset_conversion::MultiAssetIdConverter; use pallet_asset_conversion::{MultiAssetIdConversionResult, MultiAssetIdConverter};
use parachains_common::{AccountId, AssetIdForTrustBackedAssets}; use parachains_common::AccountId;
use sp_runtime::{traits::MaybeEquivalence, DispatchResult}; use sp_runtime::{traits::MaybeEquivalence, DispatchResult};
use sp_std::{boxed::Box, marker::PhantomData}; use sp_std::{boxed::Box, marker::PhantomData};
use xcm::{latest::MultiLocation, opaque::lts::Junctions::Here}; use xcm::latest::MultiLocation;
use xcm_builder::AsPrefixedGeneralIndex;
use xcm_executor::traits::JustTry;
/// Whether the multilocation refers to an asset in the local assets pallet or not, pub struct MultiLocationConverter<NativeAssetLocation: Get<MultiLocation>, MultiLocationMatcher> {
/// and if return the asset id. _phantom: PhantomData<(NativeAssetLocation, MultiLocationMatcher)>,
fn is_local<TrustBackedAssetsPalletLocation: Get<MultiLocation>>(
multilocation: MultiLocation,
) -> Option<u32> {
AsPrefixedGeneralIndex::<TrustBackedAssetsPalletLocation, AssetIdForTrustBackedAssets, JustTry>::convert(&multilocation)
} }
pub struct MultiLocationConverter<Balances, ParachainLocation: Get<InteriorMultiLocation>> { impl<NativeAssetLocation, MultiLocationMatcher>
_phantom: PhantomData<(Balances, ParachainLocation)>, MultiAssetIdConverter<Box<MultiLocation>, MultiLocation>
} for MultiLocationConverter<NativeAssetLocation, MultiLocationMatcher>
impl<Balances, ParachainLocation> MultiAssetIdConverter<Box<MultiLocation>, MultiLocation>
for MultiLocationConverter<Balances, ParachainLocation>
where where
Balances: PalletInfoAccess, NativeAssetLocation: Get<MultiLocation>,
ParachainLocation: Get<InteriorMultiLocation>, MultiLocationMatcher: Contains<MultiLocation>,
{ {
fn get_native() -> Box<MultiLocation> { fn get_native() -> Box<MultiLocation> {
Box::new(MultiLocation { parents: 0, interior: Here }) Box::new(NativeAssetLocation::get())
} }
fn is_native(asset_id: &Box<MultiLocation>) -> bool { fn is_native(asset_id: &Box<MultiLocation>) -> bool {
let mut asset_id = asset_id.clone(); *asset_id == Self::get_native()
asset_id.simplify(&ParachainLocation::get());
*asset_id == *Self::get_native()
} }
fn try_convert(asset_id: &Box<MultiLocation>) -> Result<MultiLocation, ()> { fn try_convert(
let mut asset_id = asset_id.clone(); asset_id: &Box<MultiLocation>,
asset_id.simplify(&ParachainLocation::get()); ) -> MultiAssetIdConversionResult<Box<MultiLocation>, MultiLocation> {
if Self::is_native(&asset_id) { if Self::is_native(&asset_id) {
// Otherwise it will try and touch the asset to create an account. return MultiAssetIdConversionResult::Native
return Err(())
}
// Return simplified MultiLocation:
Ok(*asset_id)
} }
fn into_multiasset_id(asset_id: &MultiLocation) -> Box<MultiLocation> { if MultiLocationMatcher::contains(&asset_id) {
let mut asset_id = *asset_id; MultiAssetIdConversionResult::Converted(*asset_id.clone())
asset_id.simplify(&ParachainLocation::get()); } else {
Box::new(asset_id) MultiAssetIdConversionResult::Unsupported(asset_id.clone())
}
} }
} }
pub struct LocalAndForeignAssets<Assets, ForeignAssets, Location> { pub trait MatchesLocalAndForeignAssetsMultiLocation {
_phantom: PhantomData<(Assets, ForeignAssets, Location)>, fn is_local(location: &MultiLocation) -> bool;
fn is_foreign(location: &MultiLocation) -> bool;
} }
impl<Assets, ForeignAssets, Location> Unbalanced<AccountId> pub struct LocalAndForeignAssets<Assets, LocalAssetIdConverter, ForeignAssets> {
for LocalAndForeignAssets<Assets, ForeignAssets, Location> _phantom: PhantomData<(Assets, LocalAssetIdConverter, ForeignAssets)>,
}
impl<Assets, LocalAssetIdConverter, ForeignAssets> Unbalanced<AccountId>
for LocalAndForeignAssets<Assets, LocalAssetIdConverter, ForeignAssets>
where where
Location: Get<MultiLocation>,
ForeignAssets: Inspect<AccountId, Balance = u128, AssetId = MultiLocation>
+ Unbalanced<AccountId>
+ Balanced<AccountId>,
Assets: Inspect<AccountId, Balance = u128, AssetId = u32> Assets: Inspect<AccountId, Balance = u128, AssetId = u32>
+ Unbalanced<AccountId> + Unbalanced<AccountId>
+ Balanced<AccountId> + Balanced<AccountId>
+ PalletInfoAccess, + PalletInfoAccess,
LocalAssetIdConverter: MaybeEquivalence<MultiLocation, u32>,
ForeignAssets: Inspect<AccountId, Balance = u128, AssetId = MultiLocation>
+ Unbalanced<AccountId>
+ Balanced<AccountId>,
{ {
fn handle_dust(dust: frame_support::traits::fungibles::Dust<AccountId, Self>) { fn handle_dust(dust: frame_support::traits::fungibles::Dust<AccountId, Self>) {
let credit = dust.into_credit(); let credit = dust.into_credit();
if let Some(asset) = is_local::<Location>(credit.asset()) { if let Some(asset) = LocalAssetIdConverter::convert(&credit.asset()) {
Assets::handle_raw_dust(asset, credit.peek()); Assets::handle_raw_dust(asset, credit.peek());
} else { } else {
ForeignAssets::handle_raw_dust(credit.asset(), credit.peek()); ForeignAssets::handle_raw_dust(credit.asset(), credit.peek());
@@ -109,14 +98,11 @@ where
} }
fn write_balance( fn write_balance(
asset: <Self as frame_support::traits::fungibles::Inspect<AccountId>>::AssetId, asset: <Self as Inspect<AccountId>>::AssetId,
who: &AccountId, who: &AccountId,
amount: <Self as frame_support::traits::fungibles::Inspect<AccountId>>::Balance, amount: <Self as Inspect<AccountId>>::Balance,
) -> Result< ) -> Result<Option<<Self as Inspect<AccountId>>::Balance>, DispatchError> {
Option<<Self as frame_support::traits::fungibles::Inspect<AccountId>>::Balance>, if let Some(asset) = LocalAssetIdConverter::convert(&asset) {
sp_runtime::DispatchError,
> {
if let Some(asset) = is_local::<Location>(asset) {
Assets::write_balance(asset, who, amount) Assets::write_balance(asset, who, amount)
} else { } else {
ForeignAssets::write_balance(asset, who, amount) ForeignAssets::write_balance(asset, who, amount)
@@ -125,27 +111,55 @@ where
/// Set the total issuance of `asset` to `amount`. /// Set the total issuance of `asset` to `amount`.
fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) { fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) {
if let Some(asset) = is_local::<Location>(asset) { if let Some(asset) = LocalAssetIdConverter::convert(&asset) {
Assets::set_total_issuance(asset, amount) Assets::set_total_issuance(asset, amount)
} else { } else {
ForeignAssets::set_total_issuance(asset, amount) ForeignAssets::set_total_issuance(asset, amount)
} }
} }
fn decrease_balance(
asset: Self::AssetId,
who: &AccountId,
amount: Self::Balance,
precision: Precision,
preservation: Preservation,
force: Fortitude,
) -> Result<Self::Balance, DispatchError> {
if let Some(asset) = LocalAssetIdConverter::convert(&asset) {
Assets::decrease_balance(asset, who, amount, precision, preservation, force)
} else {
ForeignAssets::decrease_balance(asset, who, amount, precision, preservation, force)
}
} }
impl<Assets, ForeignAssets, Location> Inspect<AccountId> fn increase_balance(
for LocalAndForeignAssets<Assets, ForeignAssets, Location> asset: Self::AssetId,
who: &AccountId,
amount: Self::Balance,
precision: Precision,
) -> Result<Self::Balance, DispatchError> {
if let Some(asset) = LocalAssetIdConverter::convert(&asset) {
Assets::increase_balance(asset, who, amount, precision)
} else {
ForeignAssets::increase_balance(asset, who, amount, precision)
}
}
}
impl<Assets, LocalAssetIdConverter, ForeignAssets> Inspect<AccountId>
for LocalAndForeignAssets<Assets, LocalAssetIdConverter, ForeignAssets>
where where
Location: Get<MultiLocation>,
ForeignAssets: Inspect<AccountId, Balance = u128, AssetId = MultiLocation>,
Assets: Inspect<AccountId, Balance = u128, AssetId = u32>, Assets: Inspect<AccountId, Balance = u128, AssetId = u32>,
LocalAssetIdConverter: MaybeEquivalence<MultiLocation, u32>,
ForeignAssets: Inspect<AccountId, Balance = u128, AssetId = MultiLocation>,
{ {
type AssetId = MultiLocation; type AssetId = MultiLocation;
type Balance = u128; type Balance = u128;
/// The total amount of issuance in the system. /// The total amount of issuance in the system.
fn total_issuance(asset: Self::AssetId) -> Self::Balance { fn total_issuance(asset: Self::AssetId) -> Self::Balance {
if let Some(asset) = is_local::<Location>(asset) { if let Some(asset) = LocalAssetIdConverter::convert(&asset) {
Assets::total_issuance(asset) Assets::total_issuance(asset)
} else { } else {
ForeignAssets::total_issuance(asset) ForeignAssets::total_issuance(asset)
@@ -154,16 +168,27 @@ where
/// The minimum balance any single account may have. /// The minimum balance any single account may have.
fn minimum_balance(asset: Self::AssetId) -> Self::Balance { fn minimum_balance(asset: Self::AssetId) -> Self::Balance {
if let Some(asset) = is_local::<Location>(asset) { if let Some(asset) = LocalAssetIdConverter::convert(&asset) {
Assets::minimum_balance(asset) Assets::minimum_balance(asset)
} else { } else {
ForeignAssets::minimum_balance(asset) ForeignAssets::minimum_balance(asset)
} }
} }
fn total_balance(
asset: <Self as Inspect<AccountId>>::AssetId,
account: &AccountId,
) -> <Self as Inspect<AccountId>>::Balance {
if let Some(asset) = LocalAssetIdConverter::convert(&asset) {
Assets::total_balance(asset, account)
} else {
ForeignAssets::total_balance(asset, account)
}
}
/// Get the `asset` balance of `who`. /// Get the `asset` balance of `who`.
fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance { fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
if let Some(asset) = is_local::<Location>(asset) { if let Some(asset) = LocalAssetIdConverter::convert(&asset) {
Assets::balance(asset, who) Assets::balance(asset, who)
} else { } else {
ForeignAssets::balance(asset, who) ForeignAssets::balance(asset, who)
@@ -177,7 +202,7 @@ where
presevation: Preservation, presevation: Preservation,
fortitude: Fortitude, fortitude: Fortitude,
) -> Self::Balance { ) -> Self::Balance {
if let Some(asset) = is_local::<Location>(asset) { if let Some(asset) = LocalAssetIdConverter::convert(&asset) {
Assets::reducible_balance(asset, who, presevation, fortitude) Assets::reducible_balance(asset, who, presevation, fortitude)
} else { } else {
ForeignAssets::reducible_balance(asset, who, presevation, fortitude) ForeignAssets::reducible_balance(asset, who, presevation, fortitude)
@@ -196,7 +221,7 @@ where
amount: Self::Balance, amount: Self::Balance,
mint: Provenance, mint: Provenance,
) -> DepositConsequence { ) -> DepositConsequence {
if let Some(asset) = is_local::<Location>(asset) { if let Some(asset) = LocalAssetIdConverter::convert(&asset) {
Assets::can_deposit(asset, who, amount, mint) Assets::can_deposit(asset, who, amount, mint)
} else { } else {
ForeignAssets::can_deposit(asset, who, amount, mint) ForeignAssets::can_deposit(asset, who, amount, mint)
@@ -210,7 +235,7 @@ where
who: &AccountId, who: &AccountId,
amount: Self::Balance, amount: Self::Balance,
) -> WithdrawConsequence<Self::Balance> { ) -> WithdrawConsequence<Self::Balance> {
if let Some(asset) = is_local::<Location>(asset) { if let Some(asset) = LocalAssetIdConverter::convert(&asset) {
Assets::can_withdraw(asset, who, amount) Assets::can_withdraw(asset, who, amount)
} else { } else {
ForeignAssets::can_withdraw(asset, who, amount) ForeignAssets::can_withdraw(asset, who, amount)
@@ -219,36 +244,25 @@ where
/// Returns `true` if an `asset` exists. /// Returns `true` if an `asset` exists.
fn asset_exists(asset: Self::AssetId) -> bool { fn asset_exists(asset: Self::AssetId) -> bool {
if let Some(asset) = is_local::<Location>(asset) { if let Some(asset) = LocalAssetIdConverter::convert(&asset) {
Assets::asset_exists(asset) Assets::asset_exists(asset)
} else { } else {
ForeignAssets::asset_exists(asset) ForeignAssets::asset_exists(asset)
} }
} }
fn total_balance(
asset: <Self as frame_support::traits::fungibles::Inspect<AccountId>>::AssetId,
account: &AccountId,
) -> <Self as frame_support::traits::fungibles::Inspect<AccountId>>::Balance {
if let Some(asset) = is_local::<Location>(asset) {
Assets::total_balance(asset, account)
} else {
ForeignAssets::total_balance(asset, account)
}
}
} }
impl<Assets, ForeignAssets, Location> MutateFungible<AccountId> impl<Assets, LocalAssetIdConverter, ForeignAssets> Mutate<AccountId>
for LocalAndForeignAssets<Assets, ForeignAssets, Location> for LocalAndForeignAssets<Assets, LocalAssetIdConverter, ForeignAssets>
where where
Location: Get<MultiLocation>, Assets: Mutate<AccountId>
ForeignAssets: MutateFungible<AccountId, Balance = u128>
+ Inspect<AccountId, Balance = u128, AssetId = MultiLocation>
+ Balanced<AccountId>,
Assets: MutateFungible<AccountId>
+ Inspect<AccountId, Balance = u128, AssetId = u32> + Inspect<AccountId, Balance = u128, AssetId = u32>
+ Balanced<AccountId> + Balanced<AccountId>
+ PalletInfoAccess, + PalletInfoAccess,
LocalAssetIdConverter: MaybeEquivalence<MultiLocation, u32>,
ForeignAssets: Mutate<AccountId, Balance = u128>
+ Inspect<AccountId, Balance = u128, AssetId = MultiLocation>
+ Balanced<AccountId>,
{ {
/// Transfer funds from one account into another. /// Transfer funds from one account into another.
fn transfer( fn transfer(
@@ -258,7 +272,7 @@ where
amount: Self::Balance, amount: Self::Balance,
keep_alive: Preservation, keep_alive: Preservation,
) -> Result<Self::Balance, DispatchError> { ) -> Result<Self::Balance, DispatchError> {
if let Some(asset_id) = is_local::<Location>(asset) { if let Some(asset_id) = LocalAssetIdConverter::convert(&asset) {
Assets::transfer(asset_id, source, dest, amount, keep_alive) Assets::transfer(asset_id, source, dest, amount, keep_alive)
} else { } else {
ForeignAssets::transfer(asset, source, dest, amount, keep_alive) ForeignAssets::transfer(asset, source, dest, amount, keep_alive)
@@ -266,12 +280,12 @@ where
} }
} }
impl<Assets, ForeignAssets, Location> Create<AccountId> impl<Assets, LocalAssetIdConverter, ForeignAssets> Create<AccountId>
for LocalAndForeignAssets<Assets, ForeignAssets, Location> for LocalAndForeignAssets<Assets, LocalAssetIdConverter, ForeignAssets>
where where
Location: Get<MultiLocation>,
ForeignAssets: Create<AccountId> + Inspect<AccountId, Balance = u128, AssetId = MultiLocation>,
Assets: Create<AccountId> + Inspect<AccountId, Balance = u128, AssetId = u32>, Assets: Create<AccountId> + Inspect<AccountId, Balance = u128, AssetId = u32>,
LocalAssetIdConverter: MaybeEquivalence<MultiLocation, u32>,
ForeignAssets: Create<AccountId> + Inspect<AccountId, Balance = u128, AssetId = MultiLocation>,
{ {
/// Create a new fungible asset. /// Create a new fungible asset.
fn create( fn create(
@@ -280,7 +294,7 @@ where
is_sufficient: bool, is_sufficient: bool,
min_balance: Self::Balance, min_balance: Self::Balance,
) -> DispatchResult { ) -> DispatchResult {
if let Some(asset_id) = is_local::<Location>(asset_id) { if let Some(asset_id) = LocalAssetIdConverter::convert(&asset_id) {
Assets::create(asset_id, admin, is_sufficient, min_balance) Assets::create(asset_id, admin, is_sufficient, min_balance)
} else { } else {
ForeignAssets::create(asset_id, admin, is_sufficient, min_balance) ForeignAssets::create(asset_id, admin, is_sufficient, min_balance)
@@ -288,19 +302,19 @@ where
} }
} }
impl<Assets, ForeignAssets, Location> AccountTouch<MultiLocation, AccountId> impl<Assets, LocalAssetIdConverter, ForeignAssets> AccountTouch<MultiLocation, AccountId>
for LocalAndForeignAssets<Assets, ForeignAssets, Location> for LocalAndForeignAssets<Assets, LocalAssetIdConverter, ForeignAssets>
where where
Location: Get<MultiLocation>,
ForeignAssets: AccountTouch<MultiLocation, AccountId, Balance = u128>,
Assets: AccountTouch<u32, AccountId, Balance = u128>, Assets: AccountTouch<u32, AccountId, Balance = u128>,
LocalAssetIdConverter: MaybeEquivalence<MultiLocation, u32>,
ForeignAssets: AccountTouch<MultiLocation, AccountId, Balance = u128>,
{ {
type Balance = u128; type Balance = u128;
fn deposit_required( fn deposit_required(
asset_id: MultiLocation, asset_id: MultiLocation,
) -> <Self as AccountTouch<MultiLocation, AccountId>>::Balance { ) -> <Self as AccountTouch<MultiLocation, AccountId>>::Balance {
if let Some(asset_id) = is_local::<Location>(asset_id) { if let Some(asset_id) = LocalAssetIdConverter::convert(&asset_id) {
Assets::deposit_required(asset_id) Assets::deposit_required(asset_id)
} else { } else {
ForeignAssets::deposit_required(asset_id) ForeignAssets::deposit_required(asset_id)
@@ -311,8 +325,8 @@ where
asset_id: MultiLocation, asset_id: MultiLocation,
who: AccountId, who: AccountId,
depositor: AccountId, depositor: AccountId,
) -> Result<(), sp_runtime::DispatchError> { ) -> Result<(), DispatchError> {
if let Some(asset_id) = is_local::<Location>(asset_id) { if let Some(asset_id) = LocalAssetIdConverter::convert(&asset_id) {
Assets::touch(asset_id, who, depositor) Assets::touch(asset_id, who, depositor)
} else { } else {
ForeignAssets::touch(asset_id, who, depositor) ForeignAssets::touch(asset_id, who, depositor)
@@ -321,16 +335,16 @@ where
} }
/// Implements [`ContainsPair`] trait for a pair of asset and account IDs. /// Implements [`ContainsPair`] trait for a pair of asset and account IDs.
impl<Assets, ForeignAssets, Location> ContainsPair<MultiLocation, AccountId> impl<Assets, LocalAssetIdConverter, ForeignAssets> ContainsPair<MultiLocation, AccountId>
for LocalAndForeignAssets<Assets, ForeignAssets, Location> for LocalAndForeignAssets<Assets, LocalAssetIdConverter, ForeignAssets>
where where
Location: Get<MultiLocation>,
ForeignAssets: ContainsPair<MultiLocation, AccountId>,
Assets: PalletInfoAccess + ContainsPair<u32, AccountId>, Assets: PalletInfoAccess + ContainsPair<u32, AccountId>,
LocalAssetIdConverter: MaybeEquivalence<MultiLocation, u32>,
ForeignAssets: ContainsPair<MultiLocation, AccountId>,
{ {
/// Check if an account with the given asset ID and account address exists. /// Check if an account with the given asset ID and account address exists.
fn contains(asset_id: &MultiLocation, who: &AccountId) -> bool { fn contains(asset_id: &MultiLocation, who: &AccountId) -> bool {
if let Some(asset_id) = is_local::<Location>(*asset_id) { if let Some(asset_id) = LocalAssetIdConverter::convert(asset_id) {
Assets::contains(&asset_id, &who) Assets::contains(&asset_id, &who)
} else { } else {
ForeignAssets::contains(&asset_id, &who) ForeignAssets::contains(&asset_id, &who)
@@ -338,33 +352,33 @@ where
} }
} }
impl<Assets, ForeignAssets, Location> Balanced<AccountId> impl<Assets, LocalAssetIdConverter, ForeignAssets> Balanced<AccountId>
for LocalAndForeignAssets<Assets, ForeignAssets, Location> for LocalAndForeignAssets<Assets, LocalAssetIdConverter, ForeignAssets>
where where
Location: Get<MultiLocation>,
ForeignAssets:
Balanced<AccountId> + Inspect<AccountId, Balance = u128, AssetId = MultiLocation>,
Assets: Assets:
Balanced<AccountId> + Inspect<AccountId, Balance = u128, AssetId = u32> + PalletInfoAccess, Balanced<AccountId> + Inspect<AccountId, Balance = u128, AssetId = u32> + PalletInfoAccess,
{ LocalAssetIdConverter: MaybeEquivalence<MultiLocation, u32>,
type OnDropDebt = DebtDropIndirection<Assets, ForeignAssets, Location>;
type OnDropCredit = CreditDropIndirection<Assets, ForeignAssets, Location>;
}
pub struct DebtDropIndirection<Assets, ForeignAssets, Location> {
_phantom: PhantomData<LocalAndForeignAssets<Assets, ForeignAssets, Location>>,
}
impl<Assets, ForeignAssets, Location> HandleImbalanceDrop<MultiLocation, u128>
for DebtDropIndirection<Assets, ForeignAssets, Location>
where
Location: Get<MultiLocation>,
ForeignAssets: ForeignAssets:
Balanced<AccountId> + Inspect<AccountId, Balance = u128, AssetId = MultiLocation>, Balanced<AccountId> + Inspect<AccountId, Balance = u128, AssetId = MultiLocation>,
{
type OnDropDebt = DebtDropIndirection<Assets, LocalAssetIdConverter, ForeignAssets>;
type OnDropCredit = CreditDropIndirection<Assets, LocalAssetIdConverter, ForeignAssets>;
}
pub struct DebtDropIndirection<Assets, LocalAssetIdConverter, ForeignAssets> {
_phantom: PhantomData<LocalAndForeignAssets<Assets, LocalAssetIdConverter, ForeignAssets>>,
}
impl<Assets, LocalAssetIdConverter, ForeignAssets> HandleImbalanceDrop<MultiLocation, u128>
for DebtDropIndirection<Assets, LocalAssetIdConverter, ForeignAssets>
where
Assets: Balanced<AccountId> + Inspect<AccountId, Balance = u128, AssetId = u32>, Assets: Balanced<AccountId> + Inspect<AccountId, Balance = u128, AssetId = u32>,
LocalAssetIdConverter: MaybeEquivalence<MultiLocation, u32>,
ForeignAssets:
Balanced<AccountId> + Inspect<AccountId, Balance = u128, AssetId = MultiLocation>,
{ {
fn handle(asset: MultiLocation, amount: u128) { fn handle(asset: MultiLocation, amount: u128) {
if let Some(asset_id) = is_local::<Location>(asset) { if let Some(asset_id) = LocalAssetIdConverter::convert(&asset) {
Assets::OnDropDebt::handle(asset_id, amount); Assets::OnDropDebt::handle(asset_id, amount);
} else { } else {
ForeignAssets::OnDropDebt::handle(asset, amount); ForeignAssets::OnDropDebt::handle(asset, amount);
@@ -372,23 +386,87 @@ where
} }
} }
pub struct CreditDropIndirection<Assets, ForeignAssets, Location> { pub struct CreditDropIndirection<Assets, LocalAssetIdConverter, ForeignAssets> {
_phantom: PhantomData<LocalAndForeignAssets<Assets, ForeignAssets, Location>>, _phantom: PhantomData<LocalAndForeignAssets<Assets, LocalAssetIdConverter, ForeignAssets>>,
} }
impl<Assets, ForeignAssets, Location> HandleImbalanceDrop<MultiLocation, u128> impl<Assets, LocalAssetIdConverter, ForeignAssets> HandleImbalanceDrop<MultiLocation, u128>
for CreditDropIndirection<Assets, ForeignAssets, Location> for CreditDropIndirection<Assets, LocalAssetIdConverter, ForeignAssets>
where where
Location: Get<MultiLocation>, Assets: Balanced<AccountId> + Inspect<AccountId, Balance = u128, AssetId = u32>,
LocalAssetIdConverter: MaybeEquivalence<MultiLocation, u32>,
ForeignAssets: ForeignAssets:
Balanced<AccountId> + Inspect<AccountId, Balance = u128, AssetId = MultiLocation>, Balanced<AccountId> + Inspect<AccountId, Balance = u128, AssetId = MultiLocation>,
Assets: Balanced<AccountId> + Inspect<AccountId, Balance = u128, AssetId = u32>,
{ {
fn handle(asset: MultiLocation, amount: u128) { fn handle(asset: MultiLocation, amount: u128) {
if let Some(asset_id) = is_local::<Location>(asset) { if let Some(asset_id) = LocalAssetIdConverter::convert(&asset) {
Assets::OnDropCredit::handle(asset_id, amount); Assets::OnDropCredit::handle(asset_id, amount);
} else { } else {
ForeignAssets::OnDropCredit::handle(asset, amount); ForeignAssets::OnDropCredit::handle(asset, amount);
} }
} }
} }
#[cfg(test)]
mod tests {
use crate::{
local_and_foreign_assets::MultiLocationConverter, matching::StartsWith,
AssetIdForPoolAssetsConvert, AssetIdForTrustBackedAssetsConvert,
};
use frame_support::traits::EverythingBut;
use pallet_asset_conversion::{MultiAssetIdConversionResult, MultiAssetIdConverter};
use sp_runtime::traits::MaybeEquivalence;
use xcm::latest::prelude::*;
#[test]
fn test_multi_location_converter_works() {
frame_support::parameter_types! {
pub const WestendLocation: MultiLocation = MultiLocation::parent();
pub TrustBackedAssetsPalletLocation: MultiLocation = PalletInstance(50_u8).into();
pub PoolAssetsPalletLocation: MultiLocation = PalletInstance(55_u8).into();
}
type C = MultiLocationConverter<
WestendLocation,
EverythingBut<StartsWith<PoolAssetsPalletLocation>>,
>;
let native_asset = WestendLocation::get();
let local_asset =
AssetIdForTrustBackedAssetsConvert::<TrustBackedAssetsPalletLocation>::convert_back(
&123,
)
.unwrap();
let pool_asset =
AssetIdForPoolAssetsConvert::<PoolAssetsPalletLocation>::convert_back(&456).unwrap();
let foreign_asset1 = MultiLocation { parents: 1, interior: X1(Parachain(2222)) };
let foreign_asset2 = MultiLocation {
parents: 2,
interior: X2(GlobalConsensus(ByGenesis([1; 32])), Parachain(2222)),
};
assert!(C::is_native(&Box::new(native_asset)));
assert!(!C::is_native(&Box::new(local_asset)));
assert!(!C::is_native(&Box::new(pool_asset)));
assert!(!C::is_native(&Box::new(foreign_asset1)));
assert!(!C::is_native(&Box::new(foreign_asset2)));
assert_eq!(C::try_convert(&Box::new(native_asset)), MultiAssetIdConversionResult::Native);
assert_eq!(
C::try_convert(&Box::new(local_asset)),
MultiAssetIdConversionResult::Converted(local_asset)
);
assert_eq!(
C::try_convert(&Box::new(pool_asset)),
MultiAssetIdConversionResult::Unsupported(Box::new(pool_asset))
);
assert_eq!(
C::try_convert(&Box::new(foreign_asset1)),
MultiAssetIdConversionResult::Converted(foreign_asset1)
);
assert_eq!(
C::try_convert(&Box::new(foreign_asset2)),
MultiAssetIdConversionResult::Converted(foreign_asset2)
);
}
}
@@ -33,6 +33,9 @@ use sp_runtime::{
use xcm::latest::prelude::*; use xcm::latest::prelude::*;
use xcm_executor::{traits::ConvertLocation, XcmExecutor}; use xcm_executor::{traits::ConvertLocation, XcmExecutor};
// Re-export test_case from `parachains-runtimes-test-utils`
pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works;
/// Test-case makes sure that `Runtime` can receive native asset from relay chain /// Test-case makes sure that `Runtime` can receive native asset from relay chain
/// and can teleport it back and to the other parachains /// and can teleport it back and to the other parachains
pub fn teleports_for_native_asset_works< pub fn teleports_for_native_asset_works<
@@ -688,6 +688,7 @@ impl_runtime_apis! {
MultiAsset { fun: Fungible(UNITS), id: Concrete(KsmRelayLocation::get()) }, MultiAsset { fun: Fungible(UNITS), id: Concrete(KsmRelayLocation::get()) },
)); ));
pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None; pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None;
pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = None;
} }
impl pallet_xcm_benchmarks::fungible::Config for Runtime { impl pallet_xcm_benchmarks::fungible::Config for Runtime {
@@ -695,6 +696,7 @@ impl_runtime_apis! {
type CheckedAccount = CheckedAccount; type CheckedAccount = CheckedAccount;
type TrustedTeleporter = TrustedTeleporter; type TrustedTeleporter = TrustedTeleporter;
type TrustedReserve = TrustedReserve;
fn get_multi_asset() -> MultiAsset { fn get_multi_asset() -> MultiAsset {
MultiAsset { MultiAsset {
@@ -688,6 +688,7 @@ impl_runtime_apis! {
MultiAsset { fun: Fungible(UNITS), id: Concrete(DotRelayLocation::get()) }, MultiAsset { fun: Fungible(UNITS), id: Concrete(DotRelayLocation::get()) },
)); ));
pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None; pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None;
pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = None;
} }
impl pallet_xcm_benchmarks::fungible::Config for Runtime { impl pallet_xcm_benchmarks::fungible::Config for Runtime {
@@ -695,6 +696,7 @@ impl_runtime_apis! {
type CheckedAccount = CheckedAccount; type CheckedAccount = CheckedAccount;
type TrustedTeleporter = TrustedTeleporter; type TrustedTeleporter = TrustedTeleporter;
type TrustedReserve = TrustedReserve;
fn get_multi_asset() -> MultiAsset { fn get_multi_asset() -> MultiAsset {
MultiAsset { MultiAsset {
@@ -956,6 +956,7 @@ impl_runtime_apis! {
MultiAsset { fun: Fungible(UNITS), id: Concrete(RelayLocation::get()) }, MultiAsset { fun: Fungible(UNITS), id: Concrete(RelayLocation::get()) },
)); ));
pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None; pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None;
pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = None;
} }
impl pallet_xcm_benchmarks::fungible::Config for Runtime { impl pallet_xcm_benchmarks::fungible::Config for Runtime {
@@ -963,6 +964,7 @@ impl_runtime_apis! {
type CheckedAccount = CheckedAccount; type CheckedAccount = CheckedAccount;
type TrustedTeleporter = TrustedTeleporter; type TrustedTeleporter = TrustedTeleporter;
type TrustedReserve = TrustedReserve;
fn get_multi_asset() -> MultiAsset { fn get_multi_asset() -> MultiAsset {
MultiAsset { MultiAsset {
@@ -54,6 +54,9 @@ use xcm_executor::XcmExecutor;
// Re-export test_case from assets // Re-export test_case from assets
pub use asset_test_utils::include_teleports_for_native_asset_works; pub use asset_test_utils::include_teleports_for_native_asset_works;
// Re-export test_case from `parachains-runtimes-test-utils`
pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works;
/// Test-case makes sure that `Runtime` can process bridging initialize via governance-like call /// Test-case makes sure that `Runtime` can process bridging initialize via governance-like call
pub fn initialize_bridge_by_governance_works<Runtime, GrandpaPalletInstance>( pub fn initialize_bridge_by_governance_works<Runtime, GrandpaPalletInstance>(
collator_session_key: CollatorSessionKeys<Runtime>, collator_session_key: CollatorSessionKeys<Runtime>,
@@ -114,76 +117,6 @@ pub fn initialize_bridge_by_governance_works<Runtime, GrandpaPalletInstance>(
}) })
} }
/// Test-case makes sure that `Runtime` can change storage constant via governance-like call
pub fn change_storage_constant_by_governance_works<Runtime, StorageConstant, StorageConstantType>(
collator_session_key: CollatorSessionKeys<Runtime>,
runtime_para_id: u32,
runtime_call_encode: Box<dyn Fn(frame_system::Call<Runtime>) -> Vec<u8>>,
storage_constant_key_value: fn() -> (Vec<u8>, StorageConstantType),
new_storage_constant_value: fn(&StorageConstantType) -> StorageConstantType,
) where
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config
+ pallet_collator_selection::Config
+ cumulus_pallet_dmp_queue::Config
+ cumulus_pallet_parachain_system::Config,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
StorageConstant: Get<StorageConstantType>,
StorageConstantType: Encode + PartialEq + std::fmt::Debug,
{
ExtBuilder::<Runtime>::default()
.with_collators(collator_session_key.collators())
.with_session_keys(collator_session_key.session_keys())
.with_para_id(runtime_para_id.into())
.with_tracing()
.build()
.execute_with(|| {
let (storage_constant_key, storage_constant_init_value): (
Vec<u8>,
StorageConstantType,
) = storage_constant_key_value();
// check delivery reward constant before (not stored yet, just as default value is used)
assert_eq!(StorageConstant::get(), storage_constant_init_value);
assert_eq!(sp_io::storage::get(&storage_constant_key), None);
let new_storage_constant_value =
new_storage_constant_value(&storage_constant_init_value);
assert_ne!(new_storage_constant_value, storage_constant_init_value);
// encode `set_storage` call
let set_storage_call =
runtime_call_encode(frame_system::Call::<Runtime>::set_storage {
items: vec![(
storage_constant_key.clone(),
new_storage_constant_value.encode(),
)],
});
// estimate - storing just 1 value
use frame_system::WeightInfo;
let require_weight_at_most =
<Runtime as frame_system::Config>::SystemWeightInfo::set_storage(1);
// execute XCM with Transact to `set_storage` as governance does
assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance(
set_storage_call,
require_weight_at_most
)
.ensure_complete());
// check delivery reward constant after (stored)
assert_eq!(StorageConstant::get(), new_storage_constant_value);
assert_eq!(
sp_io::storage::get(&storage_constant_key),
Some(new_storage_constant_value.encode().into())
);
})
}
/// Test-case makes sure that `Runtime` can handle xcm `ExportMessage`: /// Test-case makes sure that `Runtime` can handle xcm `ExportMessage`:
/// Checks if received XCM messages is correctly added to the message outbound queue for delivery. /// Checks if received XCM messages is correctly added to the message outbound queue for delivery.
/// For SystemParachains we expect unpaid execution. /// For SystemParachains we expect unpaid execution.
@@ -37,6 +37,8 @@ use xcm::{
}; };
use xcm_executor::{traits::TransactAsset, Assets}; use xcm_executor::{traits::TransactAsset, Assets};
pub mod test_cases;
pub type BalanceOf<Runtime> = <Runtime as pallet_balances::Config>::Balance; pub type BalanceOf<Runtime> = <Runtime as pallet_balances::Config>::Balance;
pub type AccountIdOf<Runtime> = <Runtime as frame_system::Config>::AccountId; pub type AccountIdOf<Runtime> = <Runtime as frame_system::Config>::AccountId;
pub type ValidatorIdOf<Runtime> = <Runtime as pallet_session::Config>::ValidatorId; pub type ValidatorIdOf<Runtime> = <Runtime as pallet_session::Config>::ValidatorId;
@@ -0,0 +1,91 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Cumulus is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Cumulus is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
//! Module contains predefined test-case scenarios for `Runtime` with common functionality.
use crate::{AccountIdOf, CollatorSessionKeys, ExtBuilder, RuntimeHelper, ValidatorIdOf};
use codec::Encode;
use frame_support::{assert_ok, traits::Get};
/// Test-case makes sure that `Runtime` can change storage constant via governance-like call
pub fn change_storage_constant_by_governance_works<Runtime, StorageConstant, StorageConstantType>(
collator_session_key: CollatorSessionKeys<Runtime>,
runtime_para_id: u32,
runtime_call_encode: Box<dyn Fn(frame_system::Call<Runtime>) -> Vec<u8>>,
storage_constant_key_value: fn() -> (Vec<u8>, StorageConstantType),
new_storage_constant_value: fn(&StorageConstantType) -> StorageConstantType,
) where
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config
+ pallet_collator_selection::Config
+ cumulus_pallet_dmp_queue::Config
+ cumulus_pallet_parachain_system::Config,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
StorageConstant: Get<StorageConstantType>,
StorageConstantType: Encode + PartialEq + std::fmt::Debug,
{
ExtBuilder::<Runtime>::default()
.with_collators(collator_session_key.collators())
.with_session_keys(collator_session_key.session_keys())
.with_para_id(runtime_para_id.into())
.with_tracing()
.build()
.execute_with(|| {
let (storage_constant_key, storage_constant_init_value): (
Vec<u8>,
StorageConstantType,
) = storage_constant_key_value();
// check delivery reward constant before (not stored yet, just as default value is used)
assert_eq!(StorageConstant::get(), storage_constant_init_value);
assert_eq!(sp_io::storage::get(&storage_constant_key), None);
let new_storage_constant_value =
new_storage_constant_value(&storage_constant_init_value);
assert_ne!(new_storage_constant_value, storage_constant_init_value);
// encode `set_storage` call
let set_storage_call =
runtime_call_encode(frame_system::Call::<Runtime>::set_storage {
items: vec![(
storage_constant_key.clone(),
new_storage_constant_value.encode(),
)],
});
// estimate - storing just 1 value
use frame_system::WeightInfo;
let require_weight_at_most =
<Runtime as frame_system::Config>::SystemWeightInfo::set_storage(1);
// execute XCM with Transact to `set_storage` as governance does
assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance(
set_storage_call,
require_weight_at_most
)
.ensure_complete());
// check delivery reward constant after (stored)
assert_eq!(StorageConstant::get(), new_storage_constant_value);
assert_eq!(
sp_io::storage::get(&storage_constant_key),
Some(new_storage_constant_value.encode().into())
);
})
}
+2 -2
View File
@@ -1100,8 +1100,8 @@ pub mod helpers {
pub fn within_threshold(threshold: u64, expected_value: u64, current_value: u64) -> bool { pub fn within_threshold(threshold: u64, expected_value: u64, current_value: u64) -> bool {
let margin = (current_value * threshold) / 100; let margin = (current_value * threshold) / 100;
let lower_limit = expected_value - margin; let lower_limit = expected_value.checked_sub(margin).unwrap_or(u64::MIN);
let upper_limit = expected_value + margin; let upper_limit = expected_value.checked_add(margin).unwrap_or(u64::MAX);
current_value >= lower_limit && current_value <= upper_limit current_value >= lower_limit && current_value <= upper_limit
} }