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
// #![cfg(feature = "runtime-benchmarks")]
17

            
18
use crate::{foreign_asset::ForeignAssetMigrationStatus, Call, Config, Pallet};
19
use frame_benchmarking::{account, benchmarks};
20
use frame_support::traits::Currency;
21
use frame_support::BoundedVec;
22
use frame_system::RawOrigin;
23
use pallet_asset_manager::AssetRegistrar;
24
use sp_core::{Get, U256};
25
use sp_runtime::traits::StaticLookup;
26
use sp_runtime::Saturating;
27
use sp_std::vec;
28
use sp_std::vec::Vec;
29
use xcm::latest::prelude::*;
30

            
31
fn setup_foreign_asset<T: Config>(n_accounts: u32) -> T::AssetIdParameter {
32
	let asset_type = T::ForeignAssetType::default();
33
	let metadata = T::AssetRegistrarMetadata::default();
34
	let asset_id = asset_type.clone().into();
35

            
36
	let caller: T::AccountId = pallet_asset_manager::Pallet::<T>::account_id();
37
	let caller_lookup = T::Lookup::unlookup(caller.clone());
38
	let root: T::RuntimeOrigin = RawOrigin::Root.into();
39

            
40
	// Register in asset manager
41
	let _ = pallet_asset_manager::Pallet::<T>::register_foreign_asset(
42
		root.clone(),
43
		asset_type,
44
		metadata,
45
		<T as pallet_asset_manager::Config>::Balance::from(1u32),
46
		true,
47
	)
48
	.unwrap();
49

            
50
	let _ = <T as pallet_assets::Config>::Currency::deposit_creating(
51
		&caller,
52
		<T as pallet_assets::Config>::MetadataDepositBase::get()
53
			.saturating_add(
54
				<T as pallet_assets::Config>::MetadataDepositPerByte::get()
55
					.saturating_mul((T::StringLimit::get() as u32).into()),
56
			)
57
			.saturating_mul(2u32.into()),
58
	);
59

            
60
	let dummy = Vec::from_iter((0..T::StringLimit::get() as usize).map(|_| 0u8));
61
	let _ = pallet_assets::Pallet::<T>::set_metadata(
62
		RawOrigin::Signed(caller.clone()).into(),
63
		asset_id.clone().into(),
64
		dummy.clone(),
65
		dummy,
66
		18,
67
	)
68
	.unwrap();
69

            
70
	// Create approval
71
	pallet_assets::Pallet::<T>::mint(
72
		RawOrigin::Signed(caller.clone()).into(),
73
		asset_id.clone().into(),
74
		caller_lookup,
75
		(100 * (n_accounts + 1)).into(),
76
	)
77
	.unwrap();
78

            
79
	// Setup n accounts with balances and approvals
80
	for i in 0..n_accounts {
81
		let user: T::AccountId = account("user", i, 0);
82
		let user_lookup = T::Lookup::unlookup(user.clone());
83

            
84
		// Mint assets
85
		let _ = pallet_assets::Pallet::<T>::mint(
86
			RawOrigin::Signed(caller.clone()).into(),
87
			asset_id.clone().into(),
88
			user_lookup,
89
			100u32.into(),
90
		)
91
		.unwrap();
92

            
93
		let spender: T::AccountId = account("spender", i, 0);
94
		let spender_lookup = T::Lookup::unlookup(spender.clone());
95
		let enough = <T as pallet_assets::Config>::Currency::minimum_balance();
96
		<T as pallet_assets::Config>::Currency::make_free_balance_be(&spender, enough);
97

            
98
		let _ = pallet_assets::Pallet::<T>::approve_transfer(
99
			RawOrigin::Signed(caller.clone()).into(),
100
			asset_id.clone().into(),
101
			spender_lookup,
102
			5u32.into(),
103
		)
104
		.unwrap();
105
	}
106

            
107
	asset_id.into()
108
}
109

            
110
benchmarks! {
111
	where_clause {
112
		where
113
		<T as pallet_assets::Config>::Balance: Into<U256>,
114
		T::ForeignAssetType: Into<Option<Location>>,
115
	}
116
	approve_assets_to_migrate {
117
		let n in 1 .. 100u32;
118
		let assets: Vec<u128> = (0..n).map(|i| {
119
			let metadata = T::AssetRegistrarMetadata::default();
120
			let asset_id: u128 = i.into();
121
			T::AssetRegistrar::create_foreign_asset(
122
				asset_id,
123
				1u32.into(),
124
				metadata.clone(),
125
				true,
126
			).expect("failed to create asset");
127
			asset_id
128
		}).collect();
129
	}: _(RawOrigin::Root, BoundedVec::try_from(assets.clone()).unwrap())
130
	verify {
131
		for asset_id in assets {
132
			assert!(crate::pallet::ApprovedForeignAssets::<T>::contains_key(asset_id));
133
		}
134
	}
135

            
136
	start_foreign_assets_migration {
137
		let asset_id = setup_foreign_asset::<T>(1);
138

            
139
		Pallet::<T>::approve_assets_to_migrate(
140
			RawOrigin::Root.into(),
141
			BoundedVec::try_from(vec![asset_id.clone().into()]).unwrap()
142
		)?;
143
	}: _(RawOrigin::Signed(account("caller", 0, 0)), asset_id.into())
144
	verify {
145
		assert!(matches!(
146
			crate::pallet::ForeignAssetMigrationStatusValue::<T>::get(),
147
			ForeignAssetMigrationStatus::Migrating(_)
148
		));
149
	}
150

            
151
	migrate_foreign_asset_balances {
152
		let n in 1 .. 1000u32;
153
		let asset_id = setup_foreign_asset::<T>(n);
154

            
155
		Pallet::<T>::approve_assets_to_migrate(
156
			RawOrigin::Root.into(),
157
			BoundedVec::try_from(vec![asset_id.clone().into()]).unwrap()
158
		)?;
159

            
160
		Pallet::<T>::start_foreign_assets_migration(
161
			RawOrigin::Signed(account("caller", 0, 0)).into(),
162
			asset_id.into()
163
		)?;
164
	}: _(RawOrigin::Signed(account("caller", 0, 0)), n + 1)
165
	verify {
166
		match crate::pallet::ForeignAssetMigrationStatusValue::<T>::get() {
167
			ForeignAssetMigrationStatus::Migrating(info) => {
168
				assert_eq!(info.remaining_balances, 0);
169
			},
170
			_ => panic!("Expected Migrating status"),
171
		}
172
	}
173

            
174
	migrate_foreign_asset_approvals {
175
		let n in 1 .. 1000u32;
176
		let asset_id = setup_foreign_asset::<T>(n);
177

            
178
		Pallet::<T>::approve_assets_to_migrate(
179
			RawOrigin::Root.into(),
180
			BoundedVec::try_from(vec![asset_id.clone().into()]).unwrap()
181
		)?;
182

            
183
		Pallet::<T>::start_foreign_assets_migration(
184
			RawOrigin::Signed(account("caller", 0, 0)).into(),
185
			asset_id.into()
186
		)?;
187

            
188
		Pallet::<T>::migrate_foreign_asset_balances(
189
			RawOrigin::Signed(account("caller", 0, 0)).into(),
190
			n + 1
191
		)?;
192
	}: _(RawOrigin::Signed(account("caller", 0, 0)), n)
193
	verify {
194
		match crate::pallet::ForeignAssetMigrationStatusValue::<T>::get() {
195
			ForeignAssetMigrationStatus::Migrating(info) => {
196
				assert_eq!(info.remaining_approvals, 0);
197
			},
198
			_ => panic!("Expected Migrating status"),
199
		}
200
	}
201

            
202
	finish_foreign_assets_migration {
203
		let n = 100u32;
204
		let asset_id = setup_foreign_asset::<T>(n);
205

            
206
		Pallet::<T>::approve_assets_to_migrate(
207
			RawOrigin::Root.into(),
208
			BoundedVec::try_from(vec![asset_id.clone().into()]).unwrap()
209
		)?;
210

            
211
		Pallet::<T>::start_foreign_assets_migration(
212
			RawOrigin::Signed(account("caller", 0, 0)).into(),
213
			asset_id.into()
214
		)?;
215

            
216
		Pallet::<T>::migrate_foreign_asset_balances(
217
			RawOrigin::Signed(account("caller", 0, 0)).into(),
218
			n + 1
219
		)?;
220

            
221
		Pallet::<T>::migrate_foreign_asset_approvals(
222
			RawOrigin::Signed(account("caller", 0, 0)).into(),
223
			n + 1
224
		)?;
225
	}: _(RawOrigin::Signed(account("caller", 0, 0)))
226
	verify {
227
		assert_eq!(
228
			crate::pallet::ForeignAssetMigrationStatusValue::<T>::get(),
229
			ForeignAssetMigrationStatus::Idle
230
		);
231
	}
232
}