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

            
32
mod benchmarking;
33
mod tests;
34
pub mod weights;
35

            
36
extern crate alloc;
37

            
38
use alloc::{boxed::Box, vec};
39
use codec::{Decode, Encode, MaxEncodedLen};
40
use frame_support::{
41
	dispatch::GetDispatchInfo,
42
	ensure,
43
	traits::{Currency, Get, InstanceFilter, IsSubType, IsType, OriginTrait, ReservableCurrency},
44
};
45
use frame_system::{self as system, ensure_signed, pallet_prelude::BlockNumberFor};
46
pub use pallet::*;
47
use scale_info::TypeInfo;
48
use sp_io::hashing::blake2_256;
49
use sp_runtime::{
50
	traits::{Dispatchable, Hash, Saturating, StaticLookup, TrailingZeroInput, Zero},
51
	DispatchError, DispatchResult, RuntimeDebug,
52
};
53
pub use weights::WeightInfo;
54

            
55
type CallHashOf<T> = <<T as Config>::CallHasher as Hash>::Output;
56

            
57
type BalanceOf<T> =
58
	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
59

            
60
type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
61

            
62
/// The parameters under which a particular account has a proxy relationship with some other
63
/// account.
64
#[derive(
65
	Encode,
66
	Decode,
67
	Clone,
68
	Copy,
69
	Eq,
70
	PartialEq,
71
	Ord,
72
	PartialOrd,
73
	RuntimeDebug,
74
	MaxEncodedLen,
75
	TypeInfo,
76
)]
77
pub struct ProxyDefinition<AccountId, ProxyType, BlockNumber> {
78
	/// The account which may act on behalf of another.
79
	pub delegate: AccountId,
80
	/// A value defining the subset of calls that it is allowed to make.
81
	pub proxy_type: ProxyType,
82
	/// The number of blocks that an announcement must be in place for before the corresponding
83
	/// call may be dispatched. If zero, then no announcement is needed.
84
	pub delay: BlockNumber,
85
}
86

            
87
/// Details surrounding a specific instance of an announcement to make a call.
88
#[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
89
pub struct Announcement<AccountId, Hash, BlockNumber> {
90
	/// The account which made the announcement.
91
	real: AccountId,
92
	/// The hash of the call to be made.
93
	call_hash: Hash,
94
	/// The height at which the announcement was made.
95
	height: BlockNumber,
96
}
97

            
98
6461
#[frame_support::pallet]
99
pub mod pallet {
100
	use super::{DispatchResult, *};
101
	use frame_support::pallet_prelude::*;
102
	use frame_system::pallet_prelude::*;
103

            
104
554790
	#[pallet::pallet]
105
	pub struct Pallet<T>(_);
106

            
107
	/// Configuration trait.
108
	#[pallet::config]
109
	pub trait Config: frame_system::Config {
110
		/// The overarching event type.
111
		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
112

            
113
		/// The overarching call type.
114
		type RuntimeCall: Parameter
115
			+ Dispatchable<RuntimeOrigin = Self::RuntimeOrigin>
116
			+ GetDispatchInfo
117
			+ From<frame_system::Call<Self>>
118
			+ IsSubType<Call<Self>>
119
			+ IsType<<Self as frame_system::Config>::RuntimeCall>;
120

            
121
		/// The currency mechanism.
122
		type Currency: ReservableCurrency<Self::AccountId>;
123

            
124
		/// A kind of proxy; specified with the proxy and passed in to the `IsProxyable` filter.
125
		/// The instance filter determines whether a given call may be proxied under this type.
126
		///
127
		/// IMPORTANT: `Default` must be provided and MUST BE the the *most permissive* value.
128
		type ProxyType: Parameter
129
			+ Member
130
			+ Ord
131
			+ PartialOrd
132
			+ InstanceFilter<<Self as Config>::RuntimeCall>
133
			+ Default
134
			+ MaxEncodedLen;
135

            
136
		/// The base amount of currency needed to reserve for creating a proxy.
137
		///
138
		/// This is held for an additional storage item whose value size is
139
		/// `sizeof(Balance)` bytes and whose key size is `sizeof(AccountId)` bytes.
140
		#[pallet::constant]
141
		type ProxyDepositBase: Get<BalanceOf<Self>>;
142

            
143
		/// The amount of currency needed per proxy added.
144
		///
145
		/// This is held for adding 32 bytes plus an instance of `ProxyType` more into a
146
		/// pre-existing storage value. Thus, when configuring `ProxyDepositFactor` one should take
147
		/// into account `32 + proxy_type.encode().len()` bytes of data.
148
		#[pallet::constant]
149
		type ProxyDepositFactor: Get<BalanceOf<Self>>;
150

            
151
		/// The maximum amount of proxies allowed for a single account.
152
		#[pallet::constant]
153
		type MaxProxies: Get<u32>;
154

            
155
		/// Weight information for extrinsics in this pallet.
156
		type WeightInfo: WeightInfo;
157

            
158
		/// The maximum amount of time-delayed announcements that are allowed to be pending.
159
		#[pallet::constant]
160
		type MaxPending: Get<u32>;
161

            
162
		/// The type of hash used for hashing the call.
163
		type CallHasher: Hash;
164

            
165
		/// The base amount of currency needed to reserve for creating an announcement.
166
		///
167
		/// This is held when a new storage item holding a `Balance` is created (typically 16
168
		/// bytes).
169
		#[pallet::constant]
170
		type AnnouncementDepositBase: Get<BalanceOf<Self>>;
171

            
172
		/// The amount of currency needed per announcement made.
173
		///
174
		/// This is held for adding an `AccountId`, `Hash` and `BlockNumber` (typically 68 bytes)
175
		/// into a pre-existing storage value.
176
		#[pallet::constant]
177
		type AnnouncementDepositFactor: Get<BalanceOf<Self>>;
178
	}
179

            
180
33678
	#[pallet::call]
181
	impl<T: Config> Pallet<T> {
182
		/// Dispatch the given `call` from an account that the sender is authorised for through
183
		/// `add_proxy`.
184
		///
185
		/// The dispatch origin for this call must be _Signed_.
186
		///
187
		/// Parameters:
188
		/// - `real`: The account that the proxy will make a call on behalf of.
189
		/// - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call.
190
		/// - `call`: The call to be made by the `real` account.
191
		#[pallet::call_index(0)]
192
		#[pallet::weight({
193
			let di = call.get_dispatch_info();
194
			(T::WeightInfo::proxy(T::MaxProxies::get())
195
				 // AccountData for inner call origin accountdata.
196
				.saturating_add(T::DbWeight::get().reads_writes(1, 1))
197
				.saturating_add(di.weight),
198
			di.class)
199
		})]
