referrerpolicy=no-referrer-when-downgrade

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			let extrinsic_index = <frame_system::Pallet<T>>::extrinsic_index().unwrap_or_default();
361			Self::deposit_event(Event::PureCreated {
362				pure,
363				who,
364				proxy_type,
365				disambiguation_index: index,
366				at: T::BlockNumberProvider::current_block_number(),
367				extrinsic_index,
368			});
369
370			Ok(())
371		}
372
373		/// Removes a previously spawned pure proxy.
374		///
375		/// WARNING: **All access to this account will be lost.** Any funds held in it will be
376		/// inaccessible.
377		///
378		/// Requires a `Signed` origin, and the sender account must have been created by a call to
379		/// `create_pure` with corresponding parameters.
380		///
381		/// - `spawner`: The account that originally called `create_pure` to create this account.
382		/// - `index`: The disambiguation index originally passed to `create_pure`. Probably `0`.
383		/// - `proxy_type`: The proxy type originally passed to `create_pure`.
384		/// - `height`: The height of the chain when the call to `create_pure` was processed.
385		/// - `ext_index`: The extrinsic index in which the call to `create_pure` was processed.
386		///
387		/// Fails with `NoPermission` in case the caller is not a previously created pure
388		/// account whose `create_pure` call has corresponding parameters.
389		#[pallet::call_index(5)]
390		#[pallet::weight(T::WeightInfo::kill_pure(T::MaxProxies::get()))]
391		pub fn kill_pure(
392			origin: OriginFor<T>,
393			spawner: AccountIdLookupOf<T>,
394			proxy_type: T::ProxyType,
395			index: u16,
396			#[pallet::compact] height: BlockNumberFor<T>,
397			#[pallet::compact] ext_index: u32,
398		) -> DispatchResult {
399			let who = ensure_signed(origin)?;
400			let spawner = T::Lookup::lookup(spawner)?;
401
402			let when = (height, ext_index);
403			let proxy = Self::pure_account(&spawner, &proxy_type, index, Some(when));
404			ensure!(proxy == who, Error::<T>::NoPermission);
405
406			let (_, deposit) = Proxies::<T>::take(&who);
407			T::Currency::unreserve(&spawner, deposit);
408
409			Self::deposit_event(Event::PureKilled {
410				pure: who,
411				spawner,
412				proxy_type,
413				disambiguation_index: index,
414			});
415
416			Ok(())
417		}
418
419		/// Publish the hash of a proxy-call that will be made in the future.
420		///
421		/// This must be called some number of blocks before the corresponding `proxy` is attempted
422		/// if the delay associated with the proxy relationship is greater than zero.
423		///
424		/// No more than `MaxPending` announcements may be made at any one time.
425		///
426		/// This will take a deposit of `AnnouncementDepositFactor` as well as
427		/// `AnnouncementDepositBase` if there are no other pending announcements.
428		///
429		/// The dispatch origin for this call must be _Signed_ and a proxy of `real`.
430		///
431		/// Parameters:
432		/// - `real`: The account that the proxy will make a call on behalf of.
433		/// - `call_hash`: The hash of the call to be made by the `real` account.
434		#[pallet::call_index(6)]
435		#[pallet::weight(T::WeightInfo::announce(T::MaxPending::get(), T::MaxProxies::get()))]
436		pub fn announce(
437			origin: OriginFor<T>,
438			real: AccountIdLookupOf<T>,
439			call_hash: CallHashOf<T>,
440		) -> DispatchResult {
441			let who = ensure_signed(origin)?;
442			let real = T::Lookup::lookup(real)?;
443			Proxies::<T>::get(&real)
444				.0
445				.into_iter()
446				.find(|x| x.delegate == who)
447				.ok_or(Error::<T>::NotProxy)?;
448
449			let announcement = Announcement {
450				real: real.clone(),
451				call_hash,
452				height: T::BlockNumberProvider::current_block_number(),
453			};
454
455			Announcements::<T>::try_mutate(&who, |(ref mut pending, ref mut deposit)| {
456				pending.try_push(announcement).map_err(|_| Error::<T>::TooMany)?;
457				Self::rejig_deposit(
458					&who,
459					*deposit,
460					T::AnnouncementDepositBase::get(),
461					T::AnnouncementDepositFactor::get(),
462					pending.len(),
463				)
464				.map(|d| {
465					d.expect("Just pushed; pending.len() > 0; rejig_deposit returns Some; qed")
466				})
467				.map(|d| *deposit = d)
468			})?;
469			Self::deposit_event(Event::Announced { real, proxy: who, call_hash });
470
471			Ok(())
472		}
473
474		/// Remove a given announcement.
475		///
476		/// May be called by a proxy account to remove a call they previously announced and return
477		/// the deposit.
478		///
479		/// The dispatch origin for this call must be _Signed_.
480		///
481		/// Parameters:
482		/// - `real`: The account that the proxy will make a call on behalf of.
483		/// - `call_hash`: The hash of the call to be made by the `real` account.
484		#[pallet::call_index(7)]
485		#[pallet::weight(T::WeightInfo::remove_announcement(
486			T::MaxPending::get(),
487			T::MaxProxies::get()
488		))]
489		pub fn remove_announcement(
490			origin: OriginFor<T>,
491			real: AccountIdLookupOf<T>,
492			call_hash: CallHashOf<T>,
493		) -> DispatchResult {
494			let who = ensure_signed(origin)?;
495			let real = T::Lookup::lookup(real)?;
496			Self::edit_announcements(&who, |ann| ann.real != real || ann.call_hash != call_hash)?;
497
498			Ok(())
499		}
500
501		/// Remove the given announcement of a delegate.
502		///
503		/// May be called by a target (proxied) account to remove a call that one of their delegates
504		/// (`delegate`) has announced they want to execute. The deposit is returned.
505		///
506		/// The dispatch origin for this call must be _Signed_.
507		///
508		/// Parameters:
509		/// - `delegate`: The account that previously announced the call.
510		/// - `call_hash`: The hash of the call to be made.
511		#[pallet::call_index(8)]
512		#[pallet::weight(T::WeightInfo::reject_announcement(
513			T::MaxPending::get(),
514			T::MaxProxies::get()
515		))]
516		pub fn reject_announcement(
517			origin: OriginFor<T>,
518			delegate: AccountIdLookupOf<T>,
519			call_hash: CallHashOf<T>,
520		) -> DispatchResult {
521			let who = ensure_signed(origin)?;
522			let delegate = T::Lookup::lookup(delegate)?;
523			Self::edit_announcements(&delegate, |ann| {
524				ann.real != who || ann.call_hash != call_hash
525			})?;
526
527			Ok(())
528		}
529
530		/// Dispatch the given `call` from an account that the sender is authorized for through
531		/// `add_proxy`.
532		///
533		/// Removes any corresponding announcement(s).
534		///
535		/// The dispatch origin for this call must be _Signed_.
536		///
537		/// Parameters:
538		/// - `real`: The account that the proxy will make a call on behalf of.
539		/// - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call.
540		/// - `call`: The call to be made by the `real` account.
541		#[pallet::call_index(9)]
542		#[pallet::weight({
543			let di = call.get_dispatch_info();
544			(T::WeightInfo::proxy_announced(T::MaxPending::get(), T::MaxProxies::get())
545				 // AccountData for inner call origin accountdata.
546				.saturating_add(T::DbWeight::get().reads_writes(1, 1))
547				.saturating_add(di.call_weight),
548			di.class)
549		})]
550		pub fn proxy_announced(
551			origin: OriginFor<T>,
552			delegate: AccountIdLookupOf<T>,
553			real: AccountIdLookupOf<T>,
554			force_proxy_type: Option<T::ProxyType>,
555			call: Box<<T as Config>::RuntimeCall>,
556		) -> DispatchResult {
557			ensure_signed(origin)?;
558			let delegate = T::Lookup::lookup(delegate)?;
559			let real = T::Lookup::lookup(real)?;
560			let def = Self::find_proxy(&real, &delegate, force_proxy_type)?;
561
562			let call_hash = T::CallHasher::hash_of(&call);
563			let now = T::BlockNumberProvider::current_block_number();
564			Self::edit_announcements(&delegate, |ann| {
565				ann.real != real ||
566					ann.call_hash != call_hash ||
567					now.saturating_sub(ann.height) < def.delay
568			})
569			.map_err(|_| Error::<T>::Unannounced)?;
570
571			Self::do_proxy(def, real, *call);
572
573			Ok(())
574		}
575
576		/// Poke / Adjust deposits made for proxies and announcements based on current values.
577		/// This can be used by accounts to possibly lower their locked amount.
578		///
579		/// The dispatch origin for this call must be _Signed_.
580		///
581		/// The transaction fee is waived if the deposit amount has changed.
582		///
583		/// Emits `DepositPoked` if successful.
584		#[pallet::call_index(10)]
585		#[pallet::weight(T::WeightInfo::poke_deposit())]
586		pub fn poke_deposit(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
587			let who = ensure_signed(origin)?;
588			let mut deposit_updated = false;
589
590			// Check and update proxy deposits
591			Proxies::<T>::try_mutate_exists(&who, |maybe_proxies| -> DispatchResult {
592				let (proxies, old_deposit) = maybe_proxies.take().unwrap_or_default();
593				let maybe_new_deposit = Self::rejig_deposit(
594					&who,
595					old_deposit,
596					T::ProxyDepositBase::get(),
597					T::ProxyDepositFactor::get(),
598					proxies.len(),
599				)?;
600
601				match maybe_new_deposit {
602					Some(new_deposit) if new_deposit != old_deposit => {
603						*maybe_proxies = Some((proxies, new_deposit));
604						deposit_updated = true;
605						Self::deposit_event(Event::DepositPoked {
606							who: who.clone(),
607							kind: DepositKind::Proxies,
608							old_deposit,
609							new_deposit,
610						});
611					},
612					Some(_) => {
613						*maybe_proxies = Some((proxies, old_deposit));
614					},
615					None => {
616						*maybe_proxies = None;
617						if !old_deposit.is_zero() {
618							deposit_updated = true;
619							Self::deposit_event(Event::DepositPoked {
620								who: who.clone(),
621								kind: DepositKind::Proxies,
622								old_deposit,
623								new_deposit: BalanceOf::<T>::zero(),
624							});
625						}
626					},
627				}
628				Ok(())
629			})?;
630
631			// Check and update announcement deposits
632			Announcements::<T>::try_mutate_exists(&who, |maybe_announcements| -> DispatchResult {
633				let (announcements, old_deposit) = maybe_announcements.take().unwrap_or_default();
634				let maybe_new_deposit = Self::rejig_deposit(
635					&who,
636					old_deposit,
637					T::AnnouncementDepositBase::get(),
638					T::AnnouncementDepositFactor::get(),
639					announcements.len(),
640				)?;
641
642				match maybe_new_deposit {
643					Some(new_deposit) if new_deposit != old_deposit => {
644						*maybe_announcements = Some((announcements, new_deposit));
645						deposit_updated = true;
646						Self::deposit_event(Event::DepositPoked {
647							who: who.clone(),
648							kind: DepositKind::Announcements,
649							old_deposit,
650							new_deposit,
651						});
652					},
653					Some(_) => {
654						*maybe_announcements = Some((announcements, old_deposit));
655					},
656					None => {
657						*maybe_announcements = None;
658						if !old_deposit.is_zero() {
659							deposit_updated = true;
660							Self::deposit_event(Event::DepositPoked {
661								who: who.clone(),
662								kind: DepositKind::Announcements,
663								old_deposit,
664								new_deposit: BalanceOf::<T>::zero(),
665							});
666						}
667					},
668				}
669				Ok(())
670			})?;
671
672			Ok(if deposit_updated { Pays::No.into() } else { Pays::Yes.into() })
673		}
674	}
675
676	#[pallet::event]
677	#[pallet::generate_deposit(pub(super) fn deposit_event)]
678	pub enum Event<T: Config> {
679		/// A proxy was executed correctly, with the given.
680		ProxyExecuted { result: DispatchResult },
681		/// A pure account has been created by new proxy with given
682		/// disambiguation index and proxy type.
683		PureCreated {
684			pure: T::AccountId,
685			who: T::AccountId,
686			proxy_type: T::ProxyType,
687			disambiguation_index: u16,
688			at: BlockNumberFor<T>,
689			extrinsic_index: u32,
690		},
691		/// A pure proxy was killed by its spawner.
692		PureKilled {
693			// The pure proxy account that was destroyed.
694			pure: T::AccountId,
695			// The account that created the pure proxy.
696			spawner: T::AccountId,
697			// The proxy type of the pure proxy that was destroyed.
698			proxy_type: T::ProxyType,
699			// The index originally passed to `create_pure` when this pure proxy was created.
700			disambiguation_index: u16,
701		},
702		/// An announcement was placed to make a call in the future.
703		Announced { real: T::AccountId, proxy: T::AccountId, call_hash: CallHashOf<T> },
704		/// A proxy was added.
705		ProxyAdded {
706			delegator: T::AccountId,
707			delegatee: T::AccountId,
708			proxy_type: T::ProxyType,
709			delay: BlockNumberFor<T>,
710		},
711		/// A proxy was removed.
712		ProxyRemoved {
713			delegator: T::AccountId,
714			delegatee: T::AccountId,
715			proxy_type: T::ProxyType,
716			delay: BlockNumberFor<T>,
717		},
718		/// A deposit stored for proxies or announcements was poked / updated.
719		DepositPoked {
720			who: T::AccountId,
721			kind: DepositKind,
722			old_deposit: BalanceOf<T>,
723			new_deposit: BalanceOf<T>,
724		},
725	}
726
727	#[pallet::error]
728	pub enum Error<T> {
729		/// There are too many proxies registered or too many announcements pending.
730		TooMany,
731		/// Proxy registration not found.
732		NotFound,
733		/// Sender is not a proxy of the account to be proxied.
734		NotProxy,
735		/// A call which is incompatible with the proxy type's filter was attempted.
736		Unproxyable,
737		/// Account is already a proxy.
738		Duplicate,
739		/// Call may not be made by proxy because it may escalate its privileges.
740		NoPermission,
741		/// Announcement, if made at all, was made too recently.
742		Unannounced,
743		/// Cannot add self as proxy.
744		NoSelfProxy,
745	}
746
747	/// The set of account proxies. Maps the account which has delegated to the accounts
748	/// which are being delegated to, together with the amount held on deposit.
749	#[pallet::storage]
750	pub type Proxies<T: Config> = StorageMap<
751		_,
752		Twox64Concat,
753		T::AccountId,
754		(
755			BoundedVec<
756				ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>,
757				T::MaxProxies,
758			>,
759			BalanceOf<T>,
760		),
761		ValueQuery,
762	>;
763
764	/// The announcements made by the proxy (key).
765	#[pallet::storage]
766	pub type Announcements<T: Config> = StorageMap<
767		_,
768		Twox64Concat,
769		T::AccountId,
770		(
771			BoundedVec<Announcement<T::AccountId, CallHashOf<T>, BlockNumberFor<T>>, T::MaxPending>,
772			BalanceOf<T>,
773		),
774		ValueQuery,
775	>;
776
777	#[pallet::view_functions]
778	impl<T: Config> Pallet<T> {
779		/// Check if a `RuntimeCall` is allowed for a given `ProxyType`.
780		pub fn check_permissions(
781			call: <T as Config>::RuntimeCall,
782			proxy_type: T::ProxyType,
783		) -> bool {
784			proxy_type.filter(&call)
785		}
786
787		/// Check if one `ProxyType` is a subset of another `ProxyType`.
788		pub fn is_superset(to_check: T::ProxyType, against: T::ProxyType) -> bool {
789			to_check.is_superset(&against)
790		}
791	}
792}
793
794impl<T: Config> Pallet<T> {
795	/// Public function to proxies storage.
796	pub fn proxies(
797		account: T::AccountId,
798	) -> (
799		BoundedVec<ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>, T::MaxProxies>,
800		BalanceOf<T>,
801	) {
802		Proxies::<T>::get(account)
803	}
804
805	/// Public function to announcements storage.
806	pub fn announcements(
807		account: T::AccountId,
808	) -> (
809		BoundedVec<Announcement<T::AccountId, CallHashOf<T>, BlockNumberFor<T>>, T::MaxPending>,
810		BalanceOf<T>,
811	) {
812		Announcements::<T>::get(account)
813	}
814
815	/// Calculate the address of an pure account.
816	///
817	/// - `who`: The spawner account.
818	/// - `proxy_type`: The type of the proxy that the sender will be registered as over the
819	/// new account. This will almost always be the most permissive `ProxyType` possible to
820	/// allow for maximum flexibility.
821	/// - `index`: A disambiguation index, in case this is called multiple times in the same
822	/// transaction (e.g. with `utility::batch`). Unless you're using `batch` you probably just
823	/// want to use `0`.
824	/// - `maybe_when`: The block height and extrinsic index of when the pure account was
825	/// created. None to use current block height and extrinsic index.
826	pub fn pure_account(
827		who: &T::AccountId,
828		proxy_type: &T::ProxyType,
829		index: u16,
830		maybe_when: Option<(BlockNumberFor<T>, u32)>,
831	) -> T::AccountId {
832		let (height, ext_index) = maybe_when.unwrap_or_else(|| {
833			(
834				T::BlockNumberProvider::current_block_number(),
835				frame_system::Pallet::<T>::extrinsic_index().unwrap_or_default(),
836			)
837		});
838
839		let entropy = (b"modlpy/proxy____", who, height, ext_index, proxy_type, index)
840			.using_encoded(blake2_256);
841		Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref()))
842			.expect("infinite length input; no invalid inputs for type; qed")
843	}
844
845	/// Register a proxy account for the delegator that is able to make calls on its behalf.
846	///
847	/// Parameters:
848	/// - `delegator`: The delegator account.
849	/// - `delegatee`: The account that the `delegator` would like to make a proxy.
850	/// - `proxy_type`: The permissions allowed for this proxy account.
851	/// - `delay`: The announcement period required of the initial proxy. Will generally be
852	/// zero.
853	pub fn add_proxy_delegate(
854		delegator: &T::AccountId,
855		delegatee: T::AccountId,
856		proxy_type: T::ProxyType,
857		delay: BlockNumberFor<T>,
858	) -> DispatchResult {
859		ensure!(delegator != &delegatee, Error::<T>::NoSelfProxy);
860		Proxies::<T>::try_mutate(delegator, |(ref mut proxies, ref mut deposit)| {
861			let proxy_def = ProxyDefinition {
862				delegate: delegatee.clone(),
863				proxy_type: proxy_type.clone(),
864				delay,
865			};
866			let i = proxies.binary_search(&proxy_def).err().ok_or(Error::<T>::Duplicate)?;
867			proxies.try_insert(i, proxy_def).map_err(|_| Error::<T>::TooMany)?;
868			let new_deposit = Self::deposit(proxies.len() as u32);
869			if new_deposit > *deposit {
870				T::Currency::reserve(delegator, new_deposit - *deposit)?;
871			} else if new_deposit < *deposit {
872				T::Currency::unreserve(delegator, *deposit - new_deposit);
873			}
874			*deposit = new_deposit;
875			Self::deposit_event(Event::<T>::ProxyAdded {
876				delegator: delegator.clone(),
877				delegatee,
878				proxy_type,
879				delay,
880			});
881			Ok(())
882		})
883	}
884
885	/// Unregister a proxy account for the delegator.
886	///
887	/// Parameters:
888	/// - `delegator`: The delegator account.
889	/// - `delegatee`: The account that the `delegator` would like to make a proxy.
890	/// - `proxy_type`: The permissions allowed for this proxy account.
891	/// - `delay`: The announcement period required of the initial proxy. Will generally be
892	/// zero.
893	pub fn remove_proxy_delegate(
894		delegator: &T::AccountId,
895		delegatee: T::AccountId,
896		proxy_type: T::ProxyType,
897		delay: BlockNumberFor<T>,
898	) -> DispatchResult {
899		Proxies::<T>::try_mutate_exists(delegator, |x| {
900			let (mut proxies, old_deposit) = x.take().ok_or(Error::<T>::NotFound)?;
901			let proxy_def = ProxyDefinition {
902				delegate: delegatee.clone(),
903				proxy_type: proxy_type.clone(),
904				delay,
905			};
906			let i = proxies.binary_search(&proxy_def).ok().ok_or(Error::<T>::NotFound)?;
907			proxies.remove(i);
908			let new_deposit = Self::deposit(proxies.len() as u32);
909			if new_deposit > old_deposit {
910				T::Currency::reserve(delegator, new_deposit - old_deposit)?;
911			} else if new_deposit < old_deposit {
912				T::Currency::unreserve(delegator, old_deposit - new_deposit);
913			}
914			if !proxies.is_empty() {
915				*x = Some((proxies, new_deposit))
916			}
917			Self::deposit_event(Event::<T>::ProxyRemoved {
918				delegator: delegator.clone(),
919				delegatee,
920				proxy_type,
921				delay,
922			});
923			Ok(())
924		})
925	}
926
927	pub fn deposit(num_proxies: u32) -> BalanceOf<T> {
928		if num_proxies == 0 {
929			Zero::zero()
930		} else {
931			T::ProxyDepositBase::get() + T::ProxyDepositFactor::get() * num_proxies.into()
932		}
933	}
934
935	fn rejig_deposit(
936		who: &T::AccountId,
937		old_deposit: BalanceOf<T>,
938		base: BalanceOf<T>,
939		factor: BalanceOf<T>,
940		len: usize,
941	) -> Result<Option<BalanceOf<T>>, DispatchError> {
942		let new_deposit =
943			if len == 0 { BalanceOf::<T>::zero() } else { base + factor * (len as u32).into() };
944		if new_deposit > old_deposit {
945			T::Currency::reserve(who, new_deposit.saturating_sub(old_deposit))?;
946		} else if new_deposit < old_deposit {
947			let excess = old_deposit.saturating_sub(new_deposit);
948			let remaining_unreserved = T::Currency::unreserve(who, excess);
949			if !remaining_unreserved.is_zero() {
950				defensive!(
951					"Failed to unreserve full amount. (Requested, Actual)",
952					(excess, excess.saturating_sub(remaining_unreserved))
953				);
954			}
955		}
956		Ok(if len == 0 { None } else { Some(new_deposit) })
957	}
958
959	fn edit_announcements<
960		F: FnMut(&Announcement<T::AccountId, CallHashOf<T>, BlockNumberFor<T>>) -> bool,
961	>(
962		delegate: &T::AccountId,
963		f: F,
964	) -> DispatchResult {
965		Announcements::<T>::try_mutate_exists(delegate, |x| {
966			let (mut pending, old_deposit) = x.take().ok_or(Error::<T>::NotFound)?;
967			let orig_pending_len = pending.len();
968			pending.retain(f);
969			ensure!(orig_pending_len > pending.len(), Error::<T>::NotFound);
970			*x = Self::rejig_deposit(
971				delegate,
972				old_deposit,
973				T::AnnouncementDepositBase::get(),
974				T::AnnouncementDepositFactor::get(),
975				pending.len(),
976			)?
977			.map(|deposit| (pending, deposit));
978			Ok(())
979		})
980	}
981
982	pub fn find_proxy(
983		real: &T::AccountId,
984		delegate: &T::AccountId,
985		force_proxy_type: Option<T::ProxyType>,
986	) -> Result<ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>, DispatchError> {
987		let f = |x: &ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>| -> bool {
988			&x.delegate == delegate &&
989				force_proxy_type.as_ref().map_or(true, |y| &x.proxy_type == y)
990		};
991		Ok(Proxies::<T>::get(real).0.into_iter().find(f).ok_or(Error::<T>::NotProxy)?)
992	}
993
994	fn do_proxy(
995		def: ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>,
996		real: T::AccountId,
997		call: <T as Config>::RuntimeCall,
998	) {
999		use frame::traits::{InstanceFilter as _, OriginTrait as _};
1000		// This is a freshly authenticated new account, the origin restrictions doesn't apply.
1001		let mut origin: T::RuntimeOrigin = frame_system::RawOrigin::Signed(real).into();
1002		origin.add_filter(move |c: &<T as frame_system::Config>::RuntimeCall| {
1003			let c = <T as Config>::RuntimeCall::from_ref(c);
1004			// We make sure the proxy call does access this pallet to change modify proxies.
1005			match c.is_sub_type() {
1006				// Proxy call cannot add or remove a proxy with more permissions than it already
1007				// has.
1008				Some(Call::add_proxy { ref proxy_type, .. }) |
1009				Some(Call::remove_proxy { ref proxy_type, .. })
1010					if !def.proxy_type.is_superset(proxy_type) =>
1011					false,
1012				// Proxy call cannot remove all proxies or kill pure proxies unless it has full
1013				// permissions.
1014				Some(Call::remove_proxies { .. }) | Some(Call::kill_pure { .. })
1015					if def.proxy_type != T::ProxyType::default() =>
1016					false,
1017				_ => def.proxy_type.filter(c),
1018			}
1019		});
1020		let e = call.dispatch(origin);
1021		Self::deposit_event(Event::ProxyExecuted { result: e.map(|_| ()).map_err(|e| e.error) });
1022	}
1023
1024	/// Removes all proxy delegates for a given delegator.
1025	///
1026	/// Parameters:
1027	/// - `delegator`: The delegator account.
1028	pub fn remove_all_proxy_delegates(delegator: &T::AccountId) {
1029		let (_, old_deposit) = Proxies::<T>::take(&delegator);
1030		T::Currency::unreserve(&delegator, old_deposit);
1031	}
1032}