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 crate::OpenTechCommitteeInstance;
21

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

            
27
use super::moonbase_weights;
28
use moonkit_xcm_primitives::AccountIdAssetIdConversion;
29

            
30
use frame_support::{
31
	dispatch::GetDispatchInfo,
32
	parameter_types,
33
	traits::{AsEnsureOriginWithArg, ConstU128, ConstU32, EitherOfDiverse},
34
	weights::Weight,
35
};
36

            
37
use frame_system::{EnsureNever, EnsureRoot};
38
use parity_scale_codec::{Compact, Decode, Encode};
39
use scale_info::TypeInfo;
40
use sp_core::H160;
41

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

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

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

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

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

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

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

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

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

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

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

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

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

            
166
28
#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
167
pub struct AssetRegistrarMetadata {
168
	pub name: Vec<u8>,
169
	pub symbol: Vec<u8>,
170
	pub decimals: u8,
171
	pub is_frozen: bool,
172
}
173

            
174
pub type ForeignAssetModifierOrigin = EitherOfDiverse<
175
	EnsureRoot<AccountId>,
176
	EitherOfDiverse<
177
		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
178
		governance::custom_origins::GeneralAdmin,
179
	>,
180
>;
181

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

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

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