200
		pub fn proxy(
201
			origin: OriginFor<T>,
202
			real: AccountIdLookupOf<T>,
203
			force_proxy_type: Option<T::ProxyType>,
204
			call: Box<<T as Config>::RuntimeCall>,
205
483
		) -> DispatchResult {
206
483
			let who = ensure_signed(origin)?;
207
483
			let real = T::Lookup::lookup(real)?;
208
162
			let def = Self::find_proxy(&real, &who, force_proxy_type)?;
209
			ensure!(def.delay.is_zero(), Error::<T>::Unannounced);
210

            
211
			Self::do_proxy(def, real, *call);
212

            
213
			Ok(())
214
		}
215

            
216
		/// Register a proxy account for the sender that is able to make calls on its behalf.
217
		///
218
		/// The dispatch origin for this call must be _Signed_.
219
		///
220
		/// Parameters:
221
		/// - `proxy`: The account that the `caller` would like to make a proxy.
222
		/// - `proxy_type`: The permissions allowed for this proxy account.
223
		/// - `delay`: The announcement period required of the initial proxy. Will generally be
224
		/// zero.
225
		#[pallet::call_index(1)]
226
		#[pallet::weight(T::WeightInfo::add_proxy(T::MaxProxies::get()))]
227
		pub fn add_proxy(
228
			origin: OriginFor<T>,
229
			delegate: AccountIdLookupOf<T>,
230
			proxy_type: T::ProxyType,
231
			delay: BlockNumberFor<T>,
232
4278
		) -> DispatchResult {
233
4278
			let who = ensure_signed(origin)?;
234
4278
			let delegate = T::Lookup::lookup(delegate)?;
235
3990
			Self::add_proxy_delegate(&who, delegate, proxy_type, delay)
236
		}
237

            
238
		/// Unregister a proxy account for the sender.
239
		///
240
		/// The dispatch origin for this call must be _Signed_.
