diff --git a/Cargo.lock b/Cargo.lock index 6e5f639d006..3785d10c65a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -342,6 +342,21 @@ dependencies = [ "substrate-wasm-builder", ] +[[package]] +name = "assets-common" +version = "0.1.0" +dependencies = [ + "frame-support", + "parachains-common", + "parity-scale-codec", + "sp-api", + "sp-std", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + [[package]] name = "async-io" version = "1.6.0" @@ -12238,6 +12253,7 @@ name = "statemine-runtime" version = "2.0.0" dependencies = [ "asset-test-utils", + "assets-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -12304,6 +12320,7 @@ name = "statemint-runtime" version = "1.0.0" dependencies = [ "asset-test-utils", + "assets-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -14280,6 +14297,7 @@ name = "westmint-runtime" version = "1.0.0" dependencies = [ "asset-test-utils", + "assets-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", diff --git a/Cargo.toml b/Cargo.toml index a4b817ffe87..acb6ae62f72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ members = [ "parachains/runtimes/testing/rococo-parachain", "parachains/runtimes/starters/shell", "parachains/runtimes/starters/seedling", + "parachains/runtimes/assets/common", "parachains/runtimes/assets/statemint", "parachains/runtimes/assets/statemine", "parachains/runtimes/assets/westmint", diff --git a/parachains/runtimes/assets/common/Cargo.toml b/parachains/runtimes/assets/common/Cargo.toml new file mode 100644 index 00000000000..6f239c8715b --- /dev/null +++ b/parachains/runtimes/assets/common/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "assets-common" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +description = "Assets common utilities" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } + +# Substrate +frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } + +# Polkadot +xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } + +# Cumulus +parachains-common = { path = "../../../common", default-features = false } + +#[dev-dependencies] + +[build-dependencies] +substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = [ "std" ] +std = [ + "codec/std", + "frame-support/std", + "parachains-common/std", + "sp-api/std", + "sp-std/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", +] diff --git a/parachains/runtimes/assets/statemine/src/assets_api.rs b/parachains/runtimes/assets/common/src/assets_api.rs similarity index 72% rename from parachains/runtimes/assets/statemine/src/assets_api.rs rename to parachains/runtimes/assets/common/src/assets_api.rs index cf1a663d703..05e69e2546d 100644 --- a/parachains/runtimes/assets/statemine/src/assets_api.rs +++ b/parachains/runtimes/assets/common/src/assets_api.rs @@ -18,9 +18,17 @@ //! Runtime API definition for assets. -use codec::Codec; +use codec::{Codec, Decode, Encode}; +use frame_support::RuntimeDebug; use sp_std::vec::Vec; +/// The possible errors that can happen querying the storage of assets. +#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)] +pub enum AssetsAccessError { + /// `MultiLocation` to `AssetId`/`ClassId` conversion failed. + AssetIdConversionFailed, +} + sp_api::decl_runtime_apis! { pub trait AssetsApi where @@ -29,6 +37,6 @@ sp_api::decl_runtime_apis! { AssetId: Codec, { /// Returns the list of `AssetId`s and corresponding balance that an `AccountId` has. - fn account_balances(account: AccountId) -> Vec<(AssetId, AssetBalance)>; + fn account_balances(account: AccountId) -> Result, AssetsAccessError>; } } diff --git a/parachains/runtimes/assets/common/src/lib.rs b/parachains/runtimes/assets/common/src/lib.rs new file mode 100644 index 00000000000..4ed04c73118 --- /dev/null +++ b/parachains/runtimes/assets/common/src/lib.rs @@ -0,0 +1,78 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod assets_api; + +use parachains_common::AssetIdForTrustBackedAssets; +use sp_std::prelude::Vec; +use xcm::latest::MultiLocation; +use xcm_builder::AsPrefixedGeneralIndex; +use xcm_executor::traits::{Convert, JustTry}; + +/// [`MultiLocation`] converter for [`TrustBackedAssets`]'s [`AssetIdForTrustBackedAssets`] +pub type AssetIdForTrustBackedAssetsConvert = + AsPrefixedGeneralIndex; + +/// Helper function to convert collections with (`AssetId`, 'Balance') to (`MultiLocation`, 'Balance') +pub fn convert_asset_id( + assets_balances: Vec<(AssetId, Balance)>, +) -> Result, assets_api::AssetsAccessError> +where + ConvertAssetId: Convert, +{ + assets_balances + .into_iter() + .map(|(asset_id, balance)| { + ConvertAssetId::reverse_ref(asset_id) + .map(|converted_asset_id| (converted_asset_id, balance)) + .map_err(|_| assets_api::AssetsAccessError::AssetIdConversionFailed) + }) + .collect() +} + +#[cfg(test)] +mod tests { + + use super::*; + use xcm::latest::prelude::*; + + frame_support::parameter_types! { + pub TrustBackedAssetsPalletLocation: MultiLocation = MultiLocation::new(5, X1(PalletInstance(13))); + } + + #[test] + fn asset_id_for_trust_backed_assets_convert_works() { + let local_asset_id = 123456789 as AssetIdForTrustBackedAssets; + let expected_reverse_ref = + MultiLocation::new(5, X2(PalletInstance(13), GeneralIndex(local_asset_id.into()))); + + assert_eq!( + AssetIdForTrustBackedAssetsConvert::::reverse_ref( + local_asset_id + ) + .unwrap(), + expected_reverse_ref + ); + assert_eq!( + AssetIdForTrustBackedAssetsConvert::::convert_ref( + expected_reverse_ref + ) + .unwrap(), + local_asset_id + ); + } +} diff --git a/parachains/runtimes/assets/statemine/Cargo.toml b/parachains/runtimes/assets/statemine/Cargo.toml index 7ec11cf5786..0f0921976a8 100644 --- a/parachains/runtimes/assets/statemine/Cargo.toml +++ b/parachains/runtimes/assets/statemine/Cargo.toml @@ -49,6 +49,7 @@ pallet-state-trie-migration = { git = "https://github.com/paritytech/substrate", # Polkadot kusama-runtime-constants = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +pallet-xcm-benchmarks = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false, optional = true } polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } @@ -69,8 +70,7 @@ cumulus-primitives-utility = { path = "../../../../primitives/utility", default- pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } -pallet-xcm-benchmarks = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false, optional = true } - +assets-common = { path = "../common", default-features = false } [dev-dependencies] asset-test-utils = { path = "../test-utils"} @@ -187,4 +187,5 @@ std = [ "pallet-collator-selection/std", "parachain-info/std", "parachains-common/std", + "assets-common/std", ] diff --git a/parachains/runtimes/assets/statemine/src/lib.rs b/parachains/runtimes/assets/statemine/src/lib.rs index 8a2d9c8843d..6e8702ff11f 100644 --- a/parachains/runtimes/assets/statemine/src/lib.rs +++ b/parachains/runtimes/assets/statemine/src/lib.rs @@ -24,7 +24,6 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -pub mod assets_api; pub mod constants; mod weights; pub mod xcm_config; @@ -65,7 +64,7 @@ use parachains_common::{ Index, Signature, AVERAGE_ON_INITIALIZE_RATIO, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, }; -use xcm_config::{KsmLocation, XcmConfig}; +use xcm_config::{AssetIdForTrustBackedAssetsConvert, KsmLocation, XcmConfig}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -832,15 +831,17 @@ impl_runtime_apis! { } } - impl assets_api::AssetsApi< + impl assets_common::assets_api::AssetsApi< Block, AccountId, Balance, - AssetIdForTrustBackedAssets, + xcm::latest::MultiLocation, > for Runtime { - fn account_balances(account: AccountId) -> Vec<(AssetIdForTrustBackedAssets, Balance)> { - Assets::account_balances(account) + fn account_balances(account: AccountId) -> Result, assets_common::assets_api::AssetsAccessError> { + Ok([ + assets_common::convert_asset_id::<_, _, AssetIdForTrustBackedAssetsConvert>(Assets::account_balances(account))?, + ].concat()) } } diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 43b1c67f0bd..6038560a5ce 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -34,12 +34,11 @@ use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, - AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, - NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, - WithComputedOrigin, + AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, CurrencyAdapter, + EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, ParentAsSuperuser, + ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + UsingComponents, WeightInfoBounds, WithComputedOrigin, }; use xcm_executor::{ traits::{JustTry, WithOriginFilter}, @@ -84,6 +83,10 @@ pub type CurrencyTransactor = CurrencyAdapter< (), >; +/// `AssetId` converter for [`AssetIdForTrustBackedAssets`] +pub type AssetIdForTrustBackedAssetsConvert = + assets_common::AssetIdForTrustBackedAssetsConvert; + /// Means for transacting assets besides the native currency on this chain. pub type FungiblesTransactor = FungiblesAdapter< // Use this fungibles implementation: @@ -92,11 +95,7 @@ pub type FungiblesTransactor = FungiblesAdapter< ConvertedConcreteId< AssetIdForTrustBackedAssets, Balance, - AsPrefixedGeneralIndex< - TrustBackedAssetsPalletLocation, - AssetIdForTrustBackedAssets, - JustTry, - >, + AssetIdForTrustBackedAssetsConvert, JustTry, >, // Convert an XCM MultiLocation into a local account id: @@ -305,11 +304,7 @@ impl xcm_executor::Config for XcmConfig { ConvertedConcreteId< AssetIdForTrustBackedAssets, Balance, - AsPrefixedGeneralIndex< - TrustBackedAssetsPalletLocation, - AssetIdForTrustBackedAssets, - JustTry, - >, + AssetIdForTrustBackedAssetsConvert, JustTry, >, Assets, diff --git a/parachains/runtimes/assets/statemine/tests/tests.rs b/parachains/runtimes/assets/statemine/tests/tests.rs index 9614fd80062..610f7e75723 100644 --- a/parachains/runtimes/assets/statemine/tests/tests.rs +++ b/parachains/runtimes/assets/statemine/tests/tests.rs @@ -2,17 +2,18 @@ use asset_test_utils::{ExtBuilder, RuntimeHelper}; use cumulus_primitives_utility::ChargeWeightInFungibles; use frame_support::{ assert_noop, assert_ok, - traits::PalletInfo, weights::{Weight, WeightToFee as WeightToFeeT}, }; use parachains_common::{AccountId, AuraId}; -use statemine_runtime::xcm_config::AssetFeeAsExistentialDepositMultiplierFeeCharger; +use statemine_runtime::xcm_config::{ + AssetFeeAsExistentialDepositMultiplierFeeCharger, AssetIdForTrustBackedAssetsConvert, +}; pub use statemine_runtime::{ constants::fee::WeightToFee, xcm_config::XcmConfig, Assets, Balances, ExistentialDeposit, Runtime, SessionKeys, System, }; use xcm::latest::prelude::*; -use xcm_executor::traits::WeightTrader; +use xcm_executor::traits::{Convert, WeightTrader}; pub const ALICE: [u8; 32] = [1u8; 32]; @@ -47,16 +48,8 @@ fn test_asset_xcm_trader() { )); // get asset id as multilocation - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(local_asset_id.into()), - ), - ); + let asset_multilocation = + AssetIdForTrustBackedAssetsConvert::reverse_ref(local_asset_id).unwrap(); // Set Alice as block author, who will receive fees RuntimeHelper::::run_to_block(2, Some(AccountId::from(ALICE))); @@ -94,12 +87,15 @@ fn test_asset_xcm_trader() { // Make sure author(Alice) has received the amount assert_eq!( - Assets::balance(1, AccountId::from(ALICE)), + Assets::balance(local_asset_id, AccountId::from(ALICE)), minimum_asset_balance + asset_amount_needed ); // We also need to ensure the total supply increased - assert_eq!(Assets::total_supply(1), minimum_asset_balance + asset_amount_needed); + assert_eq!( + Assets::total_supply(local_asset_id), + minimum_asset_balance + asset_amount_needed + ); }); } @@ -140,16 +136,7 @@ fn test_asset_xcm_trader_with_refund() { // We are going to buy 4e9 weight let bought = Weight::from_ref_time(4_000_000_000u64); - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(1), - ), - ); + let asset_multilocation = AssetIdForTrustBackedAssetsConvert::reverse_ref(1).unwrap(); // lets calculate amount needed let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -218,16 +205,7 @@ fn test_asset_xcm_trader_refund_not_possible_since_amount_less_than_ed() { // We are going to buy small amount let bought = Weight::from_ref_time(500_000_000u64); - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(1), - ), - ); + let asset_multilocation = AssetIdForTrustBackedAssetsConvert::reverse_ref(1).unwrap(); let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -278,16 +256,7 @@ fn test_that_buying_ed_refund_does_not_refund() { // We are gonna buy ED let bought = Weight::from_ref_time(ExistentialDeposit::get().try_into().unwrap()); - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(1), - ), - ); + let asset_multilocation = AssetIdForTrustBackedAssetsConvert::reverse_ref(1).unwrap(); let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -362,16 +331,7 @@ fn test_asset_xcm_trader_not_possible_for_non_sufficient_assets() { // lets calculate amount needed let asset_amount_needed = WeightToFee::weight_to_fee(&bought); - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(1), - ), - ); + let asset_multilocation = AssetIdForTrustBackedAssetsConvert::reverse_ref(1).unwrap(); let asset: MultiAsset = (asset_multilocation, asset_amount_needed).into(); @@ -388,3 +348,54 @@ fn test_asset_xcm_trader_not_possible_for_non_sufficient_assets() { assert_eq!(Assets::total_supply(1), minimum_asset_balance); }); } + +#[test] +fn test_assets_balances_api_works() { + use assets_common::assets_api::runtime_decl_for_AssetsApi::AssetsApi; + + ExtBuilder::::default() + .with_collators(vec![AccountId::from(ALICE)]) + .with_session_keys(vec![( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, + )]) + .build() + .execute_with(|| { + let local_asset_id = 1; + + // check before + assert_eq!(Assets::balance(local_asset_id, AccountId::from(ALICE)), 0); + assert!(Runtime::account_balances(AccountId::from(ALICE)).unwrap().is_empty()); + + // We need root origin to create a sufficient asset + let minimum_asset_balance = 3333333_u128; + assert_ok!(Assets::force_create( + RuntimeHelper::::root_origin(), + local_asset_id.into(), + AccountId::from(ALICE).into(), + true, + minimum_asset_balance + )); + + // We first mint enough asset for the account to exist for assets + assert_ok!(Assets::mint( + RuntimeHelper::::origin_of(AccountId::from(ALICE)), + local_asset_id.into(), + AccountId::from(ALICE).into(), + minimum_asset_balance + )); + + // check after + assert_eq!( + Assets::balance(local_asset_id, AccountId::from(ALICE)), + minimum_asset_balance + ); + let result = Runtime::account_balances(AccountId::from(ALICE)).unwrap(); + assert_eq!(result.len(), 1); + assert!(result.iter().any(|asset| asset.eq(&( + AssetIdForTrustBackedAssetsConvert::reverse_ref(local_asset_id).unwrap(), + minimum_asset_balance + )))); + }); +} diff --git a/parachains/runtimes/assets/statemint/Cargo.toml b/parachains/runtimes/assets/statemint/Cargo.toml index 08d3f689152..f6d54cbd85f 100644 --- a/parachains/runtimes/assets/statemint/Cargo.toml +++ b/parachains/runtimes/assets/statemint/Cargo.toml @@ -47,6 +47,7 @@ sp-version = { git = "https://github.com/paritytech/substrate", default-features # Polkadot pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +pallet-xcm-benchmarks = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false, optional = true } polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } @@ -68,7 +69,7 @@ cumulus-primitives-utility = { path = "../../../../primitives/utility", default- pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } -pallet-xcm-benchmarks = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false, optional = true } +assets-common = { path = "../common", default-features = false } [dev-dependencies] hex-literal = "0.3.4" @@ -176,4 +177,5 @@ std = [ "pallet-collator-selection/std", "parachain-info/std", "parachains-common/std", + "assets-common/std", ] diff --git a/parachains/runtimes/assets/statemint/src/assets_api.rs b/parachains/runtimes/assets/statemint/src/assets_api.rs deleted file mode 100644 index cf1a663d703..00000000000 --- a/parachains/runtimes/assets/statemint/src/assets_api.rs +++ /dev/null @@ -1,34 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -//! Runtime API definition for assets. - -use codec::Codec; -use sp_std::vec::Vec; - -sp_api::decl_runtime_apis! { - pub trait AssetsApi - where - AccountId: Codec, - AssetBalance: Codec, - AssetId: Codec, - { - /// Returns the list of `AssetId`s and corresponding balance that an `AccountId` has. - fn account_balances(account: AccountId) -> Vec<(AssetId, AssetBalance)>; - } -} diff --git a/parachains/runtimes/assets/statemint/src/lib.rs b/parachains/runtimes/assets/statemint/src/lib.rs index 683e98d7b71..23bdb8ac669 100644 --- a/parachains/runtimes/assets/statemint/src/lib.rs +++ b/parachains/runtimes/assets/statemint/src/lib.rs @@ -53,7 +53,6 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -pub mod assets_api; pub mod constants; mod weights; pub mod xcm_config; @@ -94,7 +93,9 @@ use parachains_common::{ Signature, StatemintAuraId as AuraId, AVERAGE_ON_INITIALIZE_RATIO, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, }; -use xcm_config::{DotLocation, XcmConfig, XcmOriginToTransactDispatchOrigin}; +use xcm_config::{ + AssetIdForTrustBackedAssetsConvert, DotLocation, XcmConfig, XcmOriginToTransactDispatchOrigin, +}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -829,15 +830,17 @@ impl_runtime_apis! { } } - impl assets_api::AssetsApi< + impl assets_common::assets_api::AssetsApi< Block, AccountId, Balance, - AssetIdForTrustBackedAssets, + xcm::latest::MultiLocation, > for Runtime { - fn account_balances(account: AccountId) -> Vec<(AssetIdForTrustBackedAssets, Balance)> { - Assets::account_balances(account) + fn account_balances(account: AccountId) -> Result, assets_common::assets_api::AssetsAccessError> { + Ok([ + assets_common::convert_asset_id::<_, _, AssetIdForTrustBackedAssetsConvert>(Assets::account_balances(account))?, + ].concat()) } } diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index 21daa980cc5..23358bc34c2 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -34,12 +34,11 @@ use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, - AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, - NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, - WithComputedOrigin, + AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, CurrencyAdapter, + EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, ParentAsSuperuser, + ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + UsingComponents, WeightInfoBounds, WithComputedOrigin, }; use xcm_executor::{ traits::{JustTry, WithOriginFilter}, @@ -84,6 +83,10 @@ pub type CurrencyTransactor = CurrencyAdapter< (), >; +/// `AssetId` converter for [`AssetIdForTrustBackedAssets`] +pub type AssetIdForTrustBackedAssetsConvert = + assets_common::AssetIdForTrustBackedAssetsConvert; + /// Means for transacting assets besides the native currency on this chain. pub type FungiblesTransactor = FungiblesAdapter< // Use this fungibles implementation: @@ -92,11 +95,7 @@ pub type FungiblesTransactor = FungiblesAdapter< ConvertedConcreteId< AssetIdForTrustBackedAssets, Balance, - AsPrefixedGeneralIndex< - TrustBackedAssetsPalletLocation, - AssetIdForTrustBackedAssets, - JustTry, - >, + AssetIdForTrustBackedAssetsConvert, JustTry, >, // Convert an XCM MultiLocation into a local account id: @@ -305,11 +304,7 @@ impl xcm_executor::Config for XcmConfig { ConvertedConcreteId< AssetIdForTrustBackedAssets, Balance, - AsPrefixedGeneralIndex< - TrustBackedAssetsPalletLocation, - AssetIdForTrustBackedAssets, - JustTry, - >, + AssetIdForTrustBackedAssetsConvert, JustTry, >, Assets, diff --git a/parachains/runtimes/assets/statemint/tests/tests.rs b/parachains/runtimes/assets/statemint/tests/tests.rs index b52383f3d3e..33028db5891 100644 --- a/parachains/runtimes/assets/statemint/tests/tests.rs +++ b/parachains/runtimes/assets/statemint/tests/tests.rs @@ -2,17 +2,18 @@ use asset_test_utils::{ExtBuilder, RuntimeHelper}; use cumulus_primitives_utility::ChargeWeightInFungibles; use frame_support::{ assert_noop, assert_ok, - traits::PalletInfo, weights::{Weight, WeightToFee as WeightToFeeT}, }; use parachains_common::{AccountId, StatemintAuraId as AuraId}; -use statemint_runtime::xcm_config::AssetFeeAsExistentialDepositMultiplierFeeCharger; +use statemint_runtime::xcm_config::{ + AssetFeeAsExistentialDepositMultiplierFeeCharger, AssetIdForTrustBackedAssetsConvert, +}; pub use statemint_runtime::{ constants::fee::WeightToFee, xcm_config::XcmConfig, Assets, Balances, ExistentialDeposit, Runtime, SessionKeys, System, }; use xcm::latest::prelude::*; -use xcm_executor::traits::WeightTrader; +use xcm_executor::traits::{Convert, WeightTrader}; pub const ALICE: [u8; 32] = [1u8; 32]; @@ -47,16 +48,8 @@ fn test_asset_xcm_trader() { )); // get asset id as multilocation - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(local_asset_id.into()), - ), - ); + let asset_multilocation = + AssetIdForTrustBackedAssetsConvert::reverse_ref(local_asset_id).unwrap(); // Set Alice as block author, who will receive fees RuntimeHelper::::run_to_block(2, Some(AccountId::from(ALICE))); @@ -97,12 +90,15 @@ fn test_asset_xcm_trader() { // Make sure author(Alice) has received the amount assert_eq!( - Assets::balance(1, AccountId::from(ALICE)), + Assets::balance(local_asset_id, AccountId::from(ALICE)), minimum_asset_balance + asset_amount_needed ); // We also need to ensure the total supply increased - assert_eq!(Assets::total_supply(1), minimum_asset_balance + asset_amount_needed); + assert_eq!( + Assets::total_supply(local_asset_id), + minimum_asset_balance + asset_amount_needed + ); }); } @@ -146,16 +142,7 @@ fn test_asset_xcm_trader_with_refund() { // bit more of weight let bought = Weight::from_ref_time(400_000_000_000u64); - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(1), - ), - ); + let asset_multilocation = AssetIdForTrustBackedAssetsConvert::reverse_ref(1).unwrap(); // lets calculate amount needed let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -227,16 +214,7 @@ fn test_asset_xcm_trader_refund_not_possible_since_amount_less_than_ed() { // bit more of weight let bought = Weight::from_ref_time(50_000_000_000u64); - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(1), - ), - ); + let asset_multilocation = AssetIdForTrustBackedAssetsConvert::reverse_ref(1).unwrap(); let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -287,16 +265,7 @@ fn test_that_buying_ed_refund_does_not_refund() { // We are gonna buy ED let bought = Weight::from_ref_time(ExistentialDeposit::get().try_into().unwrap()); - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(1), - ), - ); + let asset_multilocation = AssetIdForTrustBackedAssetsConvert::reverse_ref(1).unwrap(); let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -374,16 +343,7 @@ fn test_asset_xcm_trader_not_possible_for_non_sufficient_assets() { // lets calculate amount needed let asset_amount_needed = WeightToFee::weight_to_fee(&bought); - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(1), - ), - ); + let asset_multilocation = AssetIdForTrustBackedAssetsConvert::reverse_ref(1).unwrap(); let asset: MultiAsset = (asset_multilocation, asset_amount_needed).into(); @@ -400,3 +360,54 @@ fn test_asset_xcm_trader_not_possible_for_non_sufficient_assets() { assert_eq!(Assets::total_supply(1), minimum_asset_balance); }); } + +#[test] +fn test_assets_balances_api_works() { + use assets_common::assets_api::runtime_decl_for_AssetsApi::AssetsApi; + + ExtBuilder::::default() + .with_collators(vec![AccountId::from(ALICE)]) + .with_session_keys(vec![( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::ed25519::Public::from_raw(ALICE)) }, + )]) + .build() + .execute_with(|| { + let local_asset_id = 1; + + // check before + assert_eq!(Assets::balance(local_asset_id, AccountId::from(ALICE)), 0); + assert!(Runtime::account_balances(AccountId::from(ALICE)).unwrap().is_empty()); + + // We need root origin to create a sufficient asset + let minimum_asset_balance = 3333333_u128; + assert_ok!(Assets::force_create( + RuntimeHelper::::root_origin(), + local_asset_id.into(), + AccountId::from(ALICE).into(), + true, + minimum_asset_balance + )); + + // We first mint enough asset for the account to exist for assets + assert_ok!(Assets::mint( + RuntimeHelper::::origin_of(AccountId::from(ALICE)), + local_asset_id.into(), + AccountId::from(ALICE).into(), + minimum_asset_balance + )); + + // check after + assert_eq!( + Assets::balance(local_asset_id, AccountId::from(ALICE)), + minimum_asset_balance + ); + let result = Runtime::account_balances(AccountId::from(ALICE)).unwrap(); + assert_eq!(result.len(), 1); + assert!(result.iter().any(|asset| asset.eq(&( + AssetIdForTrustBackedAssetsConvert::reverse_ref(local_asset_id).unwrap(), + minimum_asset_balance + )))); + }); +} diff --git a/parachains/runtimes/assets/westmint/Cargo.toml b/parachains/runtimes/assets/westmint/Cargo.toml index 9bfcba6b237..e5a6e30939b 100644 --- a/parachains/runtimes/assets/westmint/Cargo.toml +++ b/parachains/runtimes/assets/westmint/Cargo.toml @@ -48,6 +48,7 @@ sp-version = { git = "https://github.com/paritytech/substrate", default-features # Polkadot pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +pallet-xcm-benchmarks = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false, optional = true } polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } @@ -69,7 +70,7 @@ cumulus-primitives-utility = { path = "../../../../primitives/utility", default- pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } -pallet-xcm-benchmarks = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false, optional = true } +assets-common = { path = "../common", default-features = false } [dev-dependencies] hex-literal = "0.3.4" @@ -180,4 +181,5 @@ std = [ "pallet-collator-selection/std", "parachain-info/std", "parachains-common/std", + "assets-common/std", ] diff --git a/parachains/runtimes/assets/westmint/src/assets_api.rs b/parachains/runtimes/assets/westmint/src/assets_api.rs deleted file mode 100644 index cf1a663d703..00000000000 --- a/parachains/runtimes/assets/westmint/src/assets_api.rs +++ /dev/null @@ -1,34 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . - -//! Runtime API definition for assets. - -use codec::Codec; -use sp_std::vec::Vec; - -sp_api::decl_runtime_apis! { - pub trait AssetsApi - where - AccountId: Codec, - AssetBalance: Codec, - AssetId: Codec, - { - /// Returns the list of `AssetId`s and corresponding balance that an `AccountId` has. - fn account_balances(account: AccountId) -> Vec<(AssetId, AssetBalance)>; - } -} diff --git a/parachains/runtimes/assets/westmint/src/lib.rs b/parachains/runtimes/assets/westmint/src/lib.rs index 72c656f68e6..2084a9771f0 100644 --- a/parachains/runtimes/assets/westmint/src/lib.rs +++ b/parachains/runtimes/assets/westmint/src/lib.rs @@ -24,7 +24,6 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -pub mod assets_api; pub mod constants; mod weights; pub mod xcm_config; @@ -66,7 +65,9 @@ use parachains_common::{ Index, Signature, AVERAGE_ON_INITIALIZE_RATIO, DAYS, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, }; -use xcm_config::{XcmConfig, XcmOriginToTransactDispatchOrigin}; +use xcm_config::{ + AssetIdForTrustBackedAssetsConvert, XcmConfig, XcmOriginToTransactDispatchOrigin, +}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -841,15 +842,17 @@ impl_runtime_apis! { } } - impl assets_api::AssetsApi< + impl assets_common::assets_api::AssetsApi< Block, AccountId, Balance, - AssetIdForTrustBackedAssets, + xcm::latest::MultiLocation, > for Runtime { - fn account_balances(account: AccountId) -> Vec<(AssetIdForTrustBackedAssets, Balance)> { - Assets::account_balances(account) + fn account_balances(account: AccountId) -> Result, assets_common::assets_api::AssetsAccessError> { + Ok([ + assets_common::convert_asset_id::<_, _, AssetIdForTrustBackedAssetsConvert>(Assets::account_balances(account))?, + ].concat()) } } diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index fd2e349a2c3..3e94e3ab161 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -34,12 +34,11 @@ use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, - AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, - NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, - WithComputedOrigin, + AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, CurrencyAdapter, + EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, ParentAsSuperuser, + ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + UsingComponents, WeightInfoBounds, WithComputedOrigin, }; use xcm_executor::{ traits::{JustTry, WithOriginFilter}, @@ -84,6 +83,10 @@ pub type CurrencyTransactor = CurrencyAdapter< (), >; +/// `AssetId` converter for [`AssetIdForTrustBackedAssets`] +pub type AssetIdForTrustBackedAssetsConvert = + assets_common::AssetIdForTrustBackedAssetsConvert; + /// Means for transacting assets besides the native currency on this chain. pub type FungiblesTransactor = FungiblesAdapter< // Use this fungibles implementation: @@ -92,11 +95,7 @@ pub type FungiblesTransactor = FungiblesAdapter< ConvertedConcreteId< AssetIdForTrustBackedAssets, Balance, - AsPrefixedGeneralIndex< - TrustBackedAssetsPalletLocation, - AssetIdForTrustBackedAssets, - JustTry, - >, + AssetIdForTrustBackedAssetsConvert, JustTry, >, // Convert an XCM MultiLocation into a local account id: @@ -300,11 +299,7 @@ impl xcm_executor::Config for XcmConfig { ConvertedConcreteId< AssetIdForTrustBackedAssets, Balance, - AsPrefixedGeneralIndex< - TrustBackedAssetsPalletLocation, - AssetIdForTrustBackedAssets, - JustTry, - >, + AssetIdForTrustBackedAssetsConvert, JustTry, >, Assets, diff --git a/parachains/runtimes/assets/westmint/tests/tests.rs b/parachains/runtimes/assets/westmint/tests/tests.rs index 68c6cf670d5..119964bdabc 100644 --- a/parachains/runtimes/assets/westmint/tests/tests.rs +++ b/parachains/runtimes/assets/westmint/tests/tests.rs @@ -2,17 +2,18 @@ use asset_test_utils::{ExtBuilder, RuntimeHelper}; use cumulus_primitives_utility::ChargeWeightInFungibles; use frame_support::{ assert_noop, assert_ok, - traits::PalletInfo, weights::{Weight, WeightToFee as WeightToFeeT}, }; use parachains_common::{AccountId, AuraId}; -use westmint_runtime::xcm_config::AssetFeeAsExistentialDepositMultiplierFeeCharger; +use westmint_runtime::xcm_config::{ + AssetFeeAsExistentialDepositMultiplierFeeCharger, AssetIdForTrustBackedAssetsConvert, +}; pub use westmint_runtime::{ constants::fee::WeightToFee, xcm_config::XcmConfig, Assets, Balances, ExistentialDeposit, Runtime, SessionKeys, System, }; use xcm::latest::prelude::*; -use xcm_executor::traits::WeightTrader; +use xcm_executor::traits::{Convert, WeightTrader}; pub const ALICE: [u8; 32] = [1u8; 32]; @@ -47,16 +48,8 @@ fn test_asset_xcm_trader() { )); // get asset id as multilocation - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(local_asset_id.into()), - ), - ); + let asset_multilocation = + AssetIdForTrustBackedAssetsConvert::reverse_ref(local_asset_id).unwrap(); // Set Alice as block author, who will receive fees RuntimeHelper::::run_to_block(2, Some(AccountId::from(ALICE))); @@ -94,12 +87,15 @@ fn test_asset_xcm_trader() { // Make sure author(Alice) has received the amount assert_eq!( - Assets::balance(1, AccountId::from(ALICE)), + Assets::balance(local_asset_id, AccountId::from(ALICE)), minimum_asset_balance + asset_amount_needed ); // We also need to ensure the total supply increased - assert_eq!(Assets::total_supply(1), minimum_asset_balance + asset_amount_needed); + assert_eq!( + Assets::total_supply(local_asset_id), + minimum_asset_balance + asset_amount_needed + ); }); } @@ -139,16 +135,7 @@ fn test_asset_xcm_trader_with_refund() { // We are going to buy 4e9 weight let bought = Weight::from_ref_time(4_000_000_000u64); - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(1), - ), - ); + let asset_multilocation = AssetIdForTrustBackedAssetsConvert::reverse_ref(1).unwrap(); // lets calculate amount needed let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -217,16 +204,7 @@ fn test_asset_xcm_trader_refund_not_possible_since_amount_less_than_ed() { // We are going to buy 5e9 weight let bought = Weight::from_ref_time(500_000_000u64); - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(1), - ), - ); + let asset_multilocation = AssetIdForTrustBackedAssetsConvert::reverse_ref(1).unwrap(); let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -276,16 +254,7 @@ fn test_that_buying_ed_refund_does_not_refund() { let bought = Weight::from_ref_time(500_000_000u64); - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(1), - ), - ); + let asset_multilocation = AssetIdForTrustBackedAssetsConvert::reverse_ref(1).unwrap(); let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -360,16 +329,7 @@ fn test_asset_xcm_trader_not_possible_for_non_sufficient_assets() { // lets calculate amount needed let asset_amount_needed = WeightToFee::weight_to_fee(&bought); - let asset_multilocation = MultiLocation::new( - 0, - X2( - PalletInstance( - ::PalletInfo::index::().unwrap() - as u8, - ), - GeneralIndex(1), - ), - ); + let asset_multilocation = AssetIdForTrustBackedAssetsConvert::reverse_ref(1).unwrap(); let asset: MultiAsset = (asset_multilocation, asset_amount_needed).into(); @@ -386,3 +346,54 @@ fn test_asset_xcm_trader_not_possible_for_non_sufficient_assets() { assert_eq!(Assets::total_supply(1), minimum_asset_balance); }); } + +#[test] +fn test_assets_balances_api_works() { + use assets_common::assets_api::runtime_decl_for_AssetsApi::AssetsApi; + + ExtBuilder::::default() + .with_collators(vec![AccountId::from(ALICE)]) + .with_session_keys(vec![( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, + )]) + .build() + .execute_with(|| { + let local_asset_id = 1; + + // check before + assert_eq!(Assets::balance(local_asset_id, AccountId::from(ALICE)), 0); + assert!(Runtime::account_balances(AccountId::from(ALICE)).unwrap().is_empty()); + + // We need root origin to create a sufficient asset + let minimum_asset_balance = 3333333_u128; + assert_ok!(Assets::force_create( + RuntimeHelper::::root_origin(), + local_asset_id.into(), + AccountId::from(ALICE).into(), + true, + minimum_asset_balance + )); + + // We first mint enough asset for the account to exist for assets + assert_ok!(Assets::mint( + RuntimeHelper::::origin_of(AccountId::from(ALICE)), + local_asset_id.into(), + AccountId::from(ALICE).into(), + minimum_asset_balance + )); + + // check after + assert_eq!( + Assets::balance(local_asset_id, AccountId::from(ALICE)), + minimum_asset_balance + ); + let result = Runtime::account_balances(AccountId::from(ALICE)).unwrap(); + assert_eq!(result.len(), 1); + assert!(result.iter().any(|asset| asset.eq(&( + AssetIdForTrustBackedAssetsConvert::reverse_ref(local_asset_id).unwrap(), + minimum_asset_balance + )))); + }); +}