1
// Copyright 2024 Moonbeam foundation
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
//! # Lazy Migration Pallet
18

            
19
#![allow(non_camel_case_types)]
20
#![cfg_attr(not(feature = "std"), no_std)]
21

            
22
#[cfg(test)]
23
pub mod mock;
24
#[cfg(test)]
25
mod tests;
26

            
27
#[cfg(any(test, feature = "runtime-benchmarks"))]
28
mod benchmarks;
29

            
30
mod foreign_asset;
31
pub mod weights;
32
pub use weights::WeightInfo;
33

            
34
use frame_support::pallet;
35
use frame_support::pallet_prelude::*;
36
use frame_system::pallet_prelude::*;
37
pub use pallet::*;
38
use xcm::latest::Location;
39

            
40
const MAX_CONTRACT_CODE_SIZE: u64 = 25 * 1024;
41

            
42
environmental::environmental!(MIGRATING_FOREIGN_ASSETS: bool);
43

            
44
199
#[pallet]
45
pub mod pallet {
46
	use super::*;
47
	use crate::foreign_asset::ForeignAssetMigrationStatus;
48
	use sp_core::{H160, U256};
49

            
50
	pub const ARRAY_LIMIT: u32 = 1000;
51
	pub type GetArrayLimit = ConstU32<ARRAY_LIMIT>;
52

            
53
	/// Pallet for multi block migrations
54
64
	#[pallet::pallet]
55
	pub struct Pallet<T>(PhantomData<T>);
56

            
57
180
	#[pallet::storage]
58
	pub(crate) type ForeignAssetMigrationStatusValue<T: Config> =
59
		StorageValue<_, ForeignAssetMigrationStatus, ValueQuery>;
60

            
61
	// List of approved foreign assets to be migrated
62
56
	#[pallet::storage]
63
	pub(crate) type ApprovedForeignAssets<T: Config> =
64
		StorageMap<_, Twox64Concat, u128, (), OptionQuery>;
65

            
66
	/// Configuration trait of this pallet.
67
	#[pallet::config]
68
	pub trait Config:
69
		frame_system::Config
70
		+ pallet_evm::Config
71
		+ pallet_balances::Config
72
		+ pallet_assets::Config<AssetId = u128>
73
		+ pallet_asset_manager::Config<AssetId = u128>
74
		+ pallet_moonbeam_foreign_assets::Config
75
	{
76
		// Origin that is allowed to start foreign assets migration
77
		type ForeignAssetMigratorOrigin: EnsureOrigin<Self::RuntimeOrigin>;
78
		type WeightInfo: WeightInfo;
79
	}
80

            
81
48
	#[pallet::error]
82
	pub enum Error<T> {
83
		/// The limit cannot be zero
84
		LimitCannotBeZero,
85
		/// The contract already have metadata
86
		ContractMetadataAlreadySet,
87
		/// Contract not exist
88
		ContractNotExist,
89
		/// The symbol length exceeds the maximum allowed
90
		SymbolTooLong,
91
		/// The name length exceeds the maximum allowed
92
		NameTooLong,
93
		/// The asset type was not found
94
		AssetTypeNotFound,
95
		/// Asset not found
96
		AssetNotFound,
97
		/// The location of the asset was not found
98
		LocationNotFound,
99
		/// Migration is not finished yet
100
		MigrationNotFinished,
101
		/// No migration in progress
102
		NoMigrationInProgress,
103
		/// Fail to mint the foreign asset
104
		MintFailed,
105
		/// Fail to add an approval
106
		ApprovalFailed,
107
	}
108

            
109
	#[pallet::call]
110
	impl<T: Config> Pallet<T>
111
	where
112
		<T as pallet_assets::Config>::Balance: Into<U256>,
113
		<T as pallet_asset_manager::Config>::ForeignAssetType: Into<Option<Location>>,
114
		<T as frame_system::Config>::AccountId: Into<H160> + From<H160>,
115
	{
116
		#[pallet::call_index(2)]
117
		#[pallet::weight(Pallet::<T>::create_contract_metadata_weight(MAX_CONTRACT_CODE_SIZE))]
118
		pub fn create_contract_metadata(
119
			origin: OriginFor<T>,
120
			address: H160,
121
3
		) -> DispatchResultWithPostInfo {
122
3
			ensure_signed(origin)?;
123

            
124
3
			ensure!(
125
3
				pallet_evm::AccountCodesMetadata::<T>::get(address).is_none(),
126
1
				Error::<T>::ContractMetadataAlreadySet
127
			);
128

            
129
			// Ensure contract exist
130
2
			let code = pallet_evm::AccountCodes::<T>::get(address);
131
2
			ensure!(!code.is_empty(), Error::<T>::ContractNotExist);
132

            
133
			// Construct metadata
134
1
			let code_size = code.len() as u64;
135
1
			let code_hash = sp_core::H256::from(sp_io::hashing::keccak_256(&code));
136
1
			let meta = pallet_evm::CodeMetadata {
137
1
				size: code_size,
138
1
				hash: code_hash,
139
1
			};
140
1

            
141
1
			// Set metadata
142
1
			pallet_evm::AccountCodesMetadata::<T>::insert(address, meta);
143
1

            
144
1
			Ok((
145
1
				Some(Self::create_contract_metadata_weight(code_size)),
146
1
				Pays::No,
147
1
			)
148
1
				.into())
149
		}
150

            
151
		#[pallet::call_index(3)]
152
		#[pallet::weight(
153
			<T as pallet::Config>::WeightInfo::approve_assets_to_migrate(assets.len() as u32)
154
		)]
