1
// Copyright 2019-2025 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! Asset configuration for Moonbase.
18
//!
19

            
20
use super::{
21
	currency, governance, xcm_config, AccountId, AssetId, AssetManager, Assets, Balance, Balances,
22
	OpenTechCommitteeInstance, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin,
23
	FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX,
24
};
25

            
26
use super::moonbeam_weights;
27
use frame_support::{
28
	dispatch::GetDispatchInfo,
29
	parameter_types,
30
	traits::{AsEnsureOriginWithArg, ConstU128, ConstU32, EitherOfDiverse},
31
	weights::Weight,
32
};
33
use moonkit_xcm_primitives::AccountIdAssetIdConversion;
34

            
35
use frame_system::{EnsureNever, EnsureRoot};
36
use sp_core::H160;
37

            
38
use parity_scale_codec::{Compact, Decode, Encode};
39
use scale_info::TypeInfo;
40

            
41
use sp_std::{
42
	convert::{From, Into},
43
	prelude::*,
44
};
45

            
46
// Number of items that can be destroyed with our configured max extrinsic proof size.
47
// x = (a - b) / c where:
48
// 		a: maxExtrinsic proof size
49
// 		b: base proof size for destroy_accounts in pallet_assets weights
50
// 		c: proof size for each item
51
// 		656.87 = (3_407_872 - 8232) / 5180
52
const REMOVE_ITEMS_LIMIT: u32 = 656;
53

            
54
// Not to disrupt the previous asset instance, we assign () to Foreign
55
pub type ForeignAssetInstance = ();
56

            
57
// For foreign assets, these parameters dont matter much
58
// as this will only be called by root with the forced arguments
59
// No deposit is substracted with those methods
60
parameter_types! {
61
	pub const AssetDeposit: Balance = 100 * currency::GLMR * currency::SUPPLY_FACTOR;
62
	pub const ApprovalDeposit: Balance = 0;
63
	pub const AssetsStringLimit: u32 = 50;
64
	pub const MetadataDepositBase: Balance = currency::deposit(1,68);
65
	pub const MetadataDepositPerByte: Balance = currency::deposit(0, 1);
66
}
67

            
68
/// We allow Root and General Admin to execute privileged asset operations.
69
pub type AssetsForceOrigin =
70
	EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
