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 }
309 .into(),
310 );
311
312 Ok(())
313 }
314
315 #[benchmark]
316 fn kill_pure(p: Linear<0, { T::MaxProxies::get() - 2 }>) -> Result<(), BenchmarkError> {
317 let caller: T::AccountId = whitelisted_caller();
318 let caller_lookup = T::Lookup::unlookup(caller.clone());
319 T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
320 Pallet::<T>::create_pure(
321 RawOrigin::Signed(whitelisted_caller()).into(),
322 T::ProxyType::default(),
323 BlockNumberFor::<T>::zero(),
324 0,
325 )?;
326 let height = T::BlockNumberProvider::current_block_number();
327 let ext_index = frame_system::Pallet::<T>::extrinsic_index().unwrap_or(0);
328 let pure_account = Pallet::<T>::pure_account(&caller, &T::ProxyType::default(), 0, None);
329
330 add_proxies::<T>(p, Some(pure_account.clone()))?;
331 ensure!(Proxies::<T>::contains_key(&pure_account), "pure proxy not created");
332
333 #[extrinsic_call]
334 _(
335 RawOrigin::Signed(pure_account.clone()),
336 caller_lookup,
337 T::ProxyType::default(),
338 0,
339 height,
340 ext_index,
341 );
342
343 assert!(!Proxies::<T>::contains_key(&pure_account));
344
345 Ok(())
346 }
347
348 #[benchmark]
349 fn poke_deposit() -> Result<(), BenchmarkError> {
350 let account_1: T::AccountId = account("account", 1, SEED);
352 let account_2: T::AccountId = account("account", 2, SEED);
353 let account_3: T::AccountId = account("account", 3, SEED);
354
355 T::Currency::make_free_balance_be(&account_1, BalanceOf::<T>::max_value() / 100u8.into());
357 T::Currency::make_free_balance_be(&account_2, BalanceOf::<T>::max_value() / 100u8.into());
358 T::Currency::make_free_balance_be(&account_3, BalanceOf::<T>::max_value() / 100u8.into());
359
360 Proxy::<T>::add_proxy(
362 RawOrigin::Signed(account_1.clone()).into(),
363 T::Lookup::unlookup(account_2.clone()),
364 T::ProxyType::default(),
365 BlockNumberFor::<T>::zero(),
366 )?;
367 Proxy::<T>::add_proxy(
368 RawOrigin::Signed(account_2.clone()).into(),
369 T::Lookup::unlookup(account_3.clone()),
370 T::ProxyType::default(),
371 BlockNumberFor::<T>::zero(),
372 )?;
373 let (proxies, initial_proxy_deposit) = Proxies::<T>::get(&account_2);
374 assert!(!initial_proxy_deposit.is_zero());
375 assert_eq!(initial_proxy_deposit, T::Currency::reserved_balance(&account_2));
376
377 Proxy::<T>::announce(
379 RawOrigin::Signed(account_2.clone()).into(),
380 T::Lookup::unlookup(account_1.clone()),
381 T::CallHasher::hash_of(&("add_announcement", 1)),
382 )?;
383 let (announcements, initial_announcement_deposit) = Announcements::<T>::get(&account_2);
384 assert!(!initial_announcement_deposit.is_zero());
385 assert_eq!(
386 initial_announcement_deposit.saturating_add(initial_proxy_deposit),
387 T::Currency::reserved_balance(&account_2)
388 );
389
390 let extra_proxy_deposit = initial_proxy_deposit; let extra_announcement_deposit = initial_announcement_deposit; let total = extra_proxy_deposit.saturating_add(extra_announcement_deposit);
394
395 T::Currency::reserve(&account_2, total)?;
396
397 let initial_reserved = T::Currency::reserved_balance(&account_2);
398 assert_eq!(initial_reserved, total.saturating_add(total)); Proxies::<T>::insert(
402 &account_2,
403 (proxies, initial_proxy_deposit.saturating_add(extra_proxy_deposit)),
404 );
405 Announcements::<T>::insert(
406 &account_2,
407 (
408 announcements,
409 initial_announcement_deposit.saturating_add(extra_announcement_deposit),
410 ),
411 );
412
413 let (_, inflated_proxy_deposit) = Proxies::<T>::get(&account_2);
415 let (_, inflated_announcement_deposit) = Announcements::<T>::get(&account_2);
416 assert_eq!(
417 inflated_proxy_deposit,
418 initial_proxy_deposit.saturating_add(extra_proxy_deposit)
419 );
420 assert_eq!(
421 inflated_announcement_deposit,
422 initial_announcement_deposit.saturating_add(extra_announcement_deposit)
423 );
424
425 #[extrinsic_call]
426 _(RawOrigin::Signed(account_2.clone()));
427
428 let (_, final_proxy_deposit) = Proxies::<T>::get(&account_2);
430 let (_, final_announcement_deposit) = Announcements::<T>::get(&account_2);
431 assert_eq!(final_proxy_deposit, initial_proxy_deposit);
432 assert_eq!(final_announcement_deposit, initial_announcement_deposit);
433
434 let final_reserved = T::Currency::reserved_balance(&account_2);
435 assert_eq!(final_reserved, initial_reserved.saturating_sub(total));
436
437 assert_has_event::<T>(
439 Event::DepositPoked {
440 who: account_2.clone(),
441 kind: DepositKind::Proxies,
442 old_deposit: inflated_proxy_deposit,
443 new_deposit: final_proxy_deposit,
444 }
445 .into(),
446 );
447 assert_last_event::<T>(
448 Event::DepositPoked {
449 who: account_2,
450 kind: DepositKind::Announcements,
451 old_deposit: inflated_announcement_deposit,
452 new_deposit: final_announcement_deposit,
453 }
454 .into(),
455 );
456
457 Ok(())
458 }
459
460 impl_benchmark_test_suite!(Proxy, crate::tests::new_test_ext(), crate::tests::Test);
461}