1+ use crate :: permission:: EnforcementReferendum ;
12use crate :: {
23 generate_permission_id, get_total_allocated_percentage, pallet,
34 permission:: emission:: DistributionReason , update_permission_indices, AccumulatedStreamAmounts ,
4- BalanceOf , Config , DistributionControl , EmissionAllocation , EmissionScope , Error , Event ,
5- Pallet , PermissionContract , PermissionDuration , PermissionId , PermissionScope , Permissions ,
6- RevocationTerms ,
5+ BalanceOf , Config , DistributionControl , EmissionAllocation , EmissionScope ,
6+ EnforcementAuthority , EnforcementTracking , Error , Event , Pallet , PermissionContract ,
7+ PermissionDuration , PermissionId , PermissionScope , Permissions , RevocationTerms ,
78} ;
89use pallet_permission0_api:: {
910 DistributionControl as ApiDistributionControl , EmissionAllocation as ApiEmissionAllocation ,
10- Permission0Api , PermissionDuration as ApiPermissionDuration ,
11- RevocationTerms as ApiRevocationTerms , StreamId ,
11+ EnforcementAuthority as ApiEnforcementAuthority , Permission0Api ,
12+ PermissionDuration as ApiPermissionDuration , RevocationTerms as ApiRevocationTerms , StreamId ,
1213} ;
1314use pallet_torus0_api:: Torus0Api ;
1415use polkadot_sdk:: polkadot_sdk_frame:: traits:: CheckedAdd ;
@@ -45,6 +46,7 @@ impl<T: Config>
4546 distribution : ApiDistributionControl < crate :: BalanceOf < T > , BlockNumberFor < T > > ,
4647 duration : ApiPermissionDuration < BlockNumberFor < T > > ,
4748 revocation : ApiRevocationTerms < T :: AccountId , BlockNumberFor < T > > ,
49+ enforcement : ApiEnforcementAuthority < T :: AccountId > ,
4850 ) -> Result < PermissionId , DispatchError > {
4951 let internal_allocation = match allocation {
5052 ApiEmissionAllocation :: Streams ( streams) => EmissionAllocation :: Streams (
@@ -84,6 +86,19 @@ impl<T: Config>
8486 ApiRevocationTerms :: RevocableAfter ( blocks) => RevocationTerms :: RevocableAfter ( blocks) ,
8587 } ;
8688
89+ let internal_enforcement = match enforcement {
90+ ApiEnforcementAuthority :: None => EnforcementAuthority :: None ,
91+ ApiEnforcementAuthority :: ControlledBy {
92+ controllers,
93+ required_votes,
94+ } => EnforcementAuthority :: ControlledBy {
95+ controllers : controllers
96+ . try_into ( )
97+ . map_err ( |_| crate :: Error :: < T > :: TooManyControllers ) ?,
98+ required_votes,
99+ } ,
100+ } ;
101+
87102 grant_permission_impl :: < T > (
88103 grantor,
89104 grantee,
@@ -92,7 +107,8 @@ impl<T: Config>
92107 internal_distribution,
93108 internal_duration,
94109 internal_revocation,
95- None ,
110+ internal_enforcement,
111+ None , // No parent by default
96112 )
97113 }
98114
@@ -139,6 +155,7 @@ pub(crate) fn grant_permission_impl<T: Config>(
139155 distribution : DistributionControl < T > ,
140156 duration : PermissionDuration < T > ,
141157 revocation : RevocationTerms < T > ,
158+ enforcement : EnforcementAuthority < T > ,
142159 parent_id : Option < PermissionId > ,
143160) -> Result < PermissionId , DispatchError > {
144161 use polkadot_sdk:: frame_support:: ensure;
@@ -217,7 +234,7 @@ pub(crate) fn grant_permission_impl<T: Config>(
217234 required_votes,
218235 } => {
219236 ensure ! ( * required_votes > 0 , Error :: <T >:: InvalidNumberOfRevokers ) ;
220- ensure ! ( accounts. len ( ) > 0 , Error :: <T >:: InvalidNumberOfRevokers ) ;
237+ ensure ! ( ! accounts. is_empty ( ) , Error :: <T >:: InvalidNumberOfRevokers ) ;
221238
222239 ensure ! (
223240 * required_votes as usize <= accounts. len( ) ,
@@ -232,6 +249,25 @@ pub(crate) fn grant_permission_impl<T: Config>(
232249 _ => { }
233250 }
234251
252+ match & enforcement {
253+ EnforcementAuthority :: None => { }
254+ EnforcementAuthority :: ControlledBy {
255+ controllers,
256+ required_votes,
257+ } => {
258+ ensure ! ( * required_votes > 0 , Error :: <T >:: InvalidNumberOfControllers ) ;
259+ ensure ! (
260+ !controllers. is_empty( ) ,
261+ Error :: <T >:: InvalidNumberOfControllers
262+ ) ;
263+
264+ ensure ! (
265+ * required_votes as usize <= controllers. len( ) ,
266+ Error :: <T >:: InvalidNumberOfControllers
267+ ) ;
268+ }
269+ }
270+
235271 // TODO: develop the idea
236272 if let Some ( parent) = parent_id {
237273 let parent_contract =
@@ -254,6 +290,7 @@ pub(crate) fn grant_permission_impl<T: Config>(
254290 allocation : allocation. clone ( ) ,
255291 distribution,
256292 targets,
293+ accumulating : true , // Start with accumulation enabled by default
257294 } ;
258295
259296 let scope = PermissionScope :: Emission ( emission_scope) ;
@@ -266,6 +303,7 @@ pub(crate) fn grant_permission_impl<T: Config>(
266303 scope,
267304 duration,
268305 revocation,
306+ enforcement,
269307 last_execution : None ,
270308 execution_count : 0 ,
271309 parent : parent_id,
@@ -329,6 +367,11 @@ pub(crate) fn execute_permission_impl<T: Config>(
329367 match & contract. scope {
330368 PermissionScope :: Emission ( emission_scope) => match emission_scope. distribution {
331369 DistributionControl :: Manual => {
370+ ensure ! (
371+ emission_scope. accumulating,
372+ Error :: <T >:: UnsupportedPermissionType
373+ ) ;
374+
332375 let accumulated = match & emission_scope. allocation {
333376 EmissionAllocation :: Streams ( streams) => streams
334377 . keys ( )
@@ -359,3 +402,174 @@ pub(crate) fn execute_permission_impl<T: Config>(
359402
360403 Ok ( ( ) )
361404}
405+
406+ /// Toggle a permission's accumulation state
407+ pub fn toggle_permission_accumulation_impl < T : Config > (
408+ origin : OriginFor < T > ,
409+ permission_id : PermissionId ,
410+ accumulating : bool ,
411+ ) -> DispatchResult {
412+ let who = ensure_signed_or_root ( origin) ?;
413+
414+ let mut contract =
415+ Permissions :: < T > :: get ( permission_id) . ok_or ( Error :: < T > :: PermissionNotFound ) ?;
416+
417+ if let Some ( who) = & who {
418+ match & contract. enforcement {
419+ _ if who == & contract. grantor => { }
420+ EnforcementAuthority :: None => {
421+ return Err ( Error :: < T > :: NotAuthorizedToToggle . into ( ) ) ;
422+ }
423+ EnforcementAuthority :: ControlledBy {
424+ controllers,
425+ required_votes,
426+ } => {
427+ ensure ! ( controllers. contains( who) , Error :: <T >:: NotAuthorizedToToggle ) ;
428+
429+ let referendum = EnforcementReferendum :: EmissionAccumulation ( accumulating) ;
430+ let votes = EnforcementTracking :: < T > :: get ( permission_id, & referendum)
431+ . into_iter ( )
432+ . filter ( |id| id != who)
433+ . filter ( |id| controllers. contains ( id) )
434+ . count ( ) ;
435+
436+ if votes + 1 < * required_votes as usize {
437+ return EnforcementTracking :: < T > :: mutate (
438+ permission_id,
439+ referendum. clone ( ) ,
440+ |votes| {
441+ votes
442+ . try_insert ( who. clone ( ) )
443+ . map_err ( |_| Error :: < T > :: TooManyControllers ) ?;
444+
445+ <Pallet < T > >:: deposit_event ( Event :: EnforcementVoteCast {
446+ permission_id,
447+ voter : who. clone ( ) ,
448+ referendum,
449+ } ) ;
450+
451+ Ok ( ( ) )
452+ } ,
453+ ) ;
454+ }
455+ }
456+ }
457+ }
458+
459+ match & mut contract. scope {
460+ PermissionScope :: Emission ( emission_scope) => emission_scope. accumulating = accumulating,
461+ }
462+
463+ Permissions :: < T > :: insert ( permission_id, contract) ;
464+
465+ // Clear any votes for this referendum
466+ EnforcementTracking :: < T > :: remove (
467+ permission_id,
468+ EnforcementReferendum :: EmissionAccumulation ( accumulating) ,
469+ ) ;
470+
471+ <Pallet < T > >:: deposit_event ( Event :: PermissionAccumulationToggled {
472+ permission_id,
473+ accumulating,
474+ toggled_by : who,
475+ } ) ;
476+
477+ Ok ( ( ) )
478+ }
479+
480+ /// Execute a permission through enforcement authority
481+ pub fn enforcement_execute_permission_impl < T : Config > (
482+ origin : OriginFor < T > ,
483+ permission_id : PermissionId ,
484+ ) -> DispatchResult {
485+ let who = ensure_signed_or_root ( origin) ?;
486+
487+ let contract = Permissions :: < T > :: get ( permission_id) . ok_or ( Error :: < T > :: PermissionNotFound ) ?;
488+
489+ // If not root, check enforcement authority
490+ if let Some ( who) = & who {
491+ match & contract. enforcement {
492+ EnforcementAuthority :: None => {
493+ return Err ( Error :: < T > :: NotAuthorizedToToggle . into ( ) ) ;
494+ }
495+ EnforcementAuthority :: ControlledBy {
496+ controllers,
497+ required_votes,
498+ } => {
499+ ensure ! ( controllers. contains( who) , Error :: <T >:: NotAuthorizedToToggle ) ;
500+
501+ let referendum = EnforcementReferendum :: Execution ;
502+ let votes = EnforcementTracking :: < T > :: get ( permission_id, & referendum)
503+ . into_iter ( )
504+ . filter ( |id| id != who)
505+ . filter ( |id| controllers. contains ( id) )
506+ . count ( ) ;
507+
508+ if votes + 1 < * required_votes as usize {
509+ return EnforcementTracking :: < T > :: mutate (
510+ permission_id,
511+ referendum. clone ( ) ,
512+ |votes| {
513+ votes
514+ . try_insert ( who. clone ( ) )
515+ . map_err ( |_| Error :: < T > :: TooManyControllers ) ?;
516+
517+ <Pallet < T > >:: deposit_event ( Event :: EnforcementVoteCast {
518+ permission_id,
519+ voter : who. clone ( ) ,
520+ referendum,
521+ } ) ;
522+
523+ Ok ( ( ) )
524+ } ,
525+ ) ;
526+ }
527+ }
528+ }
529+ }
530+
531+ match & contract. scope {
532+ PermissionScope :: Emission ( emission_scope) => {
533+ ensure ! (
534+ emission_scope. accumulating,
535+ Error :: <T >:: UnsupportedPermissionType
536+ ) ;
537+
538+ match emission_scope. distribution {
539+ DistributionControl :: Manual => {
540+ let accumulated = match & emission_scope. allocation {
541+ EmissionAllocation :: Streams ( streams) => streams
542+ . keys ( )
543+ . filter_map ( |id| {
544+ AccumulatedStreamAmounts :: < T > :: get ( (
545+ & contract. grantor ,
546+ id,
547+ permission_id,
548+ ) )
549+ } )
550+ . fold ( BalanceOf :: < T > :: zero ( ) , |acc, e| acc + e) ,
551+ EmissionAllocation :: FixedAmount ( amount) => * amount,
552+ } ;
553+
554+ ensure ! ( !accumulated. is_zero( ) , Error :: <T >:: NoAccumulatedAmount ) ;
555+
556+ crate :: permission:: emission:: do_distribute_emission :: < T > (
557+ permission_id,
558+ & contract,
559+ DistributionReason :: Manual ,
560+ ) ;
561+ }
562+ _ => return Err ( Error :: < T > :: InvalidDistributionMethod . into ( ) ) ,
563+ }
564+ }
565+ }
566+
567+ EnforcementTracking :: < T > :: remove ( permission_id, EnforcementReferendum :: Execution ) ;
568+
569+ <Pallet < T > >:: deposit_event ( Event :: PermissionEnforcementExecuted {
570+ permission_id,
571+ executed_by : who,
572+ } ) ;
573+
574+ Ok ( ( ) )
575+ }
0 commit comments