241
		///
242
		/// Parameters:
243
		/// - `proxy`: The account that the `caller` would like to remove as a proxy.
244
		/// - `proxy_type`: The permissions currently enabled for the removed proxy account.
245
		#[pallet::call_index(2)]
246
		#[pallet::weight(T::WeightInfo::remove_proxy(T::MaxProxies::get()))]
247
		pub fn remove_proxy(
248
			origin: OriginFor<T>,
249
			delegate: AccountIdLookupOf<T>,
250
			proxy_type: T::ProxyType,
251
			delay: BlockNumberFor<T>,
252
309
		) -> DispatchResult {
253
309
			let who = ensure_signed(origin)?;
254
309
			let delegate = T::Lookup::lookup(delegate)?;
255
135
			Self::remove_proxy_delegate(&who, delegate, proxy_type, delay)
256
		}
257

            
258
		/// Unregister all proxy accounts for the sender.
259
		///
260
		/// The dispatch origin for this call must be _Signed_.
261
		///
262
		/// WARNING: This may be called on accounts created by `pure`, however if done, then
263
		/// the unreserved fees will be inaccessible. **All access to this account will be lost.**
264
		#[pallet::call_index(3)]
265
		#[pallet::weight(T::WeightInfo::remove_proxies(T::MaxProxies::get()))]
266
159
		pub fn remove_proxies(origin: OriginFor<T>) -> DispatchResult {
267
159
			let who = ensure_signed(origin)?;
268
159
			Self::remove_all_proxy_delegates(&who);
269
159
			Ok(())
270
		}
271

            
272
		/// Spawn a fresh new account that is guaranteed to be otherwise inaccessible, and
273
		/// initialize it with a proxy of `proxy_type` for `origin` sender.
274
		///
275
		/// Requires a `Signed` origin.
276
		///
277
		/// - `proxy_type`: The type of the proxy that the sender will be registered as over the
278
		/// new account. This will almost always be the most permissive `ProxyType` possible to
279
		/// allow for maximum flexibility.
280
		/// - `index`: A disambiguation index, in case this is called multiple times in the same
281
		/// transaction (e.g. with `utility::batch`). Unless you're using `batch` you probably just
282
		/// want to use `0`.
283
		/// - `delay`: The announcement period required of the initial proxy. Will generally be
284
		/// zero.
285
		///
286
		/// Fails with `Duplicate` if this has already been called in this transaction, from the
287
		/// same sender, with the same parameters.
288
		///
289
		/// Fails if there are insufficient funds to pay for deposit.
290
		#[pallet::call_index(4)]
291
		#[pallet::weight(T::WeightInfo::create_pure(T::MaxProxies::get()))]
292
		pub fn create_pure(
293
			origin: OriginFor<T>,
294
			proxy_type: T::ProxyType,
295
			delay: BlockNumberFor<T>,
296
			index: u16,
297
453
		) -> DispatchResult {
298
453
			let who = ensure_signed(origin)?;
299

            
300
453
			let pure = Self::pure_account(&who, &proxy_type, index, None);
301
453
			ensure!(!Proxies::<T>::contains_key(&pure), Error::<T>::Duplicate);
302

            
303
378
			let proxy_def =
304
378
				ProxyDefinition { delegate: who.clone(), proxy_type: proxy_type.clone(), delay };
305
378
			let bounded_proxies: BoundedVec<_, T::MaxProxies> =
306
378
				vec![proxy_def].try_into().map_err(|_| Error::<T>::TooMany)?;
307

            
308
378
			let deposit = T::ProxyDepositBase::get() + T::ProxyDepositFactor::get();
309
378
			T::Currency::reserve(&who, deposit)?;
310

            
311
375
			Proxies::<T>::insert(&pure, (bounded_proxies, deposit));
312
375
			Self::deposit_event(Event::PureCreated {
313
375
				pure,
314
375
				who,
315
375
				proxy_type,
316
375
				disambiguation_index: index,
317
375
			});
318
375

            
319
375
			Ok(())
320
		}
321

            
322
		/// Removes a previously spawned pure proxy.
323
		///
324
		/// WARNING: **All access to this account will be lost.** Any funds held in it will be
325
		/// inaccessible.
326
		///
327
		/// Requires a `Signed` origin, and the sender account must have been created by a call to
