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