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