328
		/// `pure` with corresponding parameters.
329
		///
330
		/// - `spawner`: The account that originally called `pure` to create this account.
331
		/// - `index`: The disambiguation index originally passed to `pure`. Probably `0`.
332
		/// - `proxy_type`: The proxy type originally passed to `pure`.
333
		/// - `height`: The height of the chain when the call to `pure` was processed.
334
		/// - `ext_index`: The extrinsic index in which the call to `pure` was processed.
335
		///
336
		/// Fails with `NoPermission` in case the caller is not a previously created pure
337
		/// account whose `pure` call has corresponding parameters.
338
		#[pallet::call_index(5)]
339
		#[pallet::weight(T::WeightInfo::kill_pure(T::MaxProxies::get()))]
340
		pub fn kill_pure(
341
			origin: OriginFor<T>,
342
			spawner: AccountIdLookupOf<T>,
343
			proxy_type: T::ProxyType,
344
			index: u16,
345
			#[pallet::compact] height: BlockNumberFor<T>,
346
			#[pallet::compact] ext_index: u32,
347
120
		) -> DispatchResult {
348
120
			let who = ensure_signed(origin)?;
349
120
			let spawner = T::Lookup::lookup(spawner)?;
350

            
351
78
			let when = (height, ext_index);
352
78
			let proxy = Self::pure_account(&spawner, &proxy_type, index, Some(when));
353
78
			ensure!(proxy == who, Error::<T>::NoPermission);
354

            
355
			let (_, deposit) = Proxies::<T>::take(&who);
356
			T::Currency::unreserve(&spawner, deposit);
357

            
358
			Ok(())
359
		}
360

            
361
		/// Publish the hash of a proxy-call that will be made in the future.
362
		///
363
		/// This must be called some number of blocks before the corresponding `proxy` is attempted
364
		/// if the delay associated with the proxy relationship is greater than zero.
365
		///
366
		/// No more than `MaxPending` announcements may be made at any one time.
367
		///
368
		/// This will take a deposit of `AnnouncementDepositFactor` as well as
369
		/// `AnnouncementDepositBase` if there are no other pending announcements.
370
		///
371
		/// The dispatch origin for this call must be _Signed_ and a proxy of `real`.
372
		///
373
		/// Parameters:
374
		/// - `real`: The account that the proxy will make a call on behalf of.
375
		/// - `call_hash`: The hash of the call to be made by the `real` account.
376
		#[pallet::call_index(6)]
377
		#[pallet::weight(T::WeightInfo::announce(T::MaxPending::get(), T::MaxProxies::get()))]
378
		pub fn announce(
379
			origin: OriginFor<T>,
380
			real: AccountIdLookupOf<T>,
381
			call_hash: CallHashOf<T>,
382
171
		) -> DispatchResult {
383
171
			let who = ensure_signed(origin)?;
384
171
			let real = T::Lookup::lookup(real)?;
385
69
			Proxies::<T>::get(&real)
386
69
				.0
387
69
				.into_iter()
388
69
				.find(|x| x.delegate == who)
389
69
				.ok_or(Error::<T>::NotProxy)?;
390

            
391
			let announcement = Announcement {
392
				real: real.clone(),
393
				call_hash,
394
				height: system::Pallet::<T>::block_number(),
395
			};
396

            
397
			Announcements::<T>::try_mutate(&who, |(ref mut pending, ref mut deposit)| {
398
				pending.try_push(announcement).map_err(|_| Error::<T>::TooMany)?;
399
				Self::rejig_deposit(
400
					&who,
401
					*deposit,
402
					T::AnnouncementDepositBase::get(),
403
					T::AnnouncementDepositFactor::get(),
404
					pending.len(),
405
				)
406
				.map(|d| {
407
					d.expect("Just pushed; pending.len() > 0; rejig_deposit returns Some; qed")
408
				})
409
				.map(|d| *deposit = d)
410
			})?;
411
			Self::deposit_event(Event::Announced { real, proxy: who, call_hash });
412

            
413
			Ok(())
414
		}
415

            
416
		/// Remove a given announcement.
417
		///
418
		/// May be called by a proxy account to remove a call they previously announced and return
419
		/// the deposit.
420
		///
421
		/// The dispatch origin for this call must be _Signed_.
422
		///
423
		/// Parameters:
424
		/// - `real`: The account that the proxy will make a call on behalf of.