155
		pub fn approve_assets_to_migrate(
156
			origin: OriginFor<T>,
157
			assets: BoundedVec<u128, GetArrayLimit>,
158
18
		) -> DispatchResultWithPostInfo {
159
18
			T::ForeignAssetMigratorOrigin::ensure_origin(origin.clone())?;
160

            
161
17
			assets.iter().try_for_each(|asset_id| {
162
17
				ensure!(
163
17
					pallet_assets::Asset::<T>::contains_key(*asset_id),
164
					Error::<T>::AssetNotFound
165
				);
166

            
167
17
				ApprovedForeignAssets::<T>::insert(asset_id, ());
168
17
				Ok::<(), Error<T>>(())
169
17
			})?;
170
17
			Ok(Pays::No.into())
171
		}
172

            
173
		#[pallet::call_index(4)]
174
		#[pallet::weight(<T as pallet::Config>::WeightInfo::start_foreign_assets_migration())]
175
		pub fn start_foreign_assets_migration(
176
			origin: OriginFor<T>,
177
			asset_id: u128,
178
18
		) -> DispatchResultWithPostInfo {
179
18
			ensure_signed(origin)?;
180

            
181
18
			Self::do_start_foreign_asset_migration(asset_id)?;
182
15
			Ok(Pays::No.into())
183
		}
184

            
185
		#[pallet::call_index(5)]
186
		#[pallet::weight(<T as pallet::Config>::WeightInfo::migrate_foreign_asset_balances(*limit))]
187
		pub fn migrate_foreign_asset_balances(
188
			origin: OriginFor<T>,
189
			limit: u32,
190
8
		) -> DispatchResultWithPostInfo {
191
8
			ensure_signed(origin)?;
192

            
193
8
			Self::do_migrate_foreign_asset_balances(limit)?;
194
6
			Ok(Pays::No.into())
195
		}
196

            
197
		#[pallet::call_index(6)]
198
		#[pallet::weight(<T as pallet::Config>::WeightInfo::migrate_foreign_asset_approvals(*limit))]
199
		pub fn migrate_foreign_asset_approvals(
200
			origin: OriginFor<T>,
201
			limit: u32,
202
8
		) -> DispatchResultWithPostInfo {
203
8
			ensure_signed(origin)?;
204

            
205
8
			Self::do_migrate_foreign_asset_approvals(limit)?;
206
6
			Ok(Pays::No.into())
207
		}
208

            
209
		#[pallet::call_index(7)]
210
		#[pallet::weight(<T as pallet::Config>::WeightInfo::finish_foreign_assets_migration())]
211
4
		pub fn finish_foreign_assets_migration(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
212
4
			ensure_signed(origin)?;
213

            
214
4
			Self::do_finish_foreign_asset_migration()?;
215
1
			Ok(Pays::No.into())
216
		}
217
	}
218

            
219
	impl<T: Config> Pallet<T> {
220
1
		fn create_contract_metadata_weight(code_size: u64) -> Weight {
221
			// max entry size of AccountCodesMetadata (full key + value)
222
			const PROOF_SIZE_CODE_METADATA: u64 = 100;
223
			// intermediates nodes might be up to 3Kb
224
			const PROOF_SIZE_INTERMEDIATES_NODES: u64 = 3 * 1024;
225

            
226
			// Account for 2 reads, 1 write
227
1
			<T as frame_system::Config>::DbWeight::get()
228
1
				.reads_writes(2, 1)
229
1
				.set_proof_size(
230
1
					code_size + (PROOF_SIZE_INTERMEDIATES_NODES * 2) + PROOF_SIZE_CODE_METADATA,
231
1
				)
232
1
		}
233
	}
234
}
235

            
236
4040
pub fn is_migrating_foreign_assets() -> bool {
237
4040
	MIGRATING_FOREIGN_ASSETS::with(|v| *v).unwrap_or(false)
238
4040
}