1#![cfg_attr(not(feature = "std"), no_std)]
2#![allow(clippy::too_many_arguments)]
3
4pub mod benchmarking;
5pub mod weights;
6pub use weights::*;
7
8pub mod ext;
9pub mod migrations;
10pub mod permission;
11
12pub use pallet::*;
13
14pub use permission::{
15 CuratorPermissions, CuratorScope, DistributionControl, EnforcementAuthority,
16 EnforcementReferendum, PermissionContract, PermissionDuration, PermissionId, PermissionScope,
17 RevocationTerms, StreamAllocation, StreamScope, generate_permission_id,
18};
19
20pub use pallet_permission0_api::{StreamId, generate_root_stream_id};
21
22use polkadot_sdk::{
23 frame_support::{
24 dispatch::DispatchResult,
25 pallet_prelude::*,
26 traits::{Currency, Get, ReservableCurrency},
27 },
28 frame_system::{self, pallet_prelude::*},
29 polkadot_sdk_frame as frame,
30 sp_runtime::{Percent, traits::Saturating},
31 sp_std::prelude::*,
32};
33
34#[frame::pallet]
35pub mod pallet {
36 use pallet_torus0_api::NamespacePathInner;
37 use permission::wallet::WalletStake;
38 use polkadot_sdk::{frame_support::PalletId, sp_core::TryCollect};
39
40 use super::*;
41
42 const STORAGE_VERSION: StorageVersion = StorageVersion::new(7);
43
44 #[pallet::config]
46 pub trait Config: polkadot_sdk::frame_system::Config {
47 type RuntimeEvent: From<Event<Self>>
48 + IsType<<Self as polkadot_sdk::frame_system::Config>::RuntimeEvent>;
49
50 #[pallet::constant]
52 type PalletId: Get<PalletId>;
53
54 type WeightInfo: WeightInfo;
55
56 type Currency: ReservableCurrency<Self::AccountId>;
57
58 type Torus: pallet_torus0_api::Torus0Api<Self::AccountId, BalanceOf<Self>>;
59
60 #[pallet::constant]
62 type MaxControllersPerPermission: Get<u32>;
63
64 #[pallet::constant]
66 type MaxRevokersPerPermission: Get<u32>;
67
68 #[pallet::constant]
70 type MaxRecipientsPerPermission: Get<u32>;
71
72 #[pallet::constant]
74 type MaxStreamsPerPermission: Get<u32>;
75
76 #[pallet::constant]
78 type MinAutoDistributionThreshold: Get<BalanceOf<Self>>;
79
80 #[pallet::constant]
82 type MaxNamespacesPerPermission: Get<u32>;
83
84 #[pallet::constant]
86 type MaxCuratorSubpermissionsPerPermission: Get<u32>;
87
88 #[pallet::constant]
90 type MaxChildrenPerPermission: Get<u32>;
91
92 #[pallet::constant]
94 type MaxBulkOperationsPerCall: Get<u32>;
95 }
96
97 pub type BalanceOf<T> =
98 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
99
100 pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
101
102 pub type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<
103 <T as frame_system::Config>::AccountId,
104 >>::NegativeImbalance;
105
106 #[pallet::pallet]
107 #[pallet::storage_version(STORAGE_VERSION)]
108 pub struct Pallet<T>(_);
109
110 #[pallet::storage]
112 pub type Permissions<T: Config> = StorageMap<_, Identity, PermissionId, PermissionContract<T>>;
113
114 #[pallet::storage]
116 pub type PermissionsByParticipants<T: Config> = StorageMap<
117 _,
118 Identity,
119 (T::AccountId, T::AccountId),
120 BoundedBTreeSet<PermissionId, T::MaxRecipientsPerPermission>,
121 ValueQuery,
122 >;
123
124 #[pallet::storage]
126 pub type PermissionsByDelegator<T: Config> = StorageMap<
127 _,
128 Identity,
129 T::AccountId,
130 BoundedBTreeSet<PermissionId, T::MaxRecipientsPerPermission>,
131 ValueQuery,
132 >;
133
134 #[pallet::storage]
136 pub type PermissionsByRecipient<T: Config> = StorageMap<
137 _,
138 Identity,
139 T::AccountId,
140 BoundedBTreeSet<PermissionId, T::MaxRecipientsPerPermission>,
141 ValueQuery,
142 >;
143
144 #[pallet::storage]
146 pub type RevocationTracking<T: Config> = StorageMap<
147 _,
148 Identity,
149 PermissionId,
150 BoundedBTreeSet<T::AccountId, T::MaxRevokersPerPermission>,
151 ValueQuery,
152 >;
153
154 #[pallet::storage]
156 pub type EnforcementTracking<T: Config> = StorageDoubleMap<
157 _,
158 Identity,
159 PermissionId,
160 Identity,
161 EnforcementReferendum,
162 BoundedBTreeSet<T::AccountId, T::MaxControllersPerPermission>,
163 ValueQuery,
164 >;
165
166 #[pallet::storage]
168 pub type AccumulatedStreamAmounts<T: Config> = StorageNMap<
169 _,
170 (
171 NMapKey<Identity, T::AccountId>,
172 NMapKey<Identity, StreamId>,
173 NMapKey<Identity, PermissionId>,
174 ),
175 BalanceOf<T>,
176 >;
177
178 #[pallet::event]
179 #[pallet::generate_deposit(pub(super) fn deposit_event)]
180 pub enum Event<T: Config> {
181 PermissionDelegated {
183 delegator: T::AccountId,
184 permission_id: PermissionId,
185 },
186 PermissionRevoked {
188 delegator: T::AccountId,
189 revoked_by: Option<T::AccountId>,
190 permission_id: PermissionId,
191 },
192 PermissionExpired {
194 delegator: T::AccountId,
195 permission_id: PermissionId,
196 },
197 PermissionAccumulationToggled {
199 permission_id: PermissionId,
200 accumulating: bool,
201 toggled_by: Option<T::AccountId>,
202 },
203 PermissionEnforcementExecuted {
205 permission_id: PermissionId,
206 executed_by: Option<T::AccountId>,
207 },
208 EnforcementVoteCast {
210 permission_id: PermissionId,
211 voter: T::AccountId,
212 referendum: EnforcementReferendum,
213 },
214 EnforcementAuthoritySet {
216 permission_id: PermissionId,
217 controllers_count: u32,
218 required_votes: u32,
219 },
220 StreamDistribution {
222 permission_id: PermissionId,
223 stream_id: Option<StreamId>,
224 recipient: T::AccountId,
225 amount: BalanceOf<T>,
226 reason: permission::stream::DistributionReason,
227 },
228 AccumulatedEmission {
230 permission_id: PermissionId,
231 stream_id: StreamId,
232 amount: BalanceOf<T>,
233 },
234 }
235
236 #[pallet::error]
237 pub enum Error<T> {
238 NotRegisteredAgent,
240 PermissionCreationOutsideExtrinsic,
242 DuplicatePermissionInBlock,
245 PermissionNotFound,
247 SelfPermissionNotAllowed,
249 InvalidPercentage,
251 InvalidRecipientWeight,
253 NoRecipientsSpecified,
255 InvalidThreshold,
257 NoAccumulatedAmount,
259 NotAuthorizedToRevoke,
261 TotalAllocationExceeded,
263 NotPermissionRecipient,
265 NotPermissionDelegator,
267 TooManyStreams,
269 TooManyRecipients,
271 TooManyRevokers,
273 StorageError,
275 InvalidAmount,
277 InsufficientBalance,
279 InvalidInterval,
281 ParentPermissionNotFound,
283 InvalidDistributionMethod,
285 InvalidNumberOfRevokers,
288 FixedAmountCanOnlyBeTriggeredOnce,
290 UnsupportedPermissionType,
292 NotAuthorizedToToggle,
294 TooManyControllers,
296 InvalidNumberOfControllers,
298 DuplicatePermission,
300 PermissionInCooldown,
302 InvalidCuratorPermissions,
304 NamespaceDoesNotExist,
306 NamespacePathIsInvalid,
308 TooManyNamespaces,
310 NotAuthorizedToEdit,
312 NotEditable,
314 NamespaceCreationDisabled,
316 MultiParentForbidden,
318 NotEnoughInstances,
322 TooManyChildren,
324 InvalidStreamManagers,
326 RevocationTermsTooStrong,
328 TooManyCuratorPermissions,
330 DelegationDepthExceeded,
332 }
333
334 #[pallet::hooks]
335 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
336 fn on_finalize(current_block: BlockNumberFor<T>) {
337 permission::do_auto_permission_execution::<T>(current_block);
338 }
339 }
340
341 #[pallet::call]
342 impl<T: Config> Pallet<T> {
343 #[pallet::call_index(0)]
345 #[pallet::weight(T::WeightInfo::delegate_stream_permission())]
346 pub fn delegate_stream_permission(
347 origin: OriginFor<T>,
348 recipients: BoundedBTreeMap<T::AccountId, u16, T::MaxRecipientsPerPermission>,
349 allocation: StreamAllocation<T>,
350 distribution: DistributionControl<T>,
351 duration: PermissionDuration<T>,
352 revocation: RevocationTerms<T>,
353 enforcement: EnforcementAuthority<T>,
354 recipient_manager: Option<T::AccountId>,
355 weight_setter: Option<T::AccountId>,
356 ) -> DispatchResult {
357 let delegator = ensure_signed(origin)?;
358
359 ext::stream_impl::delegate_stream_permission_impl::<T>(
360 delegator,
361 recipients,
362 allocation,
363 distribution,
364 duration,
365 revocation,
366 enforcement,
367 recipient_manager,
368 weight_setter,
369 )?;
370
371 Ok(())
372 }
373
374 #[pallet::call_index(1)]
376 #[pallet::weight(T::WeightInfo::revoke_permission())]
377 pub fn revoke_permission(
378 origin: OriginFor<T>,
379 permission_id: PermissionId,
380 ) -> DispatchResult {
381 ext::revoke_permission_impl::<T>(origin, &permission_id)
382 }
383
384 #[pallet::call_index(2)]
386 #[pallet::weight(T::WeightInfo::execute_permission())]
387 pub fn execute_permission(
388 origin: OriginFor<T>,
389 permission_id: PermissionId,
390 ) -> DispatchResult {
391 ext::execute_permission_impl::<T>(origin, &permission_id)
392 }
393
394 #[pallet::call_index(3)]
397 #[pallet::weight(T::WeightInfo::toggle_permission_accumulation())]
398 pub fn toggle_permission_accumulation(
399 origin: OriginFor<T>,
400 permission_id: PermissionId,
401 accumulating: bool,
402 ) -> DispatchResult {
403 ext::stream_impl::toggle_permission_accumulation_impl::<T>(
404 origin,
405 permission_id,
406 accumulating,
407 )
408 }
409
410 #[pallet::call_index(4)]
413 #[pallet::weight(T::WeightInfo::enforcement_execute_permission())]
414 pub fn enforcement_execute_permission(
415 origin: OriginFor<T>,
416 permission_id: PermissionId,
417 ) -> DispatchResult {
418 ext::enforcement_execute_permission_impl::<T>(origin, permission_id)
419 }
420
421 #[pallet::call_index(5)]
424 #[pallet::weight(T::WeightInfo::set_enforcement_authority())]
425 pub fn set_enforcement_authority(
426 origin: OriginFor<T>,
427 permission_id: PermissionId,
428 enforcement: EnforcementAuthority<T>,
429 ) -> DispatchResult {
430 let who = ensure_signed_or_root(origin)?;
431
432 let contract =
433 Permissions::<T>::get(permission_id).ok_or(Error::<T>::PermissionNotFound)?;
434
435 if let Some(who) = &who {
436 ensure!(
437 who == &contract.delegator,
438 Error::<T>::NotPermissionDelegator
439 );
440 }
441
442 contract.update_enforcement(permission_id, enforcement)
443 }
444
445 #[pallet::call_index(6)]
447 #[pallet::weight(T::WeightInfo::delegate_curator_permission())]
448 pub fn delegate_curator_permission(
449 origin: OriginFor<T>,
450 recipient: T::AccountId,
451 flags: BoundedBTreeMap<
452 Option<PermissionId>,
453 u32,
454 T::MaxCuratorSubpermissionsPerPermission,
455 >,
456 cooldown: Option<BlockNumberFor<T>>,
457 duration: PermissionDuration<T>,
458 revocation: RevocationTerms<T>,
459 instances: u32,
460 ) -> DispatchResult {
461 let flags = flags
462 .into_iter()
463 .map(|(pid, flags)| (pid, CuratorPermissions::from_bits_truncate(flags)))
464 .try_collect()?;
465
466 ext::curator_impl::delegate_curator_permission_impl::<T>(
467 origin, recipient, flags, cooldown, duration, revocation, instances,
468 )?;
469
470 Ok(())
471 }
472
473 #[pallet::call_index(7)]
475 #[pallet::weight(T::WeightInfo::delegate_namespace_permission())]
476 pub fn delegate_namespace_permission(
477 origin: OriginFor<T>,
478 recipient: T::AccountId,
479 paths: BoundedBTreeMap<
480 Option<PermissionId>,
481 BoundedBTreeSet<NamespacePathInner, T::MaxNamespacesPerPermission>,
482 T::MaxNamespacesPerPermission,
483 >,
484 duration: PermissionDuration<T>,
485 revocation: RevocationTerms<T>,
486 instances: u32,
487 ) -> DispatchResult {
488 ext::namespace_impl::delegate_namespace_permission_impl::<T>(
489 origin, recipient, paths, duration, revocation, instances,
490 )?;
491
492 Ok(())
493 }
494
495 #[pallet::call_index(11)]
497 #[pallet::weight(T::WeightInfo::delegate_namespace_permission())]
498 pub fn delegate_wallet_stake_permission(
499 origin: OriginFor<T>,
500 recipient: T::AccountId,
501 stake_details: WalletStake,
502 duration: PermissionDuration<T>,
503 revocation: RevocationTerms<T>,
504 ) -> DispatchResult {
505 ext::wallet_impl::delegate_wallet_stake_permission::<T>(
506 origin,
507 recipient,
508 stake_details,
509 duration,
510 revocation,
511 )?;
512
513 Ok(())
514 }
515
516 #[pallet::call_index(10)]
520 #[pallet::weight({
521 T::WeightInfo::delegate_namespace_permission()
522 .saturating_mul(recipients.len() as u64)
523 })]
524 pub fn bulk_delegate_namespace_permission(
525 origin: OriginFor<T>,
526 recipients: BoundedBTreeSet<T::AccountId, T::MaxBulkOperationsPerCall>,
527 paths: BoundedBTreeMap<
528 Option<PermissionId>,
529 BoundedBTreeSet<NamespacePathInner, T::MaxNamespacesPerPermission>,
530 T::MaxNamespacesPerPermission,
531 >,
532 duration: PermissionDuration<T>,
533 revocation: RevocationTerms<T>,
534 instances: u32,
535 ) -> DispatchResult {
536 for recipient in recipients {
537 ext::namespace_impl::delegate_namespace_permission_impl::<T>(
538 origin.clone(),
539 recipient,
540 paths.clone(),
541 duration.clone(),
542 revocation.clone(),
543 instances,
544 )?;
545 }
546
547 Ok(())
548 }
549
550 #[pallet::call_index(8)]
552 #[pallet::weight(T::WeightInfo::delegate_curator_permission())]
553 pub fn update_stream_permission(
554 origin: OriginFor<T>,
555 permission_id: PermissionId,
556 new_recipients: Option<
557 BoundedBTreeMap<T::AccountId, u16, T::MaxRecipientsPerPermission>,
558 >,
559 new_streams: Option<BoundedBTreeMap<StreamId, Percent, T::MaxStreamsPerPermission>>,
560 new_distribution_control: Option<DistributionControl<T>>,
561 new_recipient_manager: Option<Option<T::AccountId>>,
562 new_weight_setter: Option<Option<T::AccountId>>,
563 ) -> DispatchResult {
564 ext::stream_impl::update_stream_permission(
565 origin,
566 permission_id,
567 new_recipients,
568 new_streams,
569 new_distribution_control,
570 new_recipient_manager,
571 new_weight_setter,
572 )?;
573
574 Ok(())
575 }
576
577 #[pallet::call_index(9)]
579 #[pallet::weight(T::WeightInfo::update_namespace_permission())]
580 pub fn update_namespace_permission(
581 origin: OriginFor<T>,
582 permission_id: PermissionId,
583 max_instances: u32,
584 ) -> DispatchResult {
585 ext::namespace_impl::update_namespace_permission::<T>(
586 origin,
587 permission_id,
588 max_instances,
589 )?;
590
591 Ok(())
592 }
593
594 #[pallet::call_index(12)]
595 #[pallet::weight(T::WeightInfo::update_namespace_permission())]
596 pub fn execute_wallet_stake_permission(
597 caller: OriginFor<T>,
598 permission_id: PermissionId,
599 op: ext::wallet_impl::WalletStakeOperation<T>,
600 ) -> DispatchResult {
601 ext::wallet_impl::execute_wallet_stake_permission(caller, permission_id, op)
602 }
603 }
604}
605
606fn get_total_allocated_percentage<T: Config>(
608 delegator: &T::AccountId,
609 stream: &StreamId,
610) -> Percent {
611 AccumulatedStreamAmounts::<T>::iter_key_prefix((delegator, stream))
612 .filter_map(Permissions::<T>::get)
613 .map(|contract| match contract.scope {
614 PermissionScope::Stream(StreamScope {
615 allocation: StreamAllocation::Streams(streams),
616 ..
617 }) => streams.get(stream).copied().unwrap_or_default(),
618 _ => Percent::zero(),
619 })
620 .fold(Percent::zero(), |acc, percentage| {
621 acc.saturating_add(percentage)
622 })
623}