425
		/// - `call_hash`: The hash of the call to be made by the `real` account.
426
		#[pallet::call_index(7)]
427
		#[pallet::weight(T::WeightInfo::remove_announcement(
428
			T::MaxPending::get(),
429
			T::MaxProxies::get()
430
		))]
431
		pub fn remove_announcement(
432
			origin: OriginFor<T>,
433
			real: AccountIdLookupOf<T>,
434
			call_hash: CallHashOf<T>,
435
90
		) -> DispatchResult {
436
90
			let who = ensure_signed(origin)?;
437
90
			let real = T::Lookup::lookup(real)?;
438
18
			Self::edit_announcements(&who, |ann| ann.real != real || ann.call_hash != call_hash)?;
439

            
440
			Ok(())
441
		}
442

            
443
		/// Remove the given announcement of a delegate.
444
		///
445
		/// May be called by a target (proxied) account to remove a call that one of their delegates
446
		/// (`delegate`) has announced they want to execute. The deposit is returned.
447
		///
448
		/// The dispatch origin for this call must be _Signed_.
449
		///
450
		/// Parameters:
451
		/// - `delegate`: The account that previously announced the call.
452
		/// - `call_hash`: The hash of the call to be made.
453
		#[pallet::call_index(8)]
454
		#[pallet::weight(T::WeightInfo::reject_announcement(
455
			T::MaxPending::get(),
456
			T::MaxProxies::get()
457
		))]
458
		pub fn reject_announcement(
459
			origin: OriginFor<T>,
460
			delegate: AccountIdLookupOf<T>,
461
			call_hash: CallHashOf<T>,
462
96
		) -> DispatchResult {
463
96
			let who = ensure_signed(origin)?;
464
96
			let delegate = T::Lookup::lookup(delegate)?;
465
6
			Self::edit_announcements(&delegate, |ann| {
466
				ann.real != who || ann.call_hash != call_hash
467
6
			})?;
468

            
469
			Ok(())
470
		}
471

            
472
		/// Dispatch the given `call` from an account that the sender is authorized for through
473
		/// `add_proxy`.
474
		///
475
		/// Removes any corresponding announcement(s).
476
		///
477
		/// The dispatch origin for this call must be _Signed_.
478
		///
479
		/// Parameters:
480
		/// - `real`: The account that the proxy will make a call on behalf of.
481
		/// - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call.
482
		/// - `call`: The call to be made by the `real` account.
483
		#[pallet::call_index(9)]
484
		#[pallet::weight({
485
			let di = call.get_dispatch_info();
486
			(T::WeightInfo::proxy_announced(T::MaxPending::get(), T::MaxProxies::get())
487
				 // AccountData for inner call origin accountdata.
488
				.saturating_add(T::DbWeight::get().reads_writes(1, 1))
489
				.saturating_add(di.weight),
490
			di.class)
491
		})]
492
		pub fn proxy_announced(
493
			origin: OriginFor<T>,
494
			delegate: AccountIdLookupOf<T>,
495
			real: AccountIdLookupOf<T>,
496
			force_proxy_type: Option<T::ProxyType>,
497
			call: Box<<T as Config>::RuntimeCall>,
498
291
		) -> DispatchResult {
499
291
			ensure_signed(origin)?;
500
291
			let delegate = T::Lookup::lookup(delegate)?;
501
162
			let real = T::Lookup::lookup(real)?;
502
63
			let def = Self::find_proxy(&real, &delegate, force_proxy_type)?;
503

            
504
			let call_hash = T::CallHasher::hash_of(&call);
505
			let now = system::Pallet::<T>::block_number();
506
			Self::edit_announcements(&delegate, |ann| {
507
				ann.real != real ||
508
					ann.call_hash != call_hash ||
509
					now.saturating_sub(ann.height) < def.delay
510
			})
511
			.map_err(|_| Error::<T>::Unannounced)?;
512

            
513
			Self::do_proxy(def, real, *call);
514

            
515
			Ok(())
516
		}
517
	}
518

            
519
	#[pallet::event]
