Generic asset manager (#341)

Co-authored-by: Nikita Khateev <nikita.khateev@gmail.com>
This commit is contained in:
Özgün Özerk
2024-10-25 13:27:39 +03:00
committed by GitHub
parent 033cf12f34
commit 46a9a3f4e8
10 changed files with 483 additions and 40 deletions
+6
View File
@@ -81,6 +81,9 @@ pallet-collator-selection = { workspace = true }
parachain-info = { workspace = true }
parachains-common = { workspace = true }
# Moonbeam
pallet-asset-manager = { workspace = true }
[dev-dependencies]
sp-io = { workspace = true }
@@ -108,6 +111,7 @@ std = [
"frame-system/std",
"frame-try-runtime?/std",
"log/std",
"pallet-asset-manager/std",
"pallet-assets/std",
"pallet-aura/std",
"pallet-authorship/std",
@@ -165,6 +169,7 @@ runtime-benchmarks = [
"frame-system-benchmarking/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"hex-literal",
"pallet-asset-manager/runtime-benchmarks",
"pallet-assets/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-collator-selection/runtime-benchmarks",
@@ -198,6 +203,7 @@ try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"frame-try-runtime/try-runtime",
"pallet-asset-manager/try-runtime",
"pallet-assets/try-runtime",
"pallet-aura/try-runtime",
"pallet-authorship/try-runtime",
+5
View File
@@ -234,6 +234,7 @@ impl_runtime_apis! {
use super::{*, types::*, configs::*, constants::currency::CENTS};
#[allow(non_local_definitions)]
impl frame_system_benchmarking::Config for Runtime {
fn setup_set_code_requirements(code: &sp_std::vec::Vec<u8>) -> Result<(), BenchmarkError> {
ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32);
@@ -262,6 +263,8 @@ impl_runtime_apis! {
>;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark;
use xcm::latest::prelude::{Asset, AssetId, Assets as AssetList, Fungible, Location, Parachain, Parent, ParentThen, PalletInstance, GeneralIndex};
#[allow(non_local_definitions)]
impl pallet_xcm::benchmarking::Config for Runtime {
type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper<
xcm_config::XcmConfig,
@@ -363,6 +366,8 @@ impl_runtime_apis! {
}
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
#[allow(non_local_definitions)]
impl cumulus_pallet_session_benchmarking::Config for Runtime {}
use frame_support::traits::WhitelistedStorageKeys;
@@ -0,0 +1,211 @@
use frame_support::{
dispatch::GetDispatchInfo, parameter_types, traits::AsEnsureOriginWithArg, weights::Weight,
};
use frame_system::{EnsureRoot, EnsureSigned};
use parity_scale_codec::{Compact, Decode, Encode};
use scale_info::TypeInfo;
use sp_core::H256;
use sp_runtime::traits::Hash as THash;
use sp_std::{
convert::{From, Into},
prelude::*,
};
use xcm::latest::Location;
use crate::{
constants::currency::{deposit, CENTS, MILLICENTS},
types::{AccountId, AssetId, Balance},
weights, AssetManager, Assets, Balances, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin,
};
parameter_types! {
pub const AssetDeposit: Balance = 10 * CENTS;
pub const AssetAccountDeposit: Balance = deposit(1, 16);
pub const ApprovalDeposit: Balance = MILLICENTS;
pub const StringLimit: u32 = 50;
pub const MetadataDepositBase: Balance = deposit(1, 68);
pub const MetadataDepositPerByte: Balance = deposit(0, 1);
pub const RemoveItemsLimit: u32 = 1000;
}
// Foreign assets
impl pallet_assets::Config for Runtime {
type ApprovalDeposit = ApprovalDeposit;
type AssetAccountDeposit = AssetAccountDeposit;
type AssetDeposit = AssetDeposit;
type AssetId = AssetId;
type AssetIdParameter = Compact<AssetId>;
type Balance = Balance;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
type CallbackHandle = ();
type CreateOrigin = AsEnsureOriginWithArg<EnsureSigned<AccountId>>;
type Currency = Balances;
type Extra = ();
type ForceOrigin = EnsureRoot<AccountId>;
type Freezer = ();
type MetadataDepositBase = MetadataDepositBase;
type MetadataDepositPerByte = MetadataDepositPerByte;
type RemoveItemsLimit = RemoveItemsLimit;
type RuntimeEvent = RuntimeEvent;
type StringLimit = StringLimit;
/// Rerun benchmarks if you are making changes to runtime configuration.
type WeightInfo = weights::pallet_assets::WeightInfo<Runtime>;
}
// Our AssetType. For now we only handle Xcm Assets
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
pub enum AssetType {
Xcm(xcm::v3::Location),
}
impl Default for AssetType {
fn default() -> Self {
Self::Xcm(xcm::v3::Location::here())
}
}
impl From<xcm::v3::Location> for AssetType {
fn from(location: xcm::v3::Location) -> Self {
Self::Xcm(location)
}
}
// This can be removed once we fully adopt xcm::v4 everywhere
impl TryFrom<Location> for AssetType {
type Error = ();
fn try_from(location: Location) -> Result<Self, Self::Error> {
Ok(Self::Xcm(location.try_into()?))
}
}
impl From<AssetType> for Option<xcm::v3::Location> {
fn from(val: AssetType) -> Self {
match val {
AssetType::Xcm(location) => Some(location),
}
}
}
// Implementation on how to retrieve the AssetId from an AssetType
impl From<AssetType> for AssetId {
fn from(asset: AssetType) -> AssetId {
match asset {
AssetType::Xcm(id) => {
let mut result: [u8; 4] = [0u8; 4];
let hash: H256 = id.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
result.copy_from_slice(&hash.as_fixed_bytes()[0..4]);
u32::from_le_bytes(result)
}
}
}
}
// We instruct how to register the Assets
// In this case, we tell it to Create an Asset in pallet-assets
pub struct AssetRegistrar;
use frame_support::{pallet_prelude::DispatchResult, transactional};
impl pallet_asset_manager::AssetRegistrar<Runtime> for AssetRegistrar {
#[transactional]
fn create_foreign_asset(
asset: AssetId,
min_balance: Balance,
metadata: AssetRegistrarMetadata,
is_sufficient: bool,
) -> DispatchResult {
Assets::force_create(
RuntimeOrigin::root(),
asset.into(),
sp_runtime::MultiAddress::Id(AssetManager::account_id()),
is_sufficient,
min_balance,
)?;
// Lastly, the metadata
Assets::force_set_metadata(
RuntimeOrigin::root(),
asset.into(),
metadata.name,
metadata.symbol,
metadata.decimals,
metadata.is_frozen,
)
}
#[transactional]
fn destroy_foreign_asset(asset: AssetId) -> DispatchResult {
// Mark the asset as destroying
Assets::start_destroy(RuntimeOrigin::root(), asset.into())
}
fn destroy_asset_dispatch_info_weight(asset: AssetId) -> Weight {
// For us both of them (Foreign and Local) have the same annotated weight for a given
// witness
// We need to take the dispatch info from the destroy call, which is already annotated in
// the assets pallet
// This is the dispatch info of destroy
RuntimeCall::Assets(pallet_assets::Call::<Runtime>::start_destroy { id: asset.into() })
.get_dispatch_info()
.weight
}
}
#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
pub struct AssetRegistrarMetadata {
pub name: Vec<u8>,
pub symbol: Vec<u8>,
pub decimals: u8,
pub is_frozen: bool,
}
impl pallet_asset_manager::Config for Runtime {
type AssetId = AssetId;
type AssetRegistrar = AssetRegistrar;
type AssetRegistrarMetadata = AssetRegistrarMetadata;
type Balance = Balance;
type ForeignAssetModifierOrigin = EnsureRoot<AccountId>;
type ForeignAssetType = AssetType;
type RuntimeEvent = RuntimeEvent;
/// Rerun benchmarks if you are making changes to runtime configuration.
type WeightInfo = weights::pallet_asset_manager::WeightInfo<Runtime>;
}
/// This trait ensure we can convert AccountIds to AssetIds.
pub trait AccountIdAssetIdConversion<Account, AssetId> {
// Get assetId and prefix from account
fn account_to_asset_id(account: Account) -> Option<(Vec<u8>, AssetId)>;
// Get AccountId from AssetId and prefix
fn asset_id_to_account(prefix: &[u8], asset_id: AssetId) -> Account;
}
const FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX: &[u8] = &[255u8; 28];
// Instruct how to go from an H256 to an AssetID
impl AccountIdAssetIdConversion<AccountId, AssetId> for Runtime {
/// The way to convert an account to assetId is by ensuring that the prefix is 0XFFFFFFFF
/// and by taking the lowest 128 bits as the assetId
fn account_to_asset_id(account: AccountId) -> Option<(Vec<u8>, AssetId)> {
let bytes: [u8; 32] = account.into();
let h256_account: H256 = bytes.into();
let mut data = [0u8; 4];
let (prefix_part, id_part) = h256_account.as_fixed_bytes().split_at(28);
if prefix_part == FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX {
data.copy_from_slice(id_part);
let asset_id: AssetId = u32::from_be_bytes(data);
Some((prefix_part.to_vec(), asset_id))
} else {
None
}
}
// The opposite conversion
fn asset_id_to_account(prefix: &[u8], asset_id: AssetId) -> AccountId {
let mut data = [0u8; 32];
data[0..28].copy_from_slice(prefix);
data[28..32].copy_from_slice(&asset_id.to_be_bytes());
AccountId::from(data)
}
}
+3 -39
View File
@@ -1,3 +1,4 @@
pub mod asset_config;
pub mod governance;
pub mod xcm_config;
@@ -10,16 +11,13 @@ use frame_support::{
derive_impl,
dispatch::DispatchClass,
parameter_types,
traits::{
AsEnsureOriginWithArg, ConstU32, ConstU64, Contains, EitherOfDiverse, InstanceFilter,
TransformOrigin,
},
traits::{ConstU32, ConstU64, Contains, EitherOfDiverse, InstanceFilter, TransformOrigin},
weights::{ConstantMultiplier, Weight},
PalletId,
};
use frame_system::{
limits::{BlockLength, BlockWeights},
EnsureRoot, EnsureSigned,
EnsureRoot,
};
pub use governance::origins::pallet_custom_origins;
use governance::{origins::Treasurer, TreasurySpender};
@@ -309,40 +307,6 @@ impl pallet_balances::Config for Runtime {
type WeightInfo = weights::pallet_balances::WeightInfo<Runtime>;
}
parameter_types! {
pub const AssetDeposit: Balance = 10 * CENTS;
pub const AssetAccountDeposit: Balance = deposit(1, 16);
pub const ApprovalDeposit: Balance = EXISTENTIAL_DEPOSIT;
pub const StringLimit: u32 = 50;
pub const MetadataDepositBase: Balance = deposit(1, 68);
pub const MetadataDepositPerByte: Balance = deposit(0, 1);
pub const RemoveItemsLimit: u32 = 1000;
}
impl pallet_assets::Config for Runtime {
type ApprovalDeposit = ApprovalDeposit;
type AssetAccountDeposit = AssetAccountDeposit;
type AssetDeposit = AssetDeposit;
type AssetId = crate::types::AssetId;
type AssetIdParameter = parity_scale_codec::Compact<crate::types::AssetId>;
type Balance = Balance;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
type CallbackHandle = ();
type CreateOrigin = AsEnsureOriginWithArg<EnsureSigned<AccountId>>;
type Currency = Balances;
type Extra = ();
type ForceOrigin = EnsureRoot<AccountId>;
type Freezer = ();
type MetadataDepositBase = MetadataDepositBase;
type MetadataDepositPerByte = MetadataDepositPerByte;
type RemoveItemsLimit = RemoveItemsLimit;
type RuntimeEvent = RuntimeEvent;
type StringLimit = StringLimit;
/// Rerun benchmarks if you are making changes to runtime configuration.
type WeightInfo = weights::pallet_assets::WeightInfo<Runtime>;
}
parameter_types! {
/// Relay Chain `TransactionByteFee` / 10
pub const TransactionByteFee: Balance = 10 * MICROCENTS;
+2
View File
@@ -149,6 +149,8 @@ mod runtime {
pub type Assets = pallet_assets;
#[runtime::pallet_index(13)]
pub type Treasury = pallet_treasury;
#[runtime::pallet_index(14)]
pub type AssetManager = pallet_asset_manager;
// Governance
#[runtime::pallet_index(15)]
@@ -21,6 +21,7 @@ pub mod block_weights;
pub mod cumulus_pallet_parachain_system;
pub mod cumulus_pallet_xcmp_queue;
pub mod extrinsic_weights;
pub mod pallet_asset_manager;
pub mod pallet_assets;
pub mod pallet_balances;
pub mod pallet_collator_selection;
@@ -0,0 +1,91 @@
//! Autogenerated weights for `pallet_asset_manager`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2024-06-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `ip-172-31-15-118`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: 1024
// Executed Command:
// target/release/parachain-template-node
// benchmark
// pallet
// --steps=50
// --repeat=20
// --extrinsic=*
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=benchmarking/results/results-pallet_asset_manager.json
// --pallet=pallet_asset_manager
// --chain=dev
// --output=benchmarking/new-benchmarks/pallet_asset_manager.rs
#![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_manager`.
pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> pallet_asset_manager::WeightInfo for WeightInfo<T> {
/// Storage: `AssetManager::AssetIdType` (r:1 w:1)
/// Proof: `AssetManager::AssetIdType` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Assets::Asset` (r:1 w:1)
/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(174), added: 2649, mode: `MaxEncodedLen`)
/// Storage: `Assets::Metadata` (r:1 w:1)
/// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(152), added: 2627, mode: `MaxEncodedLen`)
/// Storage: `AssetManager::AssetTypeId` (r:0 w:1)
/// Proof: `AssetManager::AssetTypeId` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn register_foreign_asset() -> Weight {
// Proof Size summary in bytes:
// Measured: `82`
// Estimated: `3639`
// Minimum execution time: 39_040_000 picoseconds.
Weight::from_parts(39_649_000, 0)
.saturating_add(Weight::from_parts(0, 3639))
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(4))
}
/// Storage: `AssetManager::SupportedFeePaymentAssets` (r:1 w:1)
/// Proof: `AssetManager::SupportedFeePaymentAssets` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `AssetManager::AssetIdType` (r:1 w:1)
/// Proof: `AssetManager::AssetIdType` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `AssetManager::AssetTypeUnitsPerSecond` (r:1 w:2)
/// Proof: `AssetManager::AssetTypeUnitsPerSecond` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `AssetManager::AssetTypeId` (r:0 w:2)
/// Proof: `AssetManager::AssetTypeId` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `x` is `[5, 100]`.
fn change_existing_asset_type() -> Weight {
// Proof Size summary in bytes:
// Measured: `82`
// Estimated: `3639`
// Minimum execution time: 39_040_000 picoseconds.
Weight::from_parts(39_649_000, 0)
.saturating_add(Weight::from_parts(0, 3639))
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(4))
}
/// Storage: `AssetManager::SupportedFeePaymentAssets` (r:1 w:1)
/// Proof: `AssetManager::SupportedFeePaymentAssets` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `AssetManager::AssetIdType` (r:1 w:1)
/// Proof: `AssetManager::AssetIdType` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `AssetManager::AssetTypeUnitsPerSecond` (r:0 w:1)
/// Proof: `AssetManager::AssetTypeUnitsPerSecond` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `AssetManager::AssetTypeId` (r:0 w:1)
/// Proof: `AssetManager::AssetTypeId` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `x` is `[5, 100]`.
fn remove_existing_asset_type() -> Weight {
// Proof Size summary in bytes:
// Measured: `82`
// Estimated: `3639`
// Minimum execution time: 39_040_000 picoseconds.
Weight::from_parts(39_649_000, 0)
.saturating_add(Weight::from_parts(0, 3639))
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(4))
}
}
@@ -21,7 +21,7 @@ mod constant_tests {
mod runtime_tests {
use frame_support::{pallet_prelude::Weight, traits::TypedGet, PalletId};
use generic_runtime_template::{
configs::*,
configs::{asset_config::*, *},
constants::{currency::*, *},
*,
};