pallet_proxy/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Proxy Pallet
19//! A pallet allowing accounts to give permission to other accounts to dispatch types of calls from
20//! their signed origin.
21//!
22//! The accounts to which permission is delegated may be required to announce the action that they
23//! wish to execute some duration prior to execution happens. In this case, the target account may
24//! reject the announcement and in doing so, veto the execution.
25//!
26//! - [`Config`]
27//! - [`Call`]
28
29// Ensure we're `no_std` when compiling for Wasm.
30#![cfg_attr(not(feature = "std"), no_std)]
31
32mod benchmarking;
33mod tests;
34pub mod weights;
35
36extern crate alloc;
37use alloc::{boxed::Box, vec};
38use frame::{
39	prelude::*,
40	traits::{Currency, InstanceFilter, ReservableCurrency},
41};
42pub use pallet::*;
43pub use weights::WeightInfo;
44
45type CallHashOf<T> = <<T as Config>::CallHasher as Hash>::Output;
46
47type BalanceOf<T> =
48	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
49
50pub type BlockNumberFor<T> =
51	<<T as Config>::BlockNumberProvider as BlockNumberProvider>::BlockNumber;
52
53type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
54
55/// The parameters under which a particular account has a proxy relationship with some other
56/// account.
57#[derive(
58	Encode,
59	Decode,
60	DecodeWithMemTracking,
61	Clone,
62	Copy,
63	Eq,
64	PartialEq,
65	Ord,
66	PartialOrd,
67	RuntimeDebug,
68	MaxEncodedLen,
69	TypeInfo,
70)]
71pub struct ProxyDefinition<AccountId, ProxyType, BlockNumber> {
72	/// The account which may act on behalf of another.
73	pub delegate: AccountId,
74	/// A value defining the subset of calls that it is allowed to make.
75	pub proxy_type: ProxyType,
76	/// The number of blocks that an announcement must be in place for before the corresponding
77	/// call may be dispatched. If zero, then no announcement is needed.
78	pub delay: BlockNumber,
79}
80
81/// Details surrounding a specific instance of an announcement to make a call.
82#[derive(
83	Encode,
84	Decode,
85	DecodeWithMemTracking,
86	Clone,
87	Copy,
88	Eq,
89	PartialEq,
90	RuntimeDebug,
91	MaxEncodedLen,
92	TypeInfo,
93)]
94pub struct Announcement<AccountId, Hash, BlockNumber> {
95	/// The account which made the announcement.
96	real: AccountId,
97	/// The hash of the call to be made.
98	call_hash: Hash,
99	/// The height at which the announcement was made.
100	height: BlockNumber,
101}
102
103/// The type of deposit
104#[derive(
105	Encode,
106	Decode,
107	Clone,
108	Copy,
109	Eq,
110	PartialEq,
111	RuntimeDebug,
112	MaxEncodedLen,
113	TypeInfo,
114	DecodeWithMemTracking,
115)]
116pub enum DepositKind {
117	/// Proxy registration deposit
118	Proxies,
119	/// Announcement deposit
120	Announcements,
121}
122
123#[frame::pallet]
124pub mod pallet {
125	use super::*;
126
127	#[pallet::pallet]
128	pub struct Pallet<T>(_);
129
130	/// Configuration trait.
131	#[pallet::config]
132	pub trait Config: frame_system::Config {
133		/// The overarching event type.
134		#[allow(deprecated)]
135		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
136
137		/// The overarching call type.
138		type RuntimeCall: Parameter
139			+ Dispatchable<RuntimeOrigin = Self::RuntimeOrigin>
140			+ GetDispatchInfo
141			+ From<frame_system::Call<Self>>
142			+ IsSubType<Call<Self>>
143			+ IsType<<Self as frame_system::Config>::RuntimeCall>;
144
145		/// The currency mechanism.
146		type Currency: ReservableCurrency<Self::AccountId>;
147
148		/// A kind of proxy; specified with the proxy and passed in to the `IsProxyable` filter.
149		/// The instance filter determines whether a given call may be proxied under this type.
150		///
151		/// IMPORTANT: `Default` must be provided and MUST BE the the *most permissive* value.
152		type ProxyType: Parameter
153			+ Member
154			+ Ord
155			+ PartialOrd
156			+ frame::traits::InstanceFilter<<Self as Config>::RuntimeCall>
157			+ Default
158			+ MaxEncodedLen;
159
160		/// The base amount of currency needed to reserve for creating a proxy.
161		///
162		/// This is held for an additional storage item whose value size is
163		/// `sizeof(Balance)` bytes and whose key size is `sizeof(AccountId)` bytes.
164		#[pallet::constant]
165		type ProxyDepositBase: Get<BalanceOf<Self>>;
166
167		/// The amount of currency needed per proxy added.
168		///
169		/// This is held for adding 32 bytes plus an instance of `ProxyType` more into a
170		/// pre-existing storage value. Thus, when configuring `ProxyDepositFactor` one should take
171		/// into account `32 + proxy_type.encode().len()` bytes of data.
172		#[pallet::constant]
173		type ProxyDepositFactor: Get<BalanceOf<Self>>;
174
175		/// The maximum amount of proxies allowed for a single account.
176		#[pallet::constant]
177		type MaxProxies: Get<u32>;
178
179		/// Weight information for extrinsics in this pallet.
180		type WeightInfo: WeightInfo;
181
182		/// The maximum amount of time-delayed announcements that are allowed to be pending.
183		#[pallet::constant]
184		type MaxPending: Get<u32>;
185
186		/// The type of hash used for hashing the call.
187		type CallHasher: Hash;
188
189		/// The base amount of currency needed to reserve for creating an announcement.
190		///
191		/// This is held when a new storage item holding a `Balance` is created (typically 16
192		/// bytes).
193		#[pallet::constant]
194		type AnnouncementDepositBase: Get<BalanceOf<Self>>;
195
196		/// The amount of currency needed per announcement made.
197		///
198		/// This is held for adding an `AccountId`, `Hash` and `BlockNumber` (typically 68 bytes)
199		/// into a pre-existing storage value.
200		#[pallet::constant]
201		type AnnouncementDepositFactor: Get<BalanceOf<Self>>;
202
203		/// Query the current block number.
204		///
205		/// Must return monotonically increasing values when called from consecutive blocks.
206		/// Can be configured to return either:
207		/// - the local block number of the runtime via `frame_system::Pallet`
208		/// - a remote block number, eg from the relay chain through `RelaychainDataProvider`
209		/// - an arbitrary value through a custom implementation of the trait
210		///
211		/// There is currently no migration provided to "hot-swap" block number providers and it may
212		/// result in undefined behavior when doing so. Parachains are therefore best off setting
213		/// this to their local block number provider if they have the pallet already deployed.
214		///
215		/// Suggested values:
216		/// - Solo- and Relay-chains: `frame_system::Pallet`
217		/// - Parachains that may produce blocks sparingly or only when needed (on-demand):
218		///   - already have the pallet deployed: `frame_system::Pallet`
219		///   - are freshly deploying this pallet: `RelaychainDataProvider`
220		/// - Parachains with a reliably block production rate (PLO or bulk-coretime):
221		///   - already have the pallet deployed: `frame_system::Pallet`
222		///   - are freshly deploying this pallet: no strong recommendation. Both local and remote
223		///     providers can be used. Relay provider can be a bit better in cases where the
224		///     parachain is lagging its block production to avoid clock skew.
225		type BlockNumberProvider: BlockNumberProvider;
226	}
227
228	#[pallet::call]
229	impl<T: Config> Pallet<T> {
230		/// Dispatch the given `call` from an account that the sender is authorised for through
231		/// `add_proxy`.
232		///
233		/// The dispatch origin for this call must be _Signed_.
234		///
235		/// Parameters:
236		/// - `real`: The account that the proxy will make a call on behalf of.
237		/// - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call.
238		/// - `call`: The call to be made by the `real` account.
239		#[pallet::call_index(0)]
240		#[pallet::weight({
241			let di = call.get_dispatch_info();
242			(T::WeightInfo::proxy(T::MaxProxies::get())
243				 // AccountData for inner call origin accountdata.
244				.saturating_add(T::DbWeight::get().reads_writes(1, 1))
245				.saturating_add(di.call_weight),
246			di.class)
247		})]
248		pub fn proxy(
249			origin: OriginFor<T>,
250			real: AccountIdLookupOf<T>,
251			force_proxy_type: Option<T::ProxyType>,
252			call: Box<<T as Config>::RuntimeCall>,
253		) -> DispatchResult {
254			let who = ensure_signed(origin)?;
255			let real = T::Lookup::lookup(real)?;
256			let def = Self::find_proxy(&real, &who, force_proxy_type)?;
257			ensure!(def.delay.is_zero(), Error::<T>::Unannounced);
258
259			Self::do_proxy(def, real, *call);
260
261			Ok(())
262		}
263
264		/// Register a proxy account for the sender that is able to make calls on its behalf.
265		///
266		/// The dispatch origin for this call must be _Signed_.
267		///
268		/// Parameters:
269		/// - `proxy`: The account that the `caller` would like to make a proxy.
270		/// - `proxy_type`: The permissions allowed for this proxy account.
271		/// - `delay`: The announcement period required of the initial proxy. Will generally be
272		/// zero.
273		#[pallet::call_index(1)]
274		#[pallet::weight(T::WeightInfo::add_proxy(T::MaxProxies::get()))]
275		pub fn add_proxy(
276			origin: OriginFor<T>,
277			delegate: AccountIdLookupOf<T>,
278			proxy_type: T::ProxyType,
279			delay: BlockNumberFor<T>,
280		) -> DispatchResult {
281			let who = ensure_signed(origin)?;
282			let delegate = T::Lookup::lookup(delegate)?;
283			Self::add_proxy_delegate(&who, delegate, proxy_type, delay)
284		}
285
286		/// Unregister a proxy account for the sender.
287		///
288		/// The dispatch origin for this call must be _Signed_.
289		///
290		/// Parameters:
291		/// - `proxy`: The account that the `caller` would like to remove as a proxy.
292		/// - `proxy_type`: The permissions currently enabled for the removed proxy account.
293		#[pallet::call_index(2)]
294		#[pallet::weight(T::WeightInfo::remove_proxy(T::MaxProxies::get()))]
295		pub fn remove_proxy(
296			origin: OriginFor<T>,
297			delegate: AccountIdLookupOf<T>,
298			proxy_type: T::ProxyType,
299			delay: BlockNumberFor<T>,
300		) -> DispatchResult {
301			let who = ensure_signed(origin)?;
302			let delegate = T::Lookup::lookup(delegate)?;
303			Self::remove_proxy_delegate(&who, delegate, proxy_type, delay)
304		}
305
306		/// Unregister all proxy accounts for the sender.
307		///
308		/// The dispatch origin for this call must be _Signed_.
309		///
310		/// WARNING: This may be called on accounts created by `create_pure`, however if done, then
311		/// the unreserved fees will be inaccessible. **All access to this account will be lost.**
312		#[pallet::call_index(3)]
313		#[pallet::weight(T::WeightInfo::remove_proxies(T::MaxProxies::get()))]
314		pub fn remove_proxies(origin: OriginFor<T>) -> DispatchResult {
315			let who = ensure_signed(origin)?;
316			Self::remove_all_proxy_delegates(&who);
317			Ok(())
318		}
319
320		/// Spawn a fresh new account that is guaranteed to be otherwise inaccessible, and
321		/// initialize it with a proxy of `proxy_type` for `origin` sender.
322		///
323		/// Requires a `Signed` origin.
324		///
325		/// - `proxy_type`: The type of the proxy that the sender will be registered as over the
326		/// new account. This will almost always be the most permissive `ProxyType` possible to
327		/// allow for maximum flexibility.
328		/// - `index`: A disambiguation index, in case this is called multiple times in the same
329		/// transaction (e.g. with `utility::batch`). Unless you're using `batch` you probably just
330		/// want to use `0`.
331		/// - `delay`: The announcement period required of the initial proxy. Will generally be
332		/// zero.
333		///
334		/// Fails with `Duplicate` if this has already been called in this transaction, from the
335		/// same sender, with the same parameters.
336		///
337		/// Fails if there are insufficient funds to pay for deposit.
338		#[pallet::call_index(4)]
339		#[pallet::weight(T::WeightInfo::create_pure(T::MaxProxies::get()))]
340		pub fn create_pure(
341			origin: OriginFor<T>,
342			proxy_type: T::ProxyType,
343			delay: BlockNumberFor<T>,
344			index: u16,
345		) -> DispatchResult {
346			let who = ensure_signed(origin)?;
347
348			let pure = Self::pure_account(&who, &proxy_type, index, None);
349			ensure!(!Proxies::<T>::contains_key(&pure), Error::<T>::Duplicate);
350
351			let proxy_def =
352				ProxyDefinition { delegate: who.clone(), proxy_type: proxy_type.clone(), delay };
353			let bounded_proxies: BoundedVec<_, T::MaxProxies> =
354				vec![proxy_def].try_into().map_err(|_| Error::<T>::TooMany)?;
355
356			let deposit = T::ProxyDepositBase::get() + T::ProxyDepositFactor::get();
357			T::Currency::reserve(&who, deposit)?;
358
359			Proxies::<T>::insert(&pure, (bounded_proxies, deposit));
360			Self::deposit_event(Event::PureCreated {
361				pure,
362				who,
363				proxy_type,
364				disambiguation_index: index,
365			});
366
367			Ok(())
368		}
369
370		/// Removes a previously spawned pure proxy.
371		///
372		/// WARNING: **All access to this account will be lost.** Any funds held in it will be
373		/// inaccessible.
374		///
375		/// Requires a `Signed` origin, and the sender account must have been created by a call to
376		/// `create_pure` with corresponding parameters.
377		///
378		/// - `spawner`: The account that originally called `create_pure` to create this account.
379		/// - `index`: The disambiguation index originally passed to `create_pure`. Probably `0`.
380		/// - `proxy_type`: The proxy type originally passed to `create_pure`.
381		/// - `height`: The height of the chain when the call to `create_pure` was processed.
382		/// - `ext_index`: The extrinsic index in which the call to `create_pure` was processed.
383		///
384		/// Fails with `NoPermission` in case the caller is not a previously created pure
385		/// account whose `create_pure` call has corresponding parameters.
386		#[pallet::call_index(5)]
387		#[pallet::weight(T::WeightInfo::kill_pure(T::MaxProxies::get()))]
388		pub fn kill_pure(
389			origin: OriginFor<T>,
390			spawner: AccountIdLookupOf<T>,
391			proxy_type: T::ProxyType,
392			index: u16,
393			#[pallet::compact] height: BlockNumberFor<T>,
394			#[pallet::compact] ext_index: u32,
395		) -> DispatchResult {
396			let who = ensure_signed(origin)?;
397			let spawner = T::Lookup::lookup(spawner)?;
398
399			let when = (height, ext_index);
400			let proxy = Self::pure_account(&spawner, &proxy_type, index, Some(when));
401			ensure!(proxy == who, Error::<T>::NoPermission);
402
403			let (_, deposit) = Proxies::<T>::take(&who);
404			T::Currency::unreserve(&spawner, deposit);
405
406			Self::deposit_event(Event::PureKilled {
407				pure: who,
408				spawner,
409				proxy_type,
410				disambiguation_index: index,
411			});
412
413			Ok(())
414		}
415
416		/// Publish the hash of a proxy-call that will be made in the future.
417		///
418		/// This must be called some number of blocks before the corresponding `proxy` is attempted
419		/// if the delay associated with the proxy relationship is greater than zero.
420		///
421		/// No more than `MaxPending` announcements may be made at any one time.
422		///
423		/// This will take a deposit of `AnnouncementDepositFactor` as well as
424		/// `AnnouncementDepositBase` if there are no other pending announcements.
425		///
426		/// The dispatch origin for this call must be _Signed_ and a proxy of `real`.
427		///
428		/// Parameters:
429		/// - `real`: The account that the proxy will make a call on behalf of.
430		/// - `call_hash`: The hash of the call to be made by the `real` account.
431		#[pallet::call_index(6)]
432		#[pallet::weight(T::WeightInfo::announce(T::MaxPending::get(), T::MaxProxies::get()))]
433		pub fn announce(
434			origin: OriginFor<T>,
435			real: AccountIdLookupOf<T>,
436			call_hash: CallHashOf<T>,
437		) -> DispatchResult {
438			let who = ensure_signed(origin)?;
439			let real = T::Lookup::lookup(real)?;
440			Proxies::<T>::get(&real)
441				.0
442				.into_iter()
443				.find(|x| x.delegate == who)
444				.ok_or(Error::<T>::NotProxy)?;
445
446			let announcement = Announcement {
447				real: real.clone(),
448				call_hash,
449				height: T::BlockNumberProvider::current_block_number(),
450			};
451
452			Announcements::<T>::try_mutate(&who, |(ref mut pending, ref mut deposit)| {
453				pending.try_push(announcement).map_err(|_| Error::<T>::TooMany)?;
454				Self::rejig_deposit(
455					&who,
456					*deposit,
457					T::AnnouncementDepositBase::get(),
458					T::AnnouncementDepositFactor::get(),
459					pending.len(),
460				)
461				.map(|d| {
462					d.expect("Just pushed; pending.len() > 0; rejig_deposit returns Some; qed")
463				})
464				.map(|d| *deposit = d)
465			})?;
466			Self::deposit_event(Event::Announced { real, proxy: who, call_hash });
467
468			Ok(())
469		}
470
471		/// Remove a given announcement.
472		///
473		/// May be called by a proxy account to remove a call they previously announced and return
474		/// the deposit.
475		///
476		/// The dispatch origin for this call must be _Signed_.
477		///
478		/// Parameters:
479		/// - `real`: The account that the proxy will make a call on behalf of.
480		/// - `call_hash`: The hash of the call to be made by the `real` account.
481		#[pallet::call_index(7)]
482		#[pallet::weight(T::WeightInfo::remove_announcement(
483			T::MaxPending::get(),
484			T::MaxProxies::get()
485		))]
486		pub fn remove_announcement(
487			origin: OriginFor<T>,
488			real: AccountIdLookupOf<T>,
489			call_hash: CallHashOf<T>,
490		) -> DispatchResult {
491			let who = ensure_signed(origin)?;
492			let real = T::Lookup::lookup(real)?;
493			Self::edit_announcements(&who, |ann| ann.real != real || ann.call_hash != call_hash)?;
494
495			Ok(())
496		}
497
498		/// Remove the given announcement of a delegate.
499		///
500		/// May be called by a target (proxied) account to remove a call that one of their delegates
501		/// (`delegate`) has announced they want to execute. The deposit is returned.
502		///
503		/// The dispatch origin for this call must be _Signed_.
504		///
505		/// Parameters:
506		/// - `delegate`: The account that previously announced the call.
507		/// - `call_hash`: The hash of the call to be made.
508		#[pallet::call_index(8)]
509		#[pallet::weight(T::WeightInfo::reject_announcement(
510			T::MaxPending::get(),
511			T::MaxProxies::get()
512		))]
513		pub fn reject_announcement(
514			origin: OriginFor<T>,
515			delegate: AccountIdLookupOf<T>,
516			call_hash: CallHashOf<T>,
517		) -> DispatchResult {
518			let who = ensure_signed(origin)?;
519			let delegate = T::Lookup::lookup(delegate)?;
520			Self::edit_announcements(&delegate, |ann| {
521				ann.real != who || ann.call_hash != call_hash
522			})?;
523
524			Ok(())
525		}
526
527		/// Dispatch the given `call` from an account that the sender is authorized for through
528		/// `add_proxy`.
529		///
530		/// Removes any corresponding announcement(s).
531		///
532		/// The dispatch origin for this call must be _Signed_.
533		///
534		/// Parameters:
535		/// - `real`: The account that the proxy will make a call on behalf of.
536		/// - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call.
537		/// - `call`: The call to be made by the `real` account.
538		#[pallet::call_index(9)]
539		#[pallet::weight({
540			let di = call.get_dispatch_info();
541			(T::WeightInfo::proxy_announced(T::MaxPending::get(), T::MaxProxies::get())
542				 // AccountData for inner call origin accountdata.
543				.saturating_add(T::DbWeight::get().reads_writes(1, 1))
544				.saturating_add(di.call_weight),
545			di.class)
546		})]
547		pub fn proxy_announced(
548			origin: OriginFor<T>,
549			delegate: AccountIdLookupOf<T>,
550			real: AccountIdLookupOf<T>,
551			force_proxy_type: Option<T::ProxyType>,
552			call: Box<<T as Config>::RuntimeCall>,
553		) -> DispatchResult {
554			ensure_signed(origin)?;
555			let delegate = T::Lookup::lookup(delegate)?;
556			let real = T::Lookup::lookup(real)?;
557			let def = Self::find_proxy(&real, &delegate, force_proxy_type)?;
558
559			let call_hash = T::CallHasher::hash_of(&call);
560			let now = T::BlockNumberProvider::current_block_number();
561			Self::edit_announcements(&delegate, |ann| {
562				ann.real != real ||
563					ann.call_hash != call_hash ||
564					now.saturating_sub(ann.height) < def.delay
565			})
566			.map_err(|_| Error::<T>::Unannounced)?;
567
568			Self::do_proxy(def, real, *call);
569
570			Ok(())
571		}
572
573		/// Poke / Adjust deposits made for proxies and announcements based on current values.
574		/// This can be used by accounts to possibly lower their locked amount.
575		///
576		/// The dispatch origin for this call must be _Signed_.
577		///
578		/// The transaction fee is waived if the deposit amount has changed.
579		///
580		/// Emits `DepositPoked` if successful.
581		#[pallet::call_index(10)]
582		#[pallet::weight(T::WeightInfo::poke_deposit())]
583		pub fn poke_deposit(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
584			let who = ensure_signed(origin)?;
585			let mut deposit_updated = false;
586
587			// Check and update proxy deposits
588			Proxies::<T>::try_mutate_exists(&who, |maybe_proxies| -> DispatchResult {
589				let (proxies, old_deposit) = maybe_proxies.take().unwrap_or_default();
590				let maybe_new_deposit = Self::rejig_deposit(
591					&who,
592					old_deposit,
593					T::ProxyDepositBase::get(),
594					T::ProxyDepositFactor::get(),
595					proxies.len(),
596				)?;
597
598				match maybe_new_deposit {
599					Some(new_deposit) if new_deposit != old_deposit => {
600						*maybe_proxies = Some((proxies, new_deposit));
601						deposit_updated = true;
602						Self::deposit_event(Event::DepositPoked {
603							who: who.clone(),
604							kind: DepositKind::Proxies,
605							old_deposit,
606							new_deposit,
607						});
608					},
609					Some(_) => {
610						*maybe_proxies = Some((proxies, old_deposit));
611					},
612					None => {
613						*maybe_proxies = None;
614						if !old_deposit.is_zero() {
615							deposit_updated = true;
616							Self::deposit_event(Event::DepositPoked {
617								who: who.clone(),
618								kind: DepositKind::Proxies,
619								old_deposit,
620								new_deposit: BalanceOf::<T>::zero(),
621							});
622						}
623					},
624				}
625				Ok(())
626			})?;
627
628			// Check and update announcement deposits
629			Announcements::<T>::try_mutate_exists(&who, |maybe_announcements| -> DispatchResult {
630				let (announcements, old_deposit) = maybe_announcements.take().unwrap_or_default();
631				let maybe_new_deposit = Self::rejig_deposit(
632					&who,
633					old_deposit,
634					T::AnnouncementDepositBase::get(),
635					T::AnnouncementDepositFactor::get(),
636					announcements.len(),
637				)?;
638
639				match maybe_new_deposit {
640					Some(new_deposit) if new_deposit != old_deposit => {
641						*maybe_announcements = Some((announcements, new_deposit));
642						deposit_updated = true;
643						Self::deposit_event(Event::DepositPoked {
644							who: who.clone(),
645							kind: DepositKind::Announcements,
646							old_deposit,
647							new_deposit,
648						});
649					},
650					Some(_) => {
651						*maybe_announcements = Some((announcements, old_deposit));
652					},
653					None => {
654						*maybe_announcements = None;
655						if !old_deposit.is_zero() {
656							deposit_updated = true;
657							Self::deposit_event(Event::DepositPoked {
658								who: who.clone(),
659								kind: DepositKind::Announcements,
660								old_deposit,
661								new_deposit: BalanceOf::<T>::zero(),
662							});
663						}
664					},
665				}
666				Ok(())
667			})?;
668
669			Ok(if deposit_updated { Pays::No.into() } else { Pays::Yes.into() })
670		}
671	}
672
673	#[pallet::event]
674	#[pallet::generate_deposit(pub(super) fn deposit_event)]
675	pub enum Event<T: Config> {
676		/// A proxy was executed correctly, with the given.
677		ProxyExecuted { result: DispatchResult },
678		/// A pure account has been created by new proxy with given
679		/// disambiguation index and proxy type.
680		PureCreated {
681			pure: T::AccountId,
682			who: T::AccountId,
683			proxy_type: T::ProxyType,
684			disambiguation_index: u16,
685		},
686		/// A pure proxy was killed by its spawner.
687		PureKilled {
688			// The pure proxy account that was destroyed.
689			pure: T::AccountId,
690			// The account that created the pure proxy.
691			spawner: T::AccountId,
692			// The proxy type of the pure proxy that was destroyed.
693			proxy_type: T::ProxyType,
694			// The index originally passed to `create_pure` when this pure proxy was created.
695			disambiguation_index: u16,
696		},
697		/// An announcement was placed to make a call in the future.
698		Announced { real: T::AccountId, proxy: T::AccountId, call_hash: CallHashOf<T> },
699		/// A proxy was added.
700		ProxyAdded {
701			delegator: T::AccountId,
702			delegatee: T::AccountId,
703			proxy_type: T::ProxyType,
704			delay: BlockNumberFor<T>,
705		},
706		/// A proxy was removed.
707		ProxyRemoved {
708			delegator: T::AccountId,
709			delegatee: T::AccountId,
710			proxy_type: T::ProxyType,
711			delay: BlockNumberFor<T>,
712		},
713		/// A deposit stored for proxies or announcements was poked / updated.
714		DepositPoked {
715			who: T::AccountId,
716			kind: DepositKind,
717			old_deposit: BalanceOf<T>,
718			new_deposit: BalanceOf<T>,
719		},
720	}
721
722	#[pallet::error]
723	pub enum Error<T> {
724		/// There are too many proxies registered or too many announcements pending.
725		TooMany,
726		/// Proxy registration not found.
727		NotFound,
728		/// Sender is not a proxy of the account to be proxied.
729		NotProxy,
730		/// A call which is incompatible with the proxy type's filter was attempted.
731		Unproxyable,
732		/// Account is already a proxy.
733		Duplicate,
734		/// Call may not be made by proxy because it may escalate its privileges.
735		NoPermission,
736		/// Announcement, if made at all, was made too recently.
737		Unannounced,
738		/// Cannot add self as proxy.
739		NoSelfProxy,
740	}
741
742	/// The set of account proxies. Maps the account which has delegated to the accounts
743	/// which are being delegated to, together with the amount held on deposit.
744	#[pallet::storage]
745	pub type Proxies<T: Config> = StorageMap<
746		_,
747		Twox64Concat,
748		T::AccountId,
749		(
750			BoundedVec<
751				ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>,
752				T::MaxProxies,
753			>,
754			BalanceOf<T>,
755		),
756		ValueQuery,
757	>;
758
759	/// The announcements made by the proxy (key).
760	#[pallet::storage]
761	pub type Announcements<T: Config> = StorageMap<
762		_,
763		Twox64Concat,
764		T::AccountId,
765		(
766			BoundedVec<Announcement<T::AccountId, CallHashOf<T>, BlockNumberFor<T>>, T::MaxPending>,
767			BalanceOf<T>,
768		),
769		ValueQuery,
770	>;
771
772	#[pallet::view_functions]
773	impl<T: Config> Pallet<T> {
774		/// Check if a `RuntimeCall` is allowed for a given `ProxyType`.
775		pub fn check_permissions(
776			call: <T as Config>::RuntimeCall,
777			proxy_type: T::ProxyType,
778		) -> bool {
779			proxy_type.filter(&call)
780		}
781
782		/// Check if one `ProxyType` is a subset of another `ProxyType`.
783		pub fn is_superset(to_check: T::ProxyType, against: T::ProxyType) -> bool {
784			to_check.is_superset(&against)
785		}
786	}
787}
788
789impl<T: Config> Pallet<T> {
790	/// Public function to proxies storage.
791	pub fn proxies(
792		account: T::AccountId,
793	) -> (
794		BoundedVec<ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>, T::MaxProxies>,
795		BalanceOf<T>,
796	) {
797		Proxies::<T>::get(account)
798	}
799
800	/// Public function to announcements storage.
801	pub fn announcements(
802		account: T::AccountId,
803	) -> (
804		BoundedVec<Announcement<T::AccountId, CallHashOf<T>, BlockNumberFor<T>>, T::MaxPending>,
805		BalanceOf<T>,
806	) {
807		Announcements::<T>::get(account)
808	}
809
810	/// Calculate the address of an pure account.
811	///
812	/// - `who`: The spawner account.
813	/// - `proxy_type`: The type of the proxy that the sender will be registered as over the
814	/// new account. This will almost always be the most permissive `ProxyType` possible to
815	/// allow for maximum flexibility.
816	/// - `index`: A disambiguation index, in case this is called multiple times in the same
817	/// transaction (e.g. with `utility::batch`). Unless you're using `batch` you probably just
818	/// want to use `0`.
819	/// - `maybe_when`: The block height and extrinsic index of when the pure account was
820	/// created. None to use current block height and extrinsic index.
821	pub fn pure_account(
822		who: &T::AccountId,
823		proxy_type: &T::ProxyType,
824		index: u16,
825		maybe_when: Option<(BlockNumberFor<T>, u32)>,
826	) -> T::AccountId {
827		let (height, ext_index) = maybe_when.unwrap_or_else(|| {
828			(
829				T::BlockNumberProvider::current_block_number(),
830				frame_system::Pallet::<T>::extrinsic_index().unwrap_or_default(),
831			)
832		});
833
834		let entropy = (b"modlpy/proxy____", who, height, ext_index, proxy_type, index)
835			.using_encoded(blake2_256);
836		Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref()))
837			.expect("infinite length input; no invalid inputs for type; qed")
838	}
839
840	/// Register a proxy account for the delegator that is able to make calls on its behalf.
841	///
842	/// Parameters:
843	/// - `delegator`: The delegator account.
844	/// - `delegatee`: The account that the `delegator` would like to make a proxy.
845	/// - `proxy_type`: The permissions allowed for this proxy account.
846	/// - `delay`: The announcement period required of the initial proxy. Will generally be
847	/// zero.
848	pub fn add_proxy_delegate(
849		delegator: &T::AccountId,
850		delegatee: T::AccountId,
851		proxy_type: T::ProxyType,
852		delay: BlockNumberFor<T>,
853	) -> DispatchResult {
854		ensure!(delegator != &delegatee, Error::<T>::NoSelfProxy);
855		Proxies::<T>::try_mutate(delegator, |(ref mut proxies, ref mut deposit)| {
856			let proxy_def = ProxyDefinition {
857				delegate: delegatee.clone(),
858				proxy_type: proxy_type.clone(),
859				delay,
860			};
861			let i = proxies.binary_search(&proxy_def).err().ok_or(Error::<T>::Duplicate)?;
862			proxies.try_insert(i, proxy_def).map_err(|_| Error::<T>::TooMany)?;
863			let new_deposit = Self::deposit(proxies.len() as u32);
864			if new_deposit > *deposit {
865				T::Currency::reserve(delegator, new_deposit - *deposit)?;
866			} else if new_deposit < *deposit {
867				T::Currency::unreserve(delegator, *deposit - new_deposit);
868			}
869			*deposit = new_deposit;
870			Self::deposit_event(Event::<T>::ProxyAdded {
871				delegator: delegator.clone(),
872				delegatee,
873				proxy_type,
874				delay,
875			});
876			Ok(())
877		})
878	}
879
880	/// Unregister a proxy account for the delegator.
881	///
882	/// Parameters:
883	/// - `delegator`: The delegator account.
884	/// - `delegatee`: The account that the `delegator` would like to make a proxy.
885	/// - `proxy_type`: The permissions allowed for this proxy account.
886	/// - `delay`: The announcement period required of the initial proxy. Will generally be
887	/// zero.
888	pub fn remove_proxy_delegate(
889		delegator: &T::AccountId,
890		delegatee: T::AccountId,
891		proxy_type: T::ProxyType,
892		delay: BlockNumberFor<T>,
893	) -> DispatchResult {
894		Proxies::<T>::try_mutate_exists(delegator, |x| {
895			let (mut proxies, old_deposit) = x.take().ok_or(Error::<T>::NotFound)?;
896			let proxy_def = ProxyDefinition {
897				delegate: delegatee.clone(),
898				proxy_type: proxy_type.clone(),
899				delay,
900			};
901			let i = proxies.binary_search(&proxy_def).ok().ok_or(Error::<T>::NotFound)?;
902			proxies.remove(i);
903			let new_deposit = Self::deposit(proxies.len() as u32);
904			if new_deposit > old_deposit {
905				T::Currency::reserve(delegator, new_deposit - old_deposit)?;
906			} else if new_deposit < old_deposit {
907				T::Currency::unreserve(delegator, old_deposit - new_deposit);
908			}
909			if !proxies.is_empty() {
910				*x = Some((proxies, new_deposit))
911			}
912			Self::deposit_event(Event::<T>::ProxyRemoved {
913				delegator: delegator.clone(),
914				delegatee,
915				proxy_type,
916				delay,
917			});
918			Ok(())
919		})
920	}
921
922	pub fn deposit(num_proxies: u32) -> BalanceOf<T> {
923		if num_proxies == 0 {
924			Zero::zero()
925		} else {
926			T::ProxyDepositBase::get() + T::ProxyDepositFactor::get() * num_proxies.into()
927		}
928	}
929
930	fn rejig_deposit(
931		who: &T::AccountId,
932		old_deposit: BalanceOf<T>,
933		base: BalanceOf<T>,
934		factor: BalanceOf<T>,
935		len: usize,
936	) -> Result<Option<BalanceOf<T>>, DispatchError> {
937		let new_deposit =
938			if len == 0 { BalanceOf::<T>::zero() } else { base + factor * (len as u32).into() };
939		if new_deposit > old_deposit {
940			T::Currency::reserve(who, new_deposit.saturating_sub(old_deposit))?;
941		} else if new_deposit < old_deposit {
942			let excess = old_deposit.saturating_sub(new_deposit);
943			let remaining_unreserved = T::Currency::unreserve(who, excess);
944			if !remaining_unreserved.is_zero() {
945				defensive!(
946					"Failed to unreserve full amount. (Requested, Actual)",
947					(excess, excess.saturating_sub(remaining_unreserved))
948				);
949			}
950		}
951		Ok(if len == 0 { None } else { Some(new_deposit) })
952	}
953
954	fn edit_announcements<
955		F: FnMut(&Announcement<T::AccountId, CallHashOf<T>, BlockNumberFor<T>>) -> bool,
956	>(
957		delegate: &T::AccountId,
958		f: F,
959	) -> DispatchResult {
960		Announcements::<T>::try_mutate_exists(delegate, |x| {
961			let (mut pending, old_deposit) = x.take().ok_or(Error::<T>::NotFound)?;
962			let orig_pending_len = pending.len();
963			pending.retain(f);
964			ensure!(orig_pending_len > pending.len(), Error::<T>::NotFound);
965			*x = Self::rejig_deposit(
966				delegate,
967				old_deposit,
968				T::AnnouncementDepositBase::get(),
969				T::AnnouncementDepositFactor::get(),
970				pending.len(),
971			)?
972			.map(|deposit| (pending, deposit));
973			Ok(())
974		})
975	}
976
977	pub fn find_proxy(
978		real: &T::AccountId,
979		delegate: &T::AccountId,
980		force_proxy_type: Option<T::ProxyType>,
981	) -> Result<ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>, DispatchError> {
982		let f = |x: &ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>| -> bool {
983			&x.delegate == delegate &&
984				force_proxy_type.as_ref().map_or(true, |y| &x.proxy_type == y)
985		};
986		Ok(Proxies::<T>::get(real).0.into_iter().find(f).ok_or(Error::<T>::NotProxy)?)
987	}
988
989	fn do_proxy(
990		def: ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>,
991		real: T::AccountId,
992		call: <T as Config>::RuntimeCall,
993	) {
994		use frame::traits::{InstanceFilter as _, OriginTrait as _};
995		// This is a freshly authenticated new account, the origin restrictions doesn't apply.
996		let mut origin: T::RuntimeOrigin = frame_system::RawOrigin::Signed(real).into();
997		origin.add_filter(move |c: &<T as frame_system::Config>::RuntimeCall| {
998			let c = <T as Config>::RuntimeCall::from_ref(c);
999			// We make sure the proxy call does access this pallet to change modify proxies.
1000			match c.is_sub_type() {
1001				// Proxy call cannot add or remove a proxy with more permissions than it already
1002				// has.
1003				Some(Call::add_proxy { ref proxy_type, .. }) |
1004				Some(Call::remove_proxy { ref proxy_type, .. })
1005					if !def.proxy_type.is_superset(proxy_type) =>
1006					false,
1007				// Proxy call cannot remove all proxies or kill pure proxies unless it has full
1008				// permissions.
1009				Some(Call::remove_proxies { .. }) | Some(Call::kill_pure { .. })
1010					if def.proxy_type != T::ProxyType::default() =>
1011					false,
1012				_ => def.proxy_type.filter(c),
1013			}
1014		});
1015		let e = call.dispatch(origin);
1016		Self::deposit_event(Event::ProxyExecuted { result: e.map(|_| ()).map_err(|e| e.error) });
1017	}
1018
1019	/// Removes all proxy delegates for a given delegator.
1020	///
1021	/// Parameters:
1022	/// - `delegator`: The delegator account.
1023	pub fn remove_all_proxy_delegates(delegator: &T::AccountId) {
1024		let (_, old_deposit) = Proxies::<T>::take(&delegator);
1025		T::Currency::unreserve(&delegator, old_deposit);
1026	}
1027}