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, Footprint, 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, Footprint::from_parts(1, 0));
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 }
75 (internal_id, internal_decimals)
76}
77
78fn setup_assets<T: Config>(n: u32) -> (T::AssetId, T::AssetId)
89where
90 T::Fungibles: FungiblesCreate<T::AccountId>,
91{
92 let admin: T::AccountId = whitelisted_caller();
93 let _ = frame_system::Pallet::<T>::inc_providers(&admin);
94
95 let (internal_id, internal_decimals) = ensure_internal_setup::<T>();
96
97 let target_id: T::AssetId = T::BenchmarkHelper::get_asset_id(EXTERNAL_ASSET_OFFSET);
102 if !T::Fungibles::asset_exists(target_id.clone()) {
103 T::BenchmarkHelper::create_asset(target_id.clone(), &admin, internal_decimals);
104 }
105
106 for i in 0..n {
110 let id: T::AssetId = T::BenchmarkHelper::get_asset_id(EXTERNAL_ASSET_OFFSET + i);
111 crate::ExternalAssets::<T>::insert(
112 &internal_id,
113 &id,
114 crate::ExternalAssetInfo {
115 status: CircuitBreakerLevel::AllEnabled,
116 decimals: internal_decimals,
117 },
118 );
119 crate::AssetCeilingWeight::<T>::insert(&internal_id, &id, Permill::from_percent(1));
120 crate::PsmDebt::<T>::insert(&internal_id, &id, BalanceOf::<T>::from(1u32));
121 }
122 crate::AssetCeilingWeight::<T>::insert(&internal_id, &target_id, Permill::from_percent(100));
124
125 crate::Psm::<T>::mutate(&internal_id, |maybe| {
127 if let Some(info) = maybe.as_mut() {
128 info.external_count = n.max(1);
129 }
130 });
131
132 (internal_id, target_id)
133}
134
135#[benchmarks(
136 where
137 T::Fungibles: FungiblesCreate<T::AccountId>,
138)]
139mod benchmarks {
140 use super::*;
141
142 #[benchmark]
146 fn mint(n: Linear<1, { T::MaxExternals::get() }>) -> Result<(), BenchmarkError> {
147 let caller: T::AccountId = whitelisted_caller();
148 let (internal_id, asset_id) = setup_assets::<T>(n);
149 let mint_amount = BalanceOf::<T>::from(BENCH_MIN_SWAP).saturating_mul(10u32.into());
150
151 T::Fungibles::mint_into(asset_id.clone(), &caller, mint_amount.saturating_mul(2u32.into()))
152 .map_err(|_| BenchmarkError::Stop("Failed to fund caller"))?;
153
154 let psm_account = Psm::<T>::psm_account(&internal_id);
155 let reserve_before = T::Fungibles::balance(asset_id.clone(), &psm_account);
156
157 #[extrinsic_call]
158 _(
159 RawOrigin::Signed(caller.clone()),
160 internal_id.clone(),
161 asset_id.clone(),
162 mint_amount,
163 MintingFee::<T>::get(&internal_id, &asset_id),
164 );
165
166 assert!(T::Fungibles::balance(asset_id, &psm_account) > reserve_before);
167 Ok(())
168 }
169
170 #[benchmark]
171 fn redeem() -> Result<(), BenchmarkError> {
172 let caller: T::AccountId = whitelisted_caller();
173 let (internal_id, asset_id) = setup_assets::<T>(1);
174 let setup_amount = BalanceOf::<T>::from(BENCH_MIN_SWAP).saturating_mul(10u32.into());
175 let redeem_amount = BalanceOf::<T>::from(BENCH_MIN_SWAP);
176
177 T::Fungibles::mint_into(
178 asset_id.clone(),
179 &caller,
180 setup_amount.saturating_mul(2u32.into()),
181 )
182 .map_err(|_| BenchmarkError::Stop("Failed to fund caller"))?;
183 Psm::<T>::mint(
184 RawOrigin::Signed(caller.clone()).into(),
185 internal_id.clone(),
186 asset_id.clone(),
187 setup_amount,
188 MintingFee::<T>::get(&internal_id, &asset_id),
189 )
190 .map_err(|_| BenchmarkError::Stop("Failed to setup reserve via mint"))?;
191
192 let psm_account = Psm::<T>::psm_account(&internal_id);
193 let reserve_before = T::Fungibles::balance(asset_id.clone(), &psm_account);
194
195 #[extrinsic_call]
196 _(
197 RawOrigin::Signed(caller.clone()),
198 internal_id.clone(),
199 asset_id.clone(),
200 redeem_amount,
201 RedemptionFee::<T>::get(&internal_id, &asset_id),
202 );
203
204 assert!(T::Fungibles::balance(asset_id, &psm_account) < reserve_before);
205 Ok(())
206 }
207
208 #[benchmark]
209 fn set_minting_fee() -> Result<(), BenchmarkError> {
210 let (internal_id, asset_id) = setup_assets::<T>(1);
211 let new_fee = Permill::from_percent(2);
212
213 #[extrinsic_call]
214 _(RawOrigin::Root, internal_id.clone(), asset_id.clone(), new_fee);
215
216 assert_eq!(crate::MintingFee::<T>::get(&internal_id, &asset_id), new_fee);
217 Ok(())
218 }
219
220 #[benchmark]
221 fn set_redemption_fee() -> Result<(), BenchmarkError> {
222 let (internal_id, asset_id) = setup_assets::<T>(1);
223 let new_fee = Permill::from_percent(2);
224
225 #[extrinsic_call]
226 _(RawOrigin::Root, internal_id.clone(), asset_id.clone(), new_fee);
227
228 assert_eq!(crate::RedemptionFee::<T>::get(&internal_id, &asset_id), new_fee);
229 Ok(())
230 }
231
232 #[benchmark]
233 fn set_max_debt() -> Result<(), BenchmarkError> {
234 let (internal_id, _) = ensure_internal_setup::<T>();
235 let new_value = BalanceOf::<T>::from(123u32);
236
237 #[extrinsic_call]
238 _(RawOrigin::Root, internal_id.clone(), new_value);
239
240 assert_eq!(crate::Psm::<T>::get(&internal_id).unwrap().max_debt, new_value);
241 Ok(())
242 }
243
244 #[benchmark]
245 fn set_asset_status() -> Result<(), BenchmarkError> {
246 let (internal_id, asset_id) = setup_assets::<T>(1);
247 let new_status = CircuitBreakerLevel::MintingDisabled;
248
249 #[extrinsic_call]
250 _(RawOrigin::Root, internal_id.clone(), asset_id.clone(), new_status);
251
252 assert_eq!(
253 crate::ExternalAssets::<T>::get(&internal_id, &asset_id).map(|e| e.status),
254 Some(new_status),
255 );
256 Ok(())
257 }
258
259 #[benchmark]
260 fn set_asset_ceiling_weight() -> Result<(), BenchmarkError> {
261 let (internal_id, asset_id) = setup_assets::<T>(1);
262 let new_weight = Permill::from_percent(50);
263
264 #[extrinsic_call]
265 _(RawOrigin::Root, internal_id.clone(), asset_id.clone(), new_weight);
266
267 assert_eq!(crate::AssetCeilingWeight::<T>::get(&internal_id, &asset_id), new_weight);
268 Ok(())
269 }
270 #[benchmark]
271 fn add_external_asset() -> Result<(), BenchmarkError> {
272 let (internal_id, internal_decimals) = ensure_internal_setup::<T>();
275 let caller: T::AccountId = whitelisted_caller();
276 let new_asset_id: T::AssetId = T::BenchmarkHelper::get_asset_id(EXTERNAL_ASSET_OFFSET);
277
278 T::BenchmarkHelper::create_asset(new_asset_id.clone(), &caller, internal_decimals);
279
280 #[extrinsic_call]
281 _(RawOrigin::Root, internal_id.clone(), new_asset_id.clone());
282
283 assert!(crate::ExternalAssets::<T>::contains_key(&internal_id, &new_asset_id));
284 Ok(())
285 }
286
287 #[benchmark]
288 fn remove_external_asset() -> Result<(), BenchmarkError> {
289 let (internal_id, asset_id) = setup_assets::<T>(1);
290 crate::PsmDebt::<T>::remove(&internal_id, &asset_id);
291
292 #[extrinsic_call]
293 _(RawOrigin::Root, internal_id.clone(), asset_id.clone());
294
295 assert!(!crate::ExternalAssets::<T>::contains_key(&internal_id, &asset_id));
296 Ok(())
297 }
298
299 #[benchmark]
300 fn create_psm() -> Result<(), BenchmarkError> {
301 let caller: T::AccountId = whitelisted_caller();
302 let _ = frame_system::Pallet::<T>::inc_providers(&caller);
303
304 let internal_id: T::AssetId = T::BenchmarkHelper::get_asset_id(INTERNAL_ASSET_INDEX + 1);
306 if !T::Fungibles::asset_exists(internal_id.clone()) {
307 let _ = T::Fungibles::create(internal_id.clone(), caller.clone(), true, 1u32.into());
308 }
309
310 let origin = T::CreateOrigin::try_successful_origin(&internal_id)
313 .map_err(|_| BenchmarkError::Weightless)?;
314 let maybe_depositor = T::CreateOrigin::ensure_origin(origin.clone(), &internal_id)
315 .map_err(|_| BenchmarkError::Stop("CreateOrigin failed"))?;
316 if let Some(who) = &maybe_depositor {
317 T::Consideration::ensure_successful(who, Footprint::from_parts(1, 0));
318 }
319
320 let admin: T::PalletsOrigin =
321 maybe_depositor.map(RawOrigin::Signed).unwrap_or(RawOrigin::Root).into();
322 let max_debt = BalanceOf::<T>::from(u32::MAX);
323 let min_swap = BalanceOf::<T>::from(BENCH_MIN_SWAP);
324
325 #[extrinsic_call]
326 _(
327 origin,
328 internal_id.clone(),
329 Box::new(admin.clone()),
330 Box::new(admin),
331 caller,
332 max_debt,
333 min_swap,
334 );
335
336 assert!(crate::Psm::<T>::contains_key(&internal_id));
337 Ok(())
338 }
339
340 #[benchmark]
341 fn remove_psm() -> Result<(), BenchmarkError> {
342 let (internal_id, _) = ensure_internal_setup::<T>();
345
346 #[extrinsic_call]
347 _(RawOrigin::Root, internal_id.clone());
348
349 assert!(!crate::Psm::<T>::contains_key(&internal_id));
350 Ok(())
351 }
352
353 #[benchmark]
354 fn set_full_admin() -> Result<(), BenchmarkError> {
355 let (internal_id, _) = ensure_internal_setup::<T>();
356 let new_admin: T::PalletsOrigin = RawOrigin::Signed(whitelisted_caller()).into();
357
358 #[extrinsic_call]
359 _(RawOrigin::Root, internal_id.clone(), Box::new(new_admin.clone()));
360
361 assert_eq!(crate::PsmAdmin::<T>::get(&internal_id).unwrap().full_admin, new_admin);
362 Ok(())
363 }
364
365 #[benchmark]
366 fn set_emergency_admin() -> Result<(), BenchmarkError> {
367 let (internal_id, _) = ensure_internal_setup::<T>();
368 let new_admin: T::PalletsOrigin = RawOrigin::Signed(whitelisted_caller()).into();
369
370 #[extrinsic_call]
371 _(RawOrigin::Root, internal_id.clone(), Box::new(new_admin.clone()));
372
373 assert_eq!(crate::PsmAdmin::<T>::get(&internal_id).unwrap().emergency_admin, new_admin);
374 Ok(())
375 }
376
377 impl_benchmark_test_suite!(Psm, crate::mock::new_test_ext(), crate::mock::Test);
378}