520
2931
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
521
	pub enum Event<T: Config> {
522
		/// A proxy was executed correctly, with the given.
523
		ProxyExecuted { result: DispatchResult },
524
		/// A pure account has been created by new proxy with given
525
		/// disambiguation index and proxy type.
526
		PureCreated {
527
			pure: T::AccountId,
528
			who: T::AccountId,
529
			proxy_type: T::ProxyType,
530
			disambiguation_index: u16,
531
		},
532
		/// An announcement was placed to make a call in the future.
533
		Announced { real: T::AccountId, proxy: T::AccountId, call_hash: CallHashOf<T> },
534
		/// A proxy was added.
535
		ProxyAdded {
536
			delegator: T::AccountId,
537
			delegatee: T::AccountId,
538
			proxy_type: T::ProxyType,
539
			delay: BlockNumberFor<T>,
540
		},
541
		/// A proxy was removed.
542
		ProxyRemoved {
543
			delegator: T::AccountId,
544
			delegatee: T::AccountId,
545
			proxy_type: T::ProxyType,
546
			delay: BlockNumberFor<T>,
547
		},
548
	}
549

            
550
3882
	#[pallet::error]
551
	pub enum Error<T> {
552
		/// There are too many proxies registered or too many announcements pending.
553
		TooMany,
554
		/// Proxy registration not found.
555
		NotFound,
556
		/// Sender is not a proxy of the account to be proxied.
557
		NotProxy,
558
		/// A call which is incompatible with the proxy type's filter was attempted.
559
		Unproxyable,
560
		/// Account is already a proxy.
561
		Duplicate,
562
		/// Call may not be made by proxy because it may escalate its privileges.
563
		NoPermission,
564
		/// Announcement, if made at all, was made too recently.
565
		Unannounced,
566
		/// Cannot add self as proxy.
567
		NoSelfProxy,
568
	}
569

            
570
	/// The set of account proxies. Maps the account which has delegated to the accounts
571
	/// which are being delegated to, together with the amount held on deposit.
572
5403
	#[pallet::storage]
573
	#[pallet::getter(fn proxies)]
574
	pub type Proxies<T: Config> = StorageMap<
575
		_,
576
		Twox64Concat,
577
		T::AccountId,
578
		(
579
			BoundedVec<
580
				ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>,
581
				T::MaxProxies,
582
			>,
583
			BalanceOf<T>,
584
		),
585
		ValueQuery,
586
	>;
587

            
588
	/// The announcements made by the proxy (key).
589
24
	#[pallet::storage]
590
	#[pallet::getter(fn announcements)]
591
	pub type Announcements<T: Config> = StorageMap<
592
		_,
593
		Twox64Concat,
594
		T::AccountId,
595
		(
596
			BoundedVec<Announcement<T::AccountId, CallHashOf<T>, BlockNumberFor<T>>, T::MaxPending>,
597
			BalanceOf<T>,
598
		),
599
		ValueQuery,
600
	>;
601
}
602

            
603
impl<T: Config> Pallet<T> {
604
	/// Calculate the address of an pure account.
605
	///
606
	/// - `who`: The spawner account.
607
	/// - `proxy_type`: The type of the proxy that the sender will be registered as over the
608
	/// new account. This will almost always be the most permissive `ProxyType` possible to
609
	/// allow for maximum flexibility.
610
	/// - `index`: A disambiguation index, in case this is called multiple times in the same
611
	/// transaction (e.g. with `utility::batch`). Unless you're using `batch` you probably just
612
	/// want to use `0`.
613
	/// - `maybe_when`: The block height and extrinsic index of when the pure account was
614
	/// created. None to use current block height and extrinsic index.
615
531
	pub fn pure_account(
616
531
		who: &T::AccountId,
617
531
		proxy_type: &T::ProxyType,
618
531
		index: u16,
619
531
		maybe_when: Option<(BlockNumberFor<T>, u32)>,
620
531
	) -> T::AccountId {
621
531
		let (height, ext_index) = maybe_when.unwrap_or_else(|| {
622
453
			(
623
453
				system::Pallet::<T>::block_number(),
624
453
				system::Pallet::<T>::extrinsic_index().unwrap_or_default(),
625
453
			)
626
531
		});
627
531
		let entropy = (b"modlpy/proxy____", who, height, ext_index, proxy_type, index)
628
531
			.using_encoded(blake2_256);
629
531
		Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref()))
630
531
			.expect("infinite length input; no invalid inputs for type; qed")
631
531
	}
632

            
633
	/// Register a proxy account for the delegator that is able to make calls on its behalf.
634
	///