71

            
72
// Required for runtime benchmarks
73
pallet_assets::runtime_benchmarks_enabled! {
74
	pub struct BenchmarkHelper;
75
	impl<AssetIdParameter> pallet_assets::BenchmarkHelper<AssetIdParameter> for BenchmarkHelper
76
	where
77
		AssetIdParameter: From<u128>,
78
	{
79
		fn create_asset_id_parameter(id: u32) -> AssetIdParameter {
80
			(id as u128).into()
81
		}
82
	}
83
}
84

            
85
// Foreign assets
86
impl pallet_assets::Config<ForeignAssetInstance> for Runtime {
87
	type RuntimeEvent = RuntimeEvent;
88
	type Balance = Balance;
89
	type AssetId = AssetId;
90
	type Currency = Balances;
91
	type ForceOrigin = AssetsForceOrigin;
92
	type AssetDeposit = AssetDeposit;
93
	type MetadataDepositBase = MetadataDepositBase;
94
	type MetadataDepositPerByte = MetadataDepositPerByte;
95
	type ApprovalDeposit = ApprovalDeposit;
96
	type StringLimit = AssetsStringLimit;
97
	type Freezer = ();
98
	type Extra = ();
99
	type AssetAccountDeposit = ConstU128<{ currency::deposit(1, 18) }>;
100
	type WeightInfo = moonbeam_weights::pallet_assets::WeightInfo<Runtime>;
101
	type RemoveItemsLimit = ConstU32<{ REMOVE_ITEMS_LIMIT }>;
102
	type AssetIdParameter = Compact<AssetId>;
103
	type CreateOrigin = AsEnsureOriginWithArg<EnsureNever<AccountId>>;
104
	type CallbackHandle = ();
105
	pallet_assets::runtime_benchmarks_enabled! {
106
		type BenchmarkHelper = BenchmarkHelper;
107
	}
108
}
109

            
110
// We instruct how to register the Assets
111
// In this case, we tell it to Create an Asset in pallet-assets
112
pub struct AssetRegistrar;
113
use frame_support::{pallet_prelude::DispatchResult, transactional};
114

            
115
impl pallet_asset_manager::AssetRegistrar<Runtime> for AssetRegistrar {
116
162
	#[transactional]
117
	fn create_foreign_asset(
118
		asset: AssetId,
119
		min_balance: Balance,
120
		metadata: AssetRegistrarMetadata,
121
		is_sufficient: bool,
122
	) -> DispatchResult {
123
		Assets::force_create(
124
			RuntimeOrigin::root(),
125
			asset.into(),
126
			AssetManager::account_id(),
127
			is_sufficient,
128
			min_balance,
129
		)?;
130

            
131
		// Lastly, the metadata
132
		Assets::force_set_metadata(
133
			RuntimeOrigin::root(),
134
			asset.into(),
135
			metadata.name,
136
			metadata.symbol,
137
			metadata.decimals,
138
			metadata.is_frozen,
139
		)
140
	}
141

            
142
	#[transactional]
143
	fn destroy_foreign_asset(asset: AssetId) -> DispatchResult {
144
		// Mark the asset as destroying
145
		Assets::start_destroy(RuntimeOrigin::root(), asset.into())
146
	}
147

            
148
	fn destroy_asset_dispatch_info_weight(asset: AssetId) -> Weight {
149
		// For us both of them (Foreign and Local) have the same annotated weight for a given
150
		// witness
151
		// We need to take the dispatch info from the destroy call, which is already annotated in
152
		// the assets pallet
153

            
154
		// This is the dispatch info of destroy
155
		RuntimeCall::Assets(
156
			pallet_assets::Call::<Runtime, ForeignAssetInstance>::start_destroy {
157
				id: asset.into(),
158
			},
159
		)
160
		.get_dispatch_info()
161
		.total_weight()
162
	}
163
}
164

            
165
24
#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
166
pub struct AssetRegistrarMetadata {
167
	pub name: Vec<u8>,
168
	pub symbol: Vec<u8>,
169
	pub decimals: u8,
170
	pub is_frozen: bool,
171
}
172
pub type ForeignAssetModifierOrigin = EitherOfDiverse<
173
	EnsureRoot<AccountId>,
174
	EitherOfDiverse<
175
		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
176
		governance::custom_origins::GeneralAdmin,
177
	>,
178
>;
179

            
180
impl pallet_asset_manager::Config for Runtime {
181
	type RuntimeEvent = RuntimeEvent;
182
	type Balance = Balance;
183
	type AssetId = AssetId;
184
	type AssetRegistrarMetadata = AssetRegistrarMetadata;
185
	type ForeignAssetType = xcm_config::AssetType;
186
	type AssetRegistrar = AssetRegistrar;
187
	type ForeignAssetModifierOrigin = ForeignAssetModifierOrigin;
188
	type WeightInfo = moonbeam_weights::pallet_asset_manager::WeightInfo<Runtime>;
189
}
190

            
191
// Instruct how to go from an H160 to an AssetID
192
// We just take the lowest 128 bits
193
impl AccountIdAssetIdConversion<AccountId, AssetId> for Runtime {
194
	/// The way to convert an account to assetId is by ensuring that the prefix is 0XFFFFFFFF
195
	/// and by taking the lowest 128 bits as the assetId
196
132
	fn account_to_asset_id(account: AccountId) -> Option<(Vec<u8>, AssetId)> {
197
132
		let h160_account: H160 = account.into();
198
132
		let mut data = [0u8; 16];
199
132
		let (prefix_part, id_part) = h160_account.as_fixed_bytes().split_at(4);
200
132
		if prefix_part == FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX {
201
132
			data.copy_from_slice(id_part);
202
132
			let asset_id: AssetId = u128::from_be_bytes(data).into();
203
132
			Some((prefix_part.to_vec(), asset_id))
204
		} else {
205
			None
206
		}
207
132
	}
208

            
209
	// The opposite conversion
210
287028
	fn asset_id_to_account(prefix: &[u8], asset_id: AssetId) -> AccountId {
211
287028
		let mut data = [0u8; 20];
212
287028
		data[0..4].copy_from_slice(prefix);
213
287028
		data[4..20].copy_from_slice(&asset_id.to_be_bytes());
214
287028
		AccountId::from(data)
215
287028
	}
216
}