1#![cfg(feature = "runtime-benchmarks")]
21
22use super::*;
23use crate::Pallet as Proxy;
24use alloc::{boxed::Box, vec};
25use frame::benchmarking::prelude::{
26 account, benchmarks, impl_test_function, whitelisted_caller, BenchmarkError, RawOrigin,
27};
28
29const SEED: u32 = 0;
30
31fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
32 frame_system::Pallet::<T>::assert_last_event(generic_event.into());
33}
34
35fn assert_has_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
36 frame_system::Pallet::<T>::assert_has_event(generic_event.into());
37}
38
39fn add_proxies<T: Config>(n: u32, maybe_who: Option<T::AccountId>) -> Result<(), &'static str> {
40 let caller = maybe_who.unwrap_or_else(whitelisted_caller);
41 T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value() / 2u32.into());
42 for i in 0..n {
43 let real = T::Lookup::unlookup(account("target", i, SEED));
44
45 Proxy::<T>::add_proxy(
46 RawOrigin::Signed(caller.clone()).into(),
47 real,
48 T::ProxyType::default(),
49 BlockNumberFor::<T>::zero(),
50 )?;
51 }
52 Ok(())
53}
54
55fn add_announcements<T: Config>(
56 n: u32,
57 maybe_who: Option<T::AccountId>,
58 maybe_real: Option<T::AccountId>,
59) -> Result<(), &'static str> {
60 let caller = maybe_who.unwrap_or_else(|| account("caller", 0, SEED));
61 let caller_lookup = T::Lookup::unlookup(caller.clone());
62 T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value() / 2u32.into());
63 let real = if let Some(real) = maybe_real {
64 real
65 } else {
66 let real = account("real", 0, SEED);
67 T::Currency::make_free_balance_be(&real, BalanceOf::<T>::max_value() / 2u32.into());
68 Proxy::<T>::add_proxy(
69 RawOrigin::Signed(real.clone()).into(),
70 caller_lookup,
71 T::ProxyType::default(),
72 BlockNumberFor::<T>::zero(),
73 )?;
74 real
75 };
76 let real_lookup = T::Lookup::unlookup(real);
77 for _ in 0..n {
78 Proxy::<T>::announce(
79 RawOrigin::Signed(caller.clone()).into(),
80 real_lookup.clone(),
81 T::CallHasher::hash_of(&("add_announcement", n)),
82 )?;
83 }
84 Ok(())
85}
86
87#[benchmarks]
88mod benchmarks {
89 use super::*;
90
91 #[benchmark]
92 fn proxy(p: Linear<1, { T::MaxProxies::get() - 1 }>) -> Result<(), BenchmarkError> {
93 add_proxies::<T>(p, None)?;
94 let caller: T::AccountId = account("target", p - 1, SEED);
96 T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value() / 2u32.into());
97 let real: T::AccountId = whitelisted_caller();
99 let real_lookup = T::Lookup::unlookup(real);
100 let call: <T as Config>::RuntimeCall =
101 frame_system::Call::<T>::remark { remark: vec![] }.into();
102
103 #[extrinsic_call]
104 _(RawOrigin::Signed(caller), real_lookup, Some(T::ProxyType::default()), Box::new(call));
105
106 assert_last_event::<T>(Event::ProxyExecuted { result: Ok(()) }.into());
107
108 Ok(())
109 }
110
111 #[benchmark]
112 fn proxy_announced(
113 a: Linear<0, { T::MaxPending::get() - 1 }>,
114 p: Linear<1, { T::MaxProxies::get() - 1 }>,
115 ) -> Result<(), BenchmarkError> {
116 add_proxies::<T>(p, None)?;
117 let caller: T::AccountId = account("pure", 0, SEED);
119 let delegate: T::AccountId = account("target", p - 1, SEED);
120 let delegate_lookup = T::Lookup::unlookup(delegate.clone());
121 T::Currency::make_free_balance_be(&delegate, BalanceOf::<T>::max_value() / 2u32.into());
122 let real: T::AccountId = whitelisted_caller();
124 let real_lookup = T::Lookup::unlookup(real);
125 let call: <T as Config>::RuntimeCall =
126 frame_system::Call::<T>::remark { remark: vec![] }.into();
127 Proxy::<T>::announce(
128 RawOrigin::Signed(delegate.clone()).into(),
129 real_lookup.clone(),
130 T::CallHasher::hash_of(&call),
131 )?;
132 add_announcements::<T>(a, Some(delegate.clone()), None)?;
133
134 #[extrinsic_call]
135 _(
136 RawOrigin::Signed(caller),
137 delegate_lookup,
138 real_lookup,
139 Some(T::ProxyType::default()),
140 Box::new(call),
141 );
142
143 assert_last_event::<T>(Event::ProxyExecuted { result: Ok(()) }.into());
144
145 Ok(())
146 }
147
148 #[benchmark]
149 fn remove_announcement(
150 a: Linear<0, { T::MaxPending::get() - 1 }>,
151 p: Linear<1, { T::MaxProxies::get() - 1 }>,
152 ) -> Result<(), BenchmarkError> {
153 add_proxies::<T>(p, None)?;
154 let caller: T::AccountId = account("target", p - 1, SEED);
156 T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value() / 2u32.into());
157 let real: T::AccountId = whitelisted_caller();
159 let real_lookup = T::Lookup::unlookup(real);
160 let call: <T as Config>::RuntimeCall =
161 frame_system::Call::<T>::remark { remark: vec![] }.into();
162 Proxy::<T>::announce(
163 RawOrigin::Signed(caller.clone()).into(),
164 real_lookup.clone(),
165 T::CallHasher::hash_of(&call),
166 )?;
167 add_announcements::<T>(a, Some(caller.clone()), None)?;
168
169 #[extrinsic_call]
170 _(RawOrigin::Signed(caller.clone()), real_lookup, T::CallHasher::hash_of(&call));
171
172 let (announcements, _) = Announcements::<T>::get(&caller);
173 assert_eq!(announcements.len() as u32, a);
174
175 Ok(())
176 }
177
178 #[benchmark]
179 fn reject_announcement(
180 a: Linear<0, { T::MaxPending::get() - 1 }>,
181 p: Linear<1, { T::MaxProxies::get() - 1 }>,
182 ) -> Result<(), BenchmarkError> {
183 add_proxies::<T>(p, None)?;
184 let caller: T::AccountId = account("target", p - 1, SEED);
186 let caller_lookup = T::Lookup::unlookup(caller.clone());
187 T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value() / 2u32.into());
188 let real: T::AccountId = whitelisted_caller();
190 let real_lookup = T::Lookup::unlookup(real.clone());
191 let call: <T as Config>::RuntimeCall =
192 frame_system::Call::<T>::remark { remark: vec![] }.into();
193 Proxy::<T>::announce(
194 RawOrigin::Signed(caller.clone()).into(),
195 real_lookup,
196 T::CallHasher::hash_of(&call),
197 )?;
198 add_announcements::<T>(a, Some(caller.clone()), None)?;
199
200 #[extrinsic_call]
201 _(RawOrigin::Signed(real), caller_lookup, T::CallHasher::hash_of(&call));
202
203 let (announcements, _) = Announcements::<T>::get(&caller);
204 assert_eq!(announcements.len() as u32, a);
205
206 Ok(())
207 }
208
209 #[benchmark]
210 fn announce(
211 a: Linear<0, { T::MaxPending::get() - 1 }>,
212 p: Linear<1, { T::MaxProxies::get() - 1 }>,
213 ) -> Result<(), BenchmarkError> {
214 add_proxies::<T>(p, None)?;
215 let caller: T::AccountId = account("target", p - 1, SEED);
217 T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value() / 2u32.into());
218 let real: T::AccountId = whitelisted_caller();
220 let real_lookup = T::Lookup::unlookup(real.clone());
221 add_announcements::<T>(a, Some(caller.clone()), None)?;
222 let call: <T as Config>::RuntimeCall =
223 frame_system::Call::<T>::remark { remark: vec![] }.into();
224 let call_hash = T::CallHasher::hash_of(&call);
225
226 #[extrinsic_call]
227 _(RawOrigin::Signed(caller.clone()), real_lookup, call_hash);
228
229 assert_last_event::<T>(Event::Announced { real, proxy: caller, call_hash }.into());
230
231 Ok(())
232 }
233
234 #[benchmark]
235 fn add_proxy(p: Linear<1, { T::MaxProxies::get() - 1 }>) -> Result<(), BenchmarkError> {
236 add_proxies::<T>(p, None)?;
237 let caller: T::AccountId = whitelisted_caller();
238 let real = T::Lookup::unlookup(account("target", T::MaxProxies::get(), SEED));
239
240 #[extrinsic_call]
241 _(
242 RawOrigin::Signed(caller.clone()),
243 real,
244 T::ProxyType::default(),
245 BlockNumberFor::<T>::zero(),
246 );
247
248 let (proxies, _) = Proxies::<T>::get(caller);
249 assert_eq!(proxies.len() as u32, p + 1);
250
251 Ok(())
252 }
253
254 #[benchmark]
255 fn remove_proxy(p: Linear<1, { T::MaxProxies::get() - 1 }>) -> Result<(), BenchmarkError> {
256 add_proxies::<T>(p, None)?;
257 let caller: T::AccountId = whitelisted_caller();
258 let delegate = T::Lookup::unlookup(account("target", 0, SEED));
259
260 #[extrinsic_call]
261 _(
262 RawOrigin::Signed(caller.clone()),
263 delegate,
264 T::ProxyType::default(),
265 BlockNumberFor::<T>::zero(),
266 );
267
268 let (proxies, _) = Proxies::<T>::get(caller);
269 assert_eq!(proxies.len() as u32, p - 1);
270
271 Ok(())
272 }
273
274 #[benchmark]
275 fn remove_proxies(p: Linear<1, { T::MaxProxies::get() - 1 }>) -> Result<(), BenchmarkError> {
276 add_proxies::<T>(p, None)?;
277 let caller: T::AccountId = whitelisted_caller();
278
279 #[extrinsic_call]
280 _(RawOrigin::Signed(caller.clone()));
281
282 let (proxies, _) = Proxies::<T>::get(caller);
283 assert_eq!(proxies.len() as u32, 0);
284
285 Ok(())
286 }
287
288 #[benchmark]
289 fn create_pure(p: Linear<1, { T::MaxProxies::get() - 1 }>) -> Result<(), BenchmarkError> {
290 add_proxies::<T>(p, None)?;
291 let caller: T::AccountId = whitelisted_caller();
292
293 #[extrinsic_call]
294 _(
295 RawOrigin::Signed(caller.clone()),
296 T::ProxyType::default(),
297 BlockNumberFor::<T>::zero(),
298 0,
299 );
300
301 let pure_account = Pallet::<T>::pure_account(&caller, &T::ProxyType::default(), 0, None);
302 assert_last_event::<T>(
303 Event::PureCreated {
304 pure: pure_account,
305 who: caller,
306 proxy_type: T::ProxyType::default(),
307 disambiguation_index: 0,
308 at: <T as Config>::BlockNumberProvider::current_block_number(),
309 extrinsic_index: frame_system::Pallet::<T>::extrinsic_index().unwrap_or_default(),
310 }
311 .into(),
312 );
313
314 Ok(())
315 }
316
317 #[benchmark]
318 fn kill_pure(p: Linear<0, { T::MaxProxies::get() - 2 }>) -> Result<(), BenchmarkError> {
319 let caller: T::AccountId = whitelisted_caller();
320 let caller_lookup = T::Lookup::unlookup(caller.clone());
321 T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
322 Pallet::<T>::create_pure(
323 RawOrigin::Signed(whitelisted_caller()).into(),
324 T::ProxyType::default(),
325 BlockNumberFor::<T>::zero(),
326 0,
327 )?;
328 let height = T::BlockNumberProvider::current_block_number();
329 let ext_index = frame_system::Pallet::<T>::extrinsic_index().unwrap_or(0);
330 let pure_account = Pallet::<T>::pure_account(&caller, &T::ProxyType::default(), 0, None);
331
332 add_proxies::<T>(p, Some(pure_account.clone()))?;
333 ensure!(Proxies::<T>::contains_key(&pure_account), "pure proxy not created");
334
335 #[extrinsic_call]
336 _(
337 RawOrigin::Signed(pure_account.clone()),
338 caller_lookup,
339 T::ProxyType::default(),
340 0,
341 height,
342 ext_index,
343 );
344
345 assert!(!Proxies::<T>::contains_key(&pure_account));
346
347 Ok(())
348 }
349
350 #[benchmark]
351 fn poke_deposit() -> Result<(), BenchmarkError> {
352 let account_1: T::AccountId = account("account", 1, SEED);
354 let account_2: T::AccountId = account("account", 2, SEED);
355 let account_3: T::AccountId = account("account", 3, SEED);
356
357 T::Currency::make_free_balance_be(&account_1, BalanceOf::<T>::max_value() / 100u8.into());
359 T::Currency::make_free_balance_be(&account_2, BalanceOf::<T>::max_value() / 100u8.into());
360 T::Currency::make_free_balance_be(&account_3, BalanceOf::<T>::max_value() / 100u8.into());
361
362 Proxy::<T>::add_proxy(
364 RawOrigin::Signed(account_1.clone()).into(),
365 T::Lookup::unlookup(account_2.clone()),
366 T::ProxyType::default(),
367 BlockNumberFor::<T>::zero(),
368 )?;
369 Proxy::<T>::add_proxy(
370 RawOrigin::Signed(account_2.clone()).into(),
371 T::Lookup::unlookup(account_3.clone()),
372 T::ProxyType::default(),
373 BlockNumberFor::<T>::zero(),
374 )?;
375 let (proxies, initial_proxy_deposit) = Proxies::<T>::get(&account_2);
376 assert!(!initial_proxy_deposit.is_zero());
377 assert_eq!(initial_proxy_deposit, T::Currency::reserved_balance(&account_2));
378
379 Proxy::<T>::announce(
381 RawOrigin::Signed(account_2.clone()).into(),
382 T::Lookup::unlookup(account_1.clone()),
383 T::CallHasher::hash_of(&("add_announcement", 1)),
384 )?;
385 let (announcements, initial_announcement_deposit) = Announcements::<T>::get(&account_2);
386 assert!(!initial_announcement_deposit.is_zero());
387 assert_eq!(
388 initial_announcement_deposit.saturating_add(initial_proxy_deposit),
389 T::Currency::reserved_balance(&account_2)
390 );
391
392 let extra_proxy_deposit = initial_proxy_deposit; let extra_announcement_deposit = initial_announcement_deposit; let total = extra_proxy_deposit.saturating_add(extra_announcement_deposit);
396
397 T::Currency::reserve(&account_2, total)?;
398
399 let initial_reserved = T::Currency::reserved_balance(&account_2);
400 assert_eq!(initial_reserved, total.saturating_add(total)); Proxies::<T>::insert(
404 &account_2,
405 (proxies, initial_proxy_deposit.saturating_add(extra_proxy_deposit)),
406 );
407 Announcements::<T>::insert(
408 &account_2,
409 (
410 announcements,
411 initial_announcement_deposit.saturating_add(extra_announcement_deposit),
412 ),
413 );
414
415 let (_, inflated_proxy_deposit) = Proxies::<T>::get(&account_2);
417 let (_, inflated_announcement_deposit) = Announcements::<T>::get(&account_2);
418 assert_eq!(
419 inflated_proxy_deposit,
420 initial_proxy_deposit.saturating_add(extra_proxy_deposit)
421 );
422 assert_eq!(
423 inflated_announcement_deposit,
424 initial_announcement_deposit.saturating_add(extra_announcement_deposit)
425 );
426
427 #[extrinsic_call]
428 _(RawOrigin::Signed(account_2.clone()));
429
430 let (_, final_proxy_deposit) = Proxies::<T>::get(&account_2);
432 let (_, final_announcement_deposit) = Announcements::<T>::get(&account_2);
433 assert_eq!(final_proxy_deposit, initial_proxy_deposit);
434 assert_eq!(final_announcement_deposit, initial_announcement_deposit);
435
436 let final_reserved = T::Currency::reserved_balance(&account_2);
437 assert_eq!(final_reserved, initial_reserved.saturating_sub(total));
438
439 assert_has_event::<T>(
441 Event::DepositPoked {
442 who: account_2.clone(),
443 kind: DepositKind::Proxies,
444 old_deposit: inflated_proxy_deposit,
445 new_deposit: final_proxy_deposit,
446 }
447 .into(),
448 );
449 assert_last_event::<T>(
450 Event::DepositPoked {
451 who: account_2,
452 kind: DepositKind::Announcements,
453 old_deposit: inflated_announcement_deposit,
454 new_deposit: final_announcement_deposit,
455 }
456 .into(),
457 );
458
459 Ok(())
460 }
461
462 impl_benchmark_test_suite!(Proxy, crate::tests::new_test_ext(), crate::tests::Test);
463}