pallet_permission0/
lib.rs

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    /// Configure the pallet by specifying the parameters and types on which it depends.
45    #[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        /// Permission0 pallet ID
51        #[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        /// Maximum number of controllers per permission.
61        #[pallet::constant]
62        type MaxControllersPerPermission: Get<u32>;
63
64        /// Maximum number of revokers.
65        #[pallet::constant]
66        type MaxRevokersPerPermission: Get<u32>;
67
68        /// Maximum number of recipients per permission.
69        #[pallet::constant]
70        type MaxRecipientsPerPermission: Get<u32>;
71
72        /// Maximum number of delegated streams per permission.
73        #[pallet::constant]
74        type MaxStreamsPerPermission: Get<u32>;
75
76        /// Minimum threshold for auto-distribution
77        #[pallet::constant]
78        type MinAutoDistributionThreshold: Get<BalanceOf<Self>>;
79
80        /// Maximum number of namespaces a single permission can delegate.
81        #[pallet::constant]
82        type MaxNamespacesPerPermission: Get<u32>;
83
84        /// Maximum number of curator subpermissions a single permission can delegate.
85        #[pallet::constant]
86        type MaxCuratorSubpermissionsPerPermission: Get<u32>;
87
88        /// Maximum number of children a single permission can have.
89        #[pallet::constant]
90        type MaxChildrenPerPermission: Get<u32>;
91
92        /// Max operations a bulk extrinsic can perform per extrinsic call.
93        #[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    /// Active permission contracts - stored by permission ID
111    #[pallet::storage]
112    pub type Permissions<T: Config> = StorageMap<_, Identity, PermissionId, PermissionContract<T>>;
113
114    /// Mapping from (delegator, recipient) to permission IDs
115    #[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    /// Permissions delegated by a specific account
125    #[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    /// Permissions received by a specific account
135    #[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    /// Revocations in progress and the voters
145    #[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    /// Enforcement votes in progress and the voters
155    #[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    /// Accumulated amounts for each stream
167    #[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        /// Permission delegated from delegator to recipient with ID
182        PermissionDelegated {
183            delegator: T::AccountId,
184            permission_id: PermissionId,
185        },
186        /// Permission revoked with ID
187        PermissionRevoked {
188            delegator: T::AccountId,
189            revoked_by: Option<T::AccountId>,
190            permission_id: PermissionId,
191        },
192        /// Permission expired with ID
193        PermissionExpired {
194            delegator: T::AccountId,
195            permission_id: PermissionId,
196        },
197        /// Permission accumulation state toggled
198        PermissionAccumulationToggled {
199            permission_id: PermissionId,
200            accumulating: bool,
201            toggled_by: Option<T::AccountId>,
202        },
203        /// Permission was executed by enforcement authority
204        PermissionEnforcementExecuted {
205            permission_id: PermissionId,
206            executed_by: Option<T::AccountId>,
207        },
208        /// Vote for enforcement action
209        EnforcementVoteCast {
210            permission_id: PermissionId,
211            voter: T::AccountId,
212            referendum: EnforcementReferendum,
213        },
214        /// Enforcement authority set for permission
215        EnforcementAuthoritySet {
216            permission_id: PermissionId,
217            controllers_count: u32,
218            required_votes: u32,
219        },
220        /// An stream distribution happened
221        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        /// Accumulated emission for stream
229        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        /// The agent is not registered
239        NotRegisteredAgent,
240        /// Permissions can only be created through extrinsics
241        PermissionCreationOutsideExtrinsic,
242        /// A permission with the same exact parameters was
243        /// already created in the current block
244        DuplicatePermissionInBlock,
245        /// Permission not found
246        PermissionNotFound,
247        /// Self-permission is not allowed
248        SelfPermissionNotAllowed,
249        /// Invalid percentage (out of range)
250        InvalidPercentage,
251        /// Invalid stream weight set to recipient
252        InvalidRecipientWeight,
253        /// No recipients specified
254        NoRecipientsSpecified,
255        /// Invalid threshold
256        InvalidThreshold,
257        /// No accumulated amount
258        NoAccumulatedAmount,
259        /// Not authorized to revoke
260        NotAuthorizedToRevoke,
261        /// Total allocation exceeded 100%
262        TotalAllocationExceeded,
263        /// Not the recipient of the permission
264        NotPermissionRecipient,
265        /// Not the delegator of the permission
266        NotPermissionDelegator,
267        /// Too many streams
268        TooManyStreams,
269        /// Too many recipients
270        TooManyRecipients,
271        /// Too many revokers
272        TooManyRevokers,
273        /// Failed to insert into storage
274        StorageError,
275        /// Invalid amount
276        InvalidAmount,
277        /// Insufficient balance for operation
278        InsufficientBalance,
279        /// Invalid distribution interval
280        InvalidInterval,
281        /// Parent permission not found
282        ParentPermissionNotFound,
283        /// Invalid distribution method
284        InvalidDistributionMethod,
285        /// Revokers and required voters must be at least one, and required voters must
286        /// be less than the number of revokers
287        InvalidNumberOfRevokers,
288        /// Fixed amount streams can only be triggered once, manually or at a block
289        FixedAmountCanOnlyBeTriggeredOnce,
290        /// Unsupported permission type
291        UnsupportedPermissionType,
292        /// Not authorized to toggle permission state
293        NotAuthorizedToToggle,
294        /// Too many controllers
295        TooManyControllers,
296        /// Invalid number of controllers or required votes
297        InvalidNumberOfControllers,
298        /// Permission is a duplicate, revoke the previous one
299        DuplicatePermission,
300        /// Permission is in cooldown, wait a bit.
301        PermissionInCooldown,
302        /// Curator flags provided are invalid.
303        InvalidCuratorPermissions,
304        /// Tried delegating unknown namespace.
305        NamespaceDoesNotExist,
306        /// Namespace path provided contains illegal character or is malformatted.
307        NamespacePathIsInvalid,
308        /// Exceeded amount of total namespaces allowed in a single permission.
309        TooManyNamespaces,
310        /// Not authorized to edit a permission.
311        NotAuthorizedToEdit,
312        /// Permission is not editable
313        NotEditable,
314        /// Namespace creation was disabled by a curator.
315        NamespaceCreationDisabled,
316        /// Deriving a permission from multiple parents is still forbidden.
317        MultiParentForbidden,
318        /// Not enough instances available to delegate/execute a permission.
319        /// This might mean the execution requires more than the available instances
320        /// or that all instances are locked behind redelegations.
321        NotEnoughInstances,
322        /// Too many children for a permission.
323        TooManyChildren,
324        /// Stream managers must have up to two entries and always contain the delegator,
325        InvalidStreamManagers,
326        /// Revocation terms are too strong for a permission re-delegation.
327        RevocationTermsTooStrong,
328        /// Too many curator permissions being delegated in a single permission.
329        TooManyCuratorPermissions,
330        /// Namespace delegation depth exceeded the maximum allowed limit.
331        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        /// Delegate a permission for stream delegation
344        #[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        /// Revoke a permission. The caller must meet revocation constraints or be a root key.
375        #[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        /// Execute a manual distribution based on permission
385        #[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        /// Toggle a permission's accumulation state (enabled/disabled)
395        /// The caller must be authorized as a controller or be the root key
396        #[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        /// Execute a permission through enforcement authority
411        /// The caller must be authorized as a controller or be the root key
412        #[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        /// Set enforcement authority for a permission
422        /// Only the delegator or root can set enforcement authority
423        #[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        /// Delegate a permission for curator delegation
446        #[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        /// Delegate a permission over namespaces
474        #[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        /// Delegate a permission over namespaces
496        #[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        /// Delegate a permission over namespaces to multiple recipients.
517        /// Note: this extrinsic creates _multiple_ permissions with the same
518        /// properties.
519        #[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        /// Allows Delegator/Recipient to edit stream permission
551        #[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        /// Allows a delegator to update the number of instances of a permission
578        #[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
606/// Get total allocated percentage for a delegator
607fn 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}