1use super::*;
21use crate::Pallet as Psm;
22use alloc::boxed::Box;
23use frame_benchmarking::v2::*;
24use frame_support::traits::{
25 fungibles::{
26 metadata::Inspect as FungiblesMetadataInspect, Create as FungiblesCreate,
27 Inspect as FungiblesInspect, Mutate as FungiblesMutate,
28 },
29 Consideration, EnsureOriginWithArg, Get,
30};
31use frame_system::RawOrigin;
32use pallet::BalanceOf;
33use sp_runtime::{Permill, Saturating};
34
35const INTERNAL_ASSET_INDEX: u32 = 50;
38const EXTERNAL_ASSET_OFFSET: u32 = 100;
39const BENCH_MIN_SWAP: u32 = 1_000;
41
42fn ensure_internal_setup<T: Config>() -> (T::AssetId, u8)
45where
46 T::Fungibles: FungiblesCreate<T::AccountId>,
47{
48 let admin: T::AccountId = whitelisted_caller();
49 let _ = frame_system::Pallet::<T>::inc_providers(&admin);
50 let internal_id: T::AssetId = T::BenchmarkHelper::get_asset_id(INTERNAL_ASSET_INDEX);
51 if !T::Fungibles::asset_exists(internal_id.clone()) {
52 let _ = T::Fungibles::create(internal_id.clone(), admin.clone(), true, 1u32.into());
53 }
54 let internal_decimals = T::Fungibles::decimals(internal_id.clone());
55 if !crate::Psm::<T>::contains_key(&internal_id) {
56 let origin = T::CreateOrigin::try_successful_origin(&internal_id)
57 .expect("benchmark CreateOrigin is available");
58 if let Some(depositor) = T::CreateOrigin::ensure_origin(origin.clone(), &internal_id)
59 .expect("benchmark CreateOrigin succeeds")
60 {
61 T::Consideration::ensure_successful(&depositor, Psm::<T>::psm_creation_footprint());
62 }
63 let root_origin: T::PalletsOrigin = RawOrigin::Root.into();
64 Psm::<T>::create_psm(
65 origin,
66 internal_id.clone(),
67 Box::new(root_origin.clone()),
68 Box::new(root_origin),
69 admin,
70 BalanceOf::<T>::from(u32::MAX).saturating_mul(1_000_000u32.into()),
71 BalanceOf::<T>::from(BENCH_MIN_SWAP),
72 )
73 .expect("benchmark PSM creation succeeds");
74 frame_system::Pallet::<T>::inc_providers(&Psm::<T>::psm_account(&internal_id));
76 }
77 (internal_id, internal_decimals)
78}
79
80fn setup_assets<T: Config>(n: u32) -> (T::AssetId, T::AssetId)
91where
92 T::Fungibles: FungiblesCreate<T::AccountId>,
93{
94 let admin: T::AccountId = whitelisted_caller();
95 let _ = frame_system::Pallet::<T>::inc_providers(&admin);
96
97 let (internal_id, internal_decimals) = ensure_internal_setup::<T>();
98
99 let target_id: T::AssetId = T::BenchmarkHelper::get_asset_id(EXTERNAL_ASSET_OFFSET);
104 if !T::Fungibles::asset_exists(target_id.clone()) {
105 T::BenchmarkHelper::create_asset(target_id.clone(), &admin, internal_decimals);
106 }
107
108 for i in 0..n {
112 let id: T::AssetId = T::BenchmarkHelper::get_asset_id(EXTERNAL_ASSET_OFFSET + i);
113 crate::ExternalAssets::<T>::insert(
114 &internal_id,
115 &id,
116 crate::ExternalAssetInfo {
117 status: CircuitBreakerLevel::AllEnabled,
118 decimals: internal_decimals,
119 },
120 );
121 crate::AssetCeilingWeight::<T>::insert(&internal_id, &id, Permill::from_percent(1));
122 crate::PsmDebt::<T>::insert(&internal_id, &id, BalanceOf::<T>::from(1u32));
123 }
124 crate::AssetCeilingWeight::<T>::insert(&internal_id, &target_id, Permill::from_percent(100));
126
127 crate::Psm::<T>::mutate(&internal_id, |maybe| {
129 if let Some(info) = maybe.as_mut() {
130 info.external_count = n.max(1);
131 }
132 });
133
134 (internal_id, target_id)
135}
136
137#[benchmarks(
138 where
139 T::Fungibles: FungiblesCreate<T::AccountId>,
140)]
141mod benchmarks {
142 use super::*;
143
144 #[benchmark]
148 fn mint(n: Linear<1, { T::MaxExternals::get() }>) -> Result<(), BenchmarkError> {
149 let caller: T::AccountId = whitelisted_caller();
150 let (internal_id, asset_id) = setup_assets::<T>(n);
151 let mint_amount = BalanceOf::<T>::from(BENCH_MIN_SWAP).saturating_mul(10u32.into());
152
153 T::Fungibles::mint_into(asset_id.clone(), &caller, mint_amount.saturating_mul(2u32.into()))
154 .map_err(|_| BenchmarkError::Stop("Failed to fund caller"))?;
155
156 let psm_account = Psm::<T>::psm_account(&internal_id);
157 let reserve_before = T::Fungibles::balance(asset_id.clone(), &psm_account);
158
159 #[extrinsic_call]
160 _(
161 RawOrigin::Signed(caller.clone()),
162 internal_id.clone(),
163 asset_id.clone(),
164 mint_amount,
165 MintingFee::<T>::get(&internal_id, &asset_id),
166 );
167
168 assert!(T::Fungibles::balance(asset_id, &psm_account) > reserve_before);
169 Ok(())
170 }
171
172 #[benchmark]
173 fn redeem() -> Result<(), BenchmarkError> {
174 let caller: T::AccountId = whitelisted_caller();
175 let (internal_id, asset_id) = setup_assets::<T>(1);
176 let setup_amount = BalanceOf::<T>::from(BENCH_MIN_SWAP).saturating_mul(10u32.into());
177 let redeem_amount = BalanceOf::<T>::from(BENCH_MIN_SWAP);
178
179 T::Fungibles::mint_into(
180 asset_id.clone(),
181 &caller,
182 setup_amount.saturating_mul(2u32.into()),
183 )
184 .map_err(|_| BenchmarkError::Stop("Failed to fund caller"))?;
185 Psm::<T>::mint(
186 RawOrigin::Signed(caller.clone()).into(),
187 internal_id.clone(),
188 asset_id.clone(),
189 setup_amount,
190 MintingFee::<T>::get(&internal_id, &asset_id),
191 )
192 .map_err(|_| BenchmarkError::Stop("Failed to setup reserve via mint"))?;
193
194 let psm_account = Psm::<T>::psm_account(&internal_id);
195 let reserve_before = T::Fungibles::balance(asset_id.clone(), &psm_account);
196
197 #[extrinsic_call]
198 _(
199 RawOrigin::Signed(caller.clone()),
200 internal_id.clone(),
201 asset_id.clone(),
202 redeem_amount,
203 RedemptionFee::<T>::get(&internal_id, &asset_id),
204 );
205
206 assert!(T::Fungibles::balance(asset_id, &psm_account) < reserve_before);
207 Ok(())
208 }
209
210 #[benchmark]
211 fn set_minting_fee() -> Result<(), BenchmarkError> {
212 let (internal_id, asset_id) = setup_assets::<T>(1);
213 let new_fee = Permill::from_percent(2);
214
215 #[extrinsic_call]
216 _(RawOrigin::Root, internal_id.clone(), asset_id.clone(), new_fee);
217
218 assert_eq!(crate::MintingFee::<T>::get(&internal_id, &asset_id), new_fee);
219 Ok(())
220 }
221
222 #[benchmark]
223 fn set_redemption_fee() -> Result<(), BenchmarkError> {
224 let (internal_id, asset_id) = setup_assets::<T>(1);
225 let new_fee = Permill::from_percent(2);
226
227 #[extrinsic_call]
228 _(RawOrigin::Root, internal_id.clone(), asset_id.clone(), new_fee);
229
230 assert_eq!(crate::RedemptionFee::<T>::get(&internal_id, &asset_id), new_fee);
231 Ok(())
232 }
233
234 #[benchmark]
235 fn set_max_debt() -> Result<(), BenchmarkError> {
236 let (internal_id, _) = ensure_internal_setup::<T>();
237 let new_value = BalanceOf::<T>::from(123u32);
238
239 #[extrinsic_call]
240 _(RawOrigin::Root, internal_id.clone(), new_value);
241
242 assert_eq!(crate::Psm::<T>::get(&internal_id).unwrap().max_debt, new_value);
243 Ok(())
244 }
245
246 #[benchmark]
247 fn set_asset_status() -> Result<(), BenchmarkError> {
248 let (internal_id, asset_id) = setup_assets::<T>(1);
249 let new_status = CircuitBreakerLevel::MintingDisabled;
250
251 #[extrinsic_call]
252 _(RawOrigin::Root, internal_id.clone(), asset_id.clone(), new_status);
253
254 assert_eq!(
255 crate::ExternalAssets::<T>::get(&internal_id, &asset_id).map(|e| e.status),
256 Some(new_status),
257 );
258 Ok(())
259 }
260
261 #[benchmark]
262 fn set_asset_ceiling_weight() -> Result<(), BenchmarkError> {
263 let (internal_id, asset_id) = setup_assets::<T>(1);
264 let new_weight = Permill::from_percent(50);
265
266 #[extrinsic_call]
267 _(RawOrigin::Root, internal_id.clone(), asset_id.clone(), new_weight);
268
269 assert_eq!(crate::AssetCeilingWeight::<T>::get(&internal_id, &asset_id), new_weight);
270 Ok(())
271 }
272 #[benchmark]
273 fn add_external_asset() -> Result<(), BenchmarkError> {
274 let (internal_id, internal_decimals) = ensure_internal_setup::<T>();
277 let caller: T::AccountId = whitelisted_caller();
278 let new_asset_id: T::AssetId = T::BenchmarkHelper::get_asset_id(EXTERNAL_ASSET_OFFSET);
279
280 let _ = T::Fungibles::create(new_asset_id.clone(), caller.clone(), false, 1u32.into());
283 T::BenchmarkHelper::create_asset(new_asset_id.clone(), &caller, internal_decimals);
284
285 #[extrinsic_call]
286 _(RawOrigin::Root, internal_id.clone(), new_asset_id.clone());
287
288 assert!(crate::ExternalAssets::<T>::contains_key(&internal_id, &new_asset_id));
289 Ok(())
290 }
291
292 #[benchmark]
293 fn remove_external_asset() -> Result<(), BenchmarkError> {
294 let (internal_id, asset_id) = setup_assets::<T>(1);
295 crate::PsmDebt::<T>::remove(&internal_id, &asset_id);
296
297 #[extrinsic_call]
298 _(RawOrigin::Root, internal_id.clone(), asset_id.clone());
299
300 assert!(!crate::ExternalAssets::<T>::contains_key(&internal_id, &asset_id));
301 Ok(())
302 }
303
304 #[benchmark]
305 fn create_psm() -> Result<(), BenchmarkError> {
306 let caller: T::AccountId = whitelisted_caller();
307 let _ = frame_system::Pallet::<T>::inc_providers(&caller);
308
309 let internal_id: T::AssetId = T::BenchmarkHelper::get_asset_id(INTERNAL_ASSET_INDEX + 1);
311 if !T::Fungibles::asset_exists(internal_id.clone()) {
312 let _ = T::Fungibles::create(internal_id.clone(), caller.clone(), true, 1u32.into());
313 }
314
315 let origin = T::CreateOrigin::try_successful_origin(&internal_id)
318 .map_err(|_| BenchmarkError::Weightless)?;
319 let maybe_depositor = T::CreateOrigin::ensure_origin(origin.clone(), &internal_id)
320 .map_err(|_| BenchmarkError::Stop("CreateOrigin failed"))?;
321 if let Some(who) = &maybe_depositor {
322 T::Consideration::ensure_successful(who, Psm::<T>::psm_creation_footprint());
323 }
324
325 let admin: T::PalletsOrigin =
326 maybe_depositor.map(RawOrigin::Signed).unwrap_or(RawOrigin::Root).into();
327 let max_debt = BalanceOf::<T>::from(u32::MAX);
328 let min_swap = BalanceOf::<T>::from(BENCH_MIN_SWAP);
329
330 #[extrinsic_call]
331 _(
332 origin,
333 internal_id.clone(),
334 Box::new(admin.clone()),
335 Box::new(admin),
336 caller,
337 max_debt,
338 min_swap,
339 );
340
341 assert!(crate::Psm::<T>::contains_key(&internal_id));
342 Ok(())
343 }
344
345 #[benchmark]
346 fn remove_psm() -> Result<(), BenchmarkError> {
347 let (internal_id, _) = ensure_internal_setup::<T>();
350
351 #[extrinsic_call]
352 _(RawOrigin::Root, internal_id.clone());
353
354 assert!(!crate::Psm::<T>::contains_key(&internal_id));
355 Ok(())
356 }
357
358 #[benchmark]
359 fn set_full_admin() -> Result<(), BenchmarkError> {
360 let (internal_id, _) = ensure_internal_setup::<T>();
361 let new_admin: T::PalletsOrigin = RawOrigin::Signed(whitelisted_caller()).into();
362
363 #[extrinsic_call]
364 _(RawOrigin::Root, internal_id.clone(), Box::new(new_admin.clone()));
365
366 assert_eq!(crate::PsmAdmin::<T>::get(&internal_id).unwrap().full_admin, new_admin);
367 Ok(())
368 }
369
370 #[benchmark]
371 fn set_emergency_admin() -> Result<(), BenchmarkError> {
372 let (internal_id, _) = ensure_internal_setup::<T>();
373 let new_admin: T::PalletsOrigin = RawOrigin::Signed(whitelisted_caller()).into();
374
375 #[extrinsic_call]
376 _(RawOrigin::Root, internal_id.clone(), Box::new(new_admin.clone()));
377
378 assert_eq!(crate::PsmAdmin::<T>::get(&internal_id).unwrap().emergency_admin, new_admin);
379 Ok(())
380 }
381
382 impl_benchmark_test_suite!(Psm, crate::mock::new_test_ext(), crate::mock::Test);
383}