635
	/// Parameters:
636
	/// - `delegator`: The delegator account.
637
	/// - `delegatee`: The account that the `delegator` would like to make a proxy.
638
	/// - `proxy_type`: The permissions allowed for this proxy account.
639
	/// - `delay`: The announcement period required of the initial proxy. Will generally be
640
	/// zero.
641
3990
	pub fn add_proxy_delegate(
642
3990
		delegator: &T::AccountId,
643
3990
		delegatee: T::AccountId,
644
3990
		proxy_type: T::ProxyType,
645
3990
		delay: BlockNumberFor<T>,
646
3990
	) -> DispatchResult {
647
3990
		ensure!(delegator != &delegatee, Error::<T>::NoSelfProxy);
648
5316
		Proxies::<T>::try_mutate(delegator, |(ref mut proxies, ref mut deposit)| {
649
3987
			let proxy_def = ProxyDefinition {
650
3987
				delegate: delegatee.clone(),
651
3987
				proxy_type: proxy_type.clone(),
652
3987
				delay,
653
3987
			};
654
3987
			let i = proxies.binary_search(&proxy_def).err().ok_or(Error::<T>::Duplicate)?;
655
2655
			proxies.try_insert(i, proxy_def).map_err(|_| Error::<T>::TooMany)?;
656
2655
			let new_deposit = Self::deposit(proxies.len() as u32);
657
2655
			if new_deposit > *deposit {
658
2655
				T::Currency::reserve(delegator, new_deposit - *deposit)?;
659
			} else if new_deposit < *deposit {
660
				T::Currency::unreserve(delegator, *deposit - new_deposit);
661
			}
662
2556
			*deposit = new_deposit;
663
2556
			Self::deposit_event(Event::<T>::ProxyAdded {
664
2556
				delegator: delegator.clone(),
665
2556
				delegatee,
666
2556
				proxy_type,
667
2556
				delay,
668
2556
			});
669
2556
			Ok(())
670
5316
		})
671
3990
	}
672

            
673
	/// Unregister a proxy account for the delegator.
674
	///
675
	/// Parameters:
676
	/// - `delegator`: The delegator account.
677
	/// - `delegatee`: The account that the `delegator` would like to make a proxy.
678
	/// - `proxy_type`: The permissions allowed for this proxy account.
679
	/// - `delay`: The announcement period required of the initial proxy. Will generally be
680
	/// zero.
681
135
	pub fn remove_proxy_delegate(
682
135
		delegator: &T::AccountId,
683
135
		delegatee: T::AccountId,
684
135
		proxy_type: T::ProxyType,
685
135
		delay: BlockNumberFor<T>,
686
135
	) -> DispatchResult {
687
180
		Proxies::<T>::try_mutate_exists(delegator, |x| {
688
135
			let (mut proxies, old_deposit) = x.take().ok_or(Error::<T>::NotFound)?;
689
33
			let proxy_def = ProxyDefinition {
690
33
				delegate: delegatee.clone(),
691
33
				proxy_type: proxy_type.clone(),
692
33
				delay,
693
33
			};
694
33
			let i = proxies.binary_search(&proxy_def).ok().ok_or(Error::<T>::NotFound)?;
695
			proxies.remove(i);
696
			let new_deposit = Self::deposit(proxies.len() as u32);
697
			if new_deposit > old_deposit {
698
				T::Currency::reserve(delegator, new_deposit - old_deposit)?;
699
			} else if new_deposit < old_deposit {
700
				T::Currency::unreserve(delegator, old_deposit - new_deposit);
701
			}
702
			if !proxies.is_empty() {
703
				*x = Some((proxies, new_deposit))
704
			}
705
			Self::deposit_event(Event::<T>::ProxyRemoved {
706
				delegator: delegator.clone(),
707
				delegatee,
708
				proxy_type,
709
				delay,
710
			});
711
			Ok(())
712
180
		})
713
135
	}
714

            
715
2655
	pub fn deposit(num_proxies: u32) -> BalanceOf<T> {
716
2655
		if num_proxies == 0 {
717
			Zero::zero()
718
		} else {
719
2655
			T::ProxyDepositBase::get() + T::ProxyDepositFactor::get() * num_proxies.into()
720
		}
721
2655
	}
722

            
723
	fn rejig_deposit(
724
		who: &T::AccountId,
725
		old_deposit: BalanceOf<T>,
726
		base: BalanceOf<T>,
727
		factor: BalanceOf<T>,
728
		len: usize,
729
	) -> Result<Option<BalanceOf<T>>, DispatchError> {
730
		let new_deposit =
731
			if len == 0 { BalanceOf::<T>::zero() } else { base + factor * (len as u32).into() };
732
		if new_deposit > old_deposit {
733
			T::Currency::reserve(who, new_deposit - old_deposit)?;
734
		} else if new_deposit < old_deposit {
735
			T::Currency::unreserve(who, old_deposit - new_deposit);
736
		}
737
		Ok(if len == 0 { None } else { Some(new_deposit) })
738
	}
739

            
740
24
	fn edit_announcements<
741
24
		F: FnMut(&Announcement<T::AccountId, CallHashOf<T>, BlockNumberFor<T>>) -> bool,
742
24
	>(
743
24
		delegate: &T::AccountId,
744
24
		f: F,
745
24
	) -> DispatchResult {
746
32
		Announcements::<T>::try_mutate_exists(delegate, |x| {
747
24
			let (mut pending, old_deposit) = x.take().ok_or(Error::<T>::NotFound)?;
748
			let orig_pending_len = pending.len();
749
			pending.retain(f);
750
			ensure!(orig_pending_len > pending.len(), Error::<T>::NotFound);
751
			*x = Self::rejig_deposit(
752
				delegate,
753
				old_deposit,
754
				T::AnnouncementDepositBase::get(),
755
				T::AnnouncementDepositFactor::get(),
756
				pending.len(),
757
			)?
758
			.map(|deposit| (pending, deposit));
759
			Ok(())
760
32
		})
761
24
	}
762

            
763
225
	pub fn find_proxy(
764
225
		real: &T::AccountId,
765
225
		delegate: &T::AccountId,
766
225
		force_proxy_type: Option<T::ProxyType>,
767
225
	) -> Result<ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>, DispatchError> {
768
236
		let f = |x: &ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>| -> bool {
769
33
			&x.delegate == delegate &&
770
				force_proxy_type.as_ref().map_or(true, |y| &x.proxy_type == y)
771
33
		};
772
225
		Ok(Proxies::<T>::get(real).0.into_iter().find(f).ok_or(Error::<T>::NotProxy)?)
773
225
	}
774

            
775
	fn do_proxy(
776
		def: ProxyDefinition<T::AccountId, T::ProxyType, BlockNumberFor<T>>,
777
		real: T::AccountId,
778
		call: <T as Config>::RuntimeCall,
779
	) {
780
		// This is a freshly authenticated new account, the origin restrictions doesn't apply.
781
		let mut origin: T::RuntimeOrigin = frame_system::RawOrigin::Signed(real).into();
782
		origin.add_filter(move |c: &<T as frame_system::Config>::RuntimeCall| {
783
			let c = <T as Config>::RuntimeCall::from_ref(c);
784
			// We make sure the proxy call does access this pallet to change modify proxies.
785
			match c.is_sub_type() {
786
				// Proxy call cannot add or remove a proxy with more permissions than it already
787
				// has.
788
				Some(Call::add_proxy { ref proxy_type, .. }) |
789
				Some(Call::remove_proxy { ref proxy_type, .. })
790
					if !def.proxy_type.is_superset(proxy_type) =>
791
					false,
792
				// Proxy call cannot remove all proxies or kill pure proxies unless it has full
793
				// permissions.
794
				Some(Call::remove_proxies { .. }) | Some(Call::kill_pure { .. })
795
					if def.proxy_type != T::ProxyType::default() =>
796
					false,
797
				_ => def.proxy_type.filter(c),
798
			}
799
		});
800
		let e = call.dispatch(origin);
801
		Self::deposit_event(Event::ProxyExecuted { result: e.map(|_| ()).map_err(|e| e.error) });
802
	}
803

            
804
	/// Removes all proxy delegates for a given delegator.
805
	///
806
	/// Parameters:
807
	/// - `delegator`: The delegator account.
808
159
	pub fn remove_all_proxy_delegates(delegator: &T::AccountId) {
809
159
		let (_, old_deposit) = Proxies::<T>::take(&delegator);
810
159
		T::Currency::unreserve(&delegator, old_deposit);
811
159
